From 5ff0cfa7365613146156978947076c7c79b71b4f Mon Sep 17 00:00:00 2001 From: Git User Date: Fri, 18 Aug 2017 12:27:00 -0700 Subject: [PATCH 001/276] Initial empty repository From 8e2277f79f0fa54d5dc505881025ea27ecd6be6b Mon Sep 17 00:00:00 2001 From: Asish Bhattacharya Date: Thu, 20 Jul 2017 18:31:55 +0530 Subject: [PATCH 002/276] audio-lnx: Initial change for techpack of audio drivers. Add snapshot for audio drivers for SDM targets. The code is migrated from msm-4.9 kernel at the below cutoff - (74ff856e8d6: "net: ipc_router: Add dynamic enable/disable wakeup source feature") This changes are done for new techpack addition for audio kernel. Migrate all audio kernel drivers to this techpack. Change-Id: I33d580af3ba86a5cb777583efc5d4cdaf2882d93 Signed-off-by: Asish Bhattacharya --- Makefile | 23 + NOTICE | 24 + drivers/Makefile | 7 + drivers/base/Makefile | 2 + drivers/base/regmap/Makefile | 2 + drivers/base/regmap/internal.h | 1 + drivers/base/regmap/regmap-swr.c | 216 + drivers/mfd/Makefile | 8 + drivers/mfd/msm-cdc-pinctrl.c | 253 + drivers/mfd/msm-cdc-supply.c | 456 + drivers/mfd/wcd9335-regmap.c | 1611 ++ drivers/mfd/wcd9335-tables.c | 1325 ++ drivers/mfd/wcd934x-regmap.c | 1957 ++ drivers/mfd/wcd934x-tables.c | 2155 +++ drivers/mfd/wcd9xxx-core-init.c | 55 + drivers/mfd/wcd9xxx-core.c | 1714 ++ drivers/mfd/wcd9xxx-irq.c | 853 + drivers/mfd/wcd9xxx-regmap.h | 68 + drivers/mfd/wcd9xxx-rst.c | 443 + drivers/mfd/wcd9xxx-slimslave.c | 584 + drivers/mfd/wcd9xxx-utils.c | 1198 ++ drivers/misc/Makefile | 5 + drivers/misc/qcom/Kconfig | 19 + drivers/misc/qcom/Makefile | 1 + drivers/misc/qcom/qdsp6v2/Makefile | 6 + drivers/misc/qcom/qdsp6v2/aac_in.c | 709 + drivers/misc/qcom/qdsp6v2/amrnb_in.c | 402 + 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 | 778 + 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 | 2142 +++ 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 | 106 + 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 | 2465 +++ .../misc/qcom/qdsp6v2/ultrasound/usfcdev.c | 422 + .../misc/qcom/qdsp6v2/ultrasound/usfcdev.h | 28 + drivers/pinctrl/Makefile | 2 + drivers/pinctrl/core.h | 1 + drivers/pinctrl/pinctrl-utils.h | 1 + drivers/pinctrl/qcom/Makefile | 3 + drivers/pinctrl/qcom/pinctrl-lpi.c | 647 + drivers/pinctrl/qcom/pinctrl-wcd.c | 435 + drivers/soc/Makefile | 5 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/qdsp6v2/Makefile | 8 + drivers/soc/qcom/qdsp6v2/adsp-loader.c | 316 + drivers/soc/qcom/qdsp6v2/apr.c | 976 + drivers/soc/qcom/qdsp6v2/apr_tal_glink.c | 451 + drivers/soc/qcom/qdsp6v2/apr_v2.c | 67 + drivers/soc/qcom/qdsp6v2/apr_v3.c | 65 + drivers/soc/qcom/qdsp6v2/audio_notifier.c | 636 + drivers/soc/qcom/qdsp6v2/audio_pdr.c | 147 + drivers/soc/qcom/qdsp6v2/audio_ssr.c | 66 + drivers/soc/qcom/qdsp6v2/cdsp-loader.c | 280 + drivers/soc/qcom/qdsp6v2/msm_audio_ion.c | 853 + drivers/soundwire/Kconfig | 17 + drivers/soundwire/Makefile | 5 + drivers/soundwire/soundwire.c | 1032 + drivers/soundwire/swr-wcd-ctrl.c | 1880 ++ drivers/soundwire/swr-wcd-ctrl.h | 106 + drivers/soundwire/swrm_registers.h | 204 + include/Kbuild | 2 + include/linux/avtimer_kernel.h | 24 + include/linux/mfd/msm-cdc-pinctrl.h | 49 + include/linux/mfd/msm-cdc-supply.h | 48 + include/linux/mfd/wcd9335/irq.h | 55 + include/linux/mfd/wcd9335/registers.h | 1348 ++ include/linux/mfd/wcd934x/irq.h | 56 + include/linux/mfd/wcd934x/registers.h | 1848 ++ include/linux/mfd/wcd9xxx/core.h | 440 + include/linux/mfd/wcd9xxx/pdata.h | 197 + include/linux/mfd/wcd9xxx/wcd9xxx-irq.h | 37 + include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h | 119 + include/linux/mfd/wcd9xxx/wcd9xxx-utils.h | 40 + include/linux/qdsp6v2/apr.h | 190 + include/linux/qdsp6v2/apr_tal.h | 90 + 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 | 99 + include/linux/qdsp6v2/usf.h | 298 + include/linux/soundwire/soundwire.h | 312 + include/linux/soundwire/swr-wcd.h | 35 + include/sound/apr_audio-v2.h | 10683 +++++++++++ include/sound/apr_audio.h | 1931 ++ include/sound/audio_cal_utils.h | 102 + include/sound/audio_calibration.h | 41 + 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-slim-dma.h | 44 + include/sound/q6adm-v2.h | 187 + include/sound/q6afe-v2.h | 371 + include/sound/q6asm-v2.h | 686 + include/sound/q6audio-v2.h | 36 + include/sound/q6core.h | 159 + include/sound/q6lsm.h | 338 + include/uapi/Kbuild | 6 + include/uapi/linux/Kbuild | 19 + include/uapi/linux/avtimer.h | 10 + include/uapi/linux/msm_audio.h | 475 + 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 | 719 + 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 | 8 + include/uapi/sound/audio_effects.h | 361 + include/uapi/sound/audio_slimslave.h | 18 + include/uapi/sound/devdep_params.h | 56 + include/uapi/sound/lsm_params.h | 200 + include/uapi/sound/msmcal-hwdep.h | 23 + include/uapi/sound/voice_params.h | 14 + include/uapi/sound/wcd-dsp-glink.h | 60 + sound/Makefile | 4 + sound/soc/codecs/Makefile | 26 + sound/soc/codecs/audio-ext-clk-up.c | 626 + sound/soc/codecs/audio-ext-clk-up.h | 20 + sound/soc/codecs/audio-ext-clk.c | 348 + sound/soc/codecs/msm_stub.c | 88 + sound/soc/codecs/sdm660_cdc/Kconfig | 5 + sound/soc/codecs/sdm660_cdc/Makefile | 2 + sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c | 4683 +++++ sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h | 240 + sound/soc/codecs/sdm660_cdc/msm-cdc-common.h | 67 + sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c | 2189 +++ sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h | 91 + sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.c | 413 + sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.h | 34 + .../codecs/sdm660_cdc/sdm660-cdc-registers.h | 603 + sound/soc/codecs/sdm660_cdc/sdm660-regmap.c | 611 + sound/soc/codecs/wcd-dsp-mgr.c | 1253 ++ sound/soc/codecs/wcd-dsp-utils.c | 221 + sound/soc/codecs/wcd-dsp-utils.h | 51 + sound/soc/codecs/wcd-mbhc-adc.c | 1020 + sound/soc/codecs/wcd-mbhc-adc.h | 35 + sound/soc/codecs/wcd-mbhc-legacy.c | 975 + sound/soc/codecs/wcd-mbhc-legacy.h | 26 + sound/soc/codecs/wcd-mbhc-v2-api.h | 60 + sound/soc/codecs/wcd-mbhc-v2.c | 2074 ++ sound/soc/codecs/wcd-mbhc-v2.h | 592 + sound/soc/codecs/wcd-spi-registers.h | 43 + sound/soc/codecs/wcd-spi.c | 1535 ++ sound/soc/codecs/wcd9335.c | 14178 ++++++++++++++ sound/soc/codecs/wcd9335.h | 166 + sound/soc/codecs/wcd934x/Makefile | 6 + sound/soc/codecs/wcd934x/wcd934x-dsd.c | 772 + sound/soc/codecs/wcd934x/wcd934x-dsd.h | 97 + sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c | 1369 ++ sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h | 119 + sound/soc/codecs/wcd934x/wcd934x-mbhc.c | 1098 ++ sound/soc/codecs/wcd934x/wcd934x-mbhc.h | 84 + sound/soc/codecs/wcd934x/wcd934x-routing.h | 1171 ++ sound/soc/codecs/wcd934x/wcd934x.c | 9938 ++++++++++ sound/soc/codecs/wcd934x/wcd934x.h | 158 + sound/soc/codecs/wcd9xxx-common-v2.c | 1367 ++ sound/soc/codecs/wcd9xxx-common-v2.h | 236 + sound/soc/codecs/wcd9xxx-resmgr-v2.c | 693 + sound/soc/codecs/wcd9xxx-resmgr-v2.h | 93 + sound/soc/codecs/wcd9xxx-soc-init.c | 45 + sound/soc/codecs/wcd_cmi_api.h | 43 + sound/soc/codecs/wcd_cpe_core.c | 4579 +++++ sound/soc/codecs/wcd_cpe_core.h | 226 + sound/soc/codecs/wcd_cpe_services.c | 2990 +++ sound/soc/codecs/wcd_cpe_services.h | 179 + sound/soc/codecs/wcdcal-hwdep.c | 226 + sound/soc/codecs/wcdcal-hwdep.h | 40 + sound/soc/codecs/wsa881x-analog.c | 1446 ++ sound/soc/codecs/wsa881x-analog.h | 50 + sound/soc/codecs/wsa881x-irq.c | 610 + sound/soc/codecs/wsa881x-irq.h | 82 + sound/soc/codecs/wsa881x-registers-analog.h | 206 + sound/soc/codecs/wsa881x-registers.h | 178 + sound/soc/codecs/wsa881x-regmap-analog.c | 499 + sound/soc/codecs/wsa881x-regmap.c | 266 + sound/soc/codecs/wsa881x-tables-analog.c | 171 + sound/soc/codecs/wsa881x-tables.c | 171 + sound/soc/codecs/wsa881x-temp-sensor.c | 149 + sound/soc/codecs/wsa881x-temp-sensor.h | 39 + sound/soc/codecs/wsa881x.c | 1439 ++ sound/soc/codecs/wsa881x.h | 34 + sound/soc/msm/Kconfig | 283 + sound/soc/msm/Makefile | 39 + 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 | 3342 ++++ sound/soc/msm/msm-dai-fe.c | 2690 +++ sound/soc/msm/msm-pcm-hostless.c | 82 + sound/soc/msm/msm8996.c | 4006 ++++ sound/soc/msm/msm8998.c | 7481 ++++++++ sound/soc/msm/qdsp6v2/Makefile | 19 + sound/soc/msm/qdsp6v2/adsp_err.c | 167 + sound/soc/msm/qdsp6v2/audio_cal_utils.c | 1030 + 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 | 1378 ++ sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c | 4541 +++++ sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c | 551 + sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c | 8253 ++++++++ sound/soc/msm/qdsp6v2/msm-dai-slim.c | 664 + sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c | 394 + sound/soc/msm/qdsp6v2/msm-dolby-common.h | 267 + sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h | 86 + sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c | 2317 +++ sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h | 124 + sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c | 357 + sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h | 41 + sound/soc/msm/qdsp6v2/msm-lsm-client.c | 2414 +++ sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c | 922 + 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 | 801 + sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c | 1136 ++ sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c | 1884 ++ sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h | 130 + .../soc/msm/qdsp6v2/msm-pcm-routing-devdep.c | 139 + .../soc/msm/qdsp6v2/msm-pcm-routing-devdep.h | 35 + sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c | 15765 ++++++++++++++++ sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h | 486 + sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c | 781 + sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h | 42 + sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c | 1715 ++ sound/soc/msm/qdsp6v2/msm-qti-pp-config.c | 1407 ++ sound/soc/msm/qdsp6v2/msm-qti-pp-config.h | 54 + .../qdsp6v2/msm-transcode-loopback-q6-v2.c | 971 + sound/soc/msm/qdsp6v2/q6adm.c | 4860 +++++ sound/soc/msm/qdsp6v2/q6afe.c | 7150 +++++++ sound/soc/msm/qdsp6v2/q6asm.c | 9396 +++++++++ sound/soc/msm/qdsp6v2/q6audio-v2.c | 807 + sound/soc/msm/qdsp6v2/q6core.c | 853 + sound/soc/msm/qdsp6v2/q6lsm.c | 2173 +++ sound/soc/msm/qdsp6v2/q6voice.c | 8661 +++++++++ sound/soc/msm/qdsp6v2/q6voice.h | 1900 ++ sound/soc/msm/qdsp6v2/rtac.c | 1924 ++ sound/soc/msm/sdm660-common.c | 3210 ++++ sound/soc/msm/sdm660-common.h | 125 + sound/soc/msm/sdm660-ext-dai-links.c | 2038 ++ sound/soc/msm/sdm660-external.c | 1869 ++ sound/soc/msm/sdm660-external.h | 54 + sound/soc/msm/sdm660-internal.c | 3149 +++ sound/soc/msm/sdm660-internal.h | 32 + sound/soc/msm/sdm845.c | 6951 +++++++ 283 files changed, 250571 insertions(+) create mode 100644 Makefile create mode 100644 NOTICE create mode 100644 drivers/Makefile create mode 100644 drivers/base/Makefile create mode 100644 drivers/base/regmap/Makefile create mode 120000 drivers/base/regmap/internal.h create mode 100644 drivers/base/regmap/regmap-swr.c create mode 100644 drivers/mfd/Makefile create mode 100644 drivers/mfd/msm-cdc-pinctrl.c create mode 100644 drivers/mfd/msm-cdc-supply.c create mode 100644 drivers/mfd/wcd9335-regmap.c create mode 100644 drivers/mfd/wcd9335-tables.c create mode 100644 drivers/mfd/wcd934x-regmap.c create mode 100644 drivers/mfd/wcd934x-tables.c create mode 100644 drivers/mfd/wcd9xxx-core-init.c create mode 100644 drivers/mfd/wcd9xxx-core.c create mode 100644 drivers/mfd/wcd9xxx-irq.c create mode 100644 drivers/mfd/wcd9xxx-regmap.h create mode 100644 drivers/mfd/wcd9xxx-rst.c create mode 100644 drivers/mfd/wcd9xxx-slimslave.c create mode 100644 drivers/mfd/wcd9xxx-utils.c create mode 100644 drivers/misc/Makefile 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/pinctrl/Makefile create mode 120000 drivers/pinctrl/core.h create mode 120000 drivers/pinctrl/pinctrl-utils.h create mode 100644 drivers/pinctrl/qcom/Makefile create mode 100644 drivers/pinctrl/qcom/pinctrl-lpi.c create mode 100644 drivers/pinctrl/qcom/pinctrl-wcd.c create mode 100644 drivers/soc/Makefile create mode 100644 drivers/soc/qcom/Makefile 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_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/cdsp-loader.c create mode 100644 drivers/soc/qcom/qdsp6v2/msm_audio_ion.c create mode 100644 drivers/soundwire/Kconfig create mode 100644 drivers/soundwire/Makefile create mode 100644 drivers/soundwire/soundwire.c create mode 100644 drivers/soundwire/swr-wcd-ctrl.c create mode 100644 drivers/soundwire/swr-wcd-ctrl.h create mode 100644 drivers/soundwire/swrm_registers.h create mode 100644 include/Kbuild create mode 100644 include/linux/avtimer_kernel.h create mode 100644 include/linux/mfd/msm-cdc-pinctrl.h create mode 100644 include/linux/mfd/msm-cdc-supply.h create mode 100644 include/linux/mfd/wcd9335/irq.h create mode 100644 include/linux/mfd/wcd9335/registers.h create mode 100644 include/linux/mfd/wcd934x/irq.h create mode 100644 include/linux/mfd/wcd934x/registers.h create mode 100644 include/linux/mfd/wcd9xxx/core.h create mode 100644 include/linux/mfd/wcd9xxx/pdata.h create mode 100644 include/linux/mfd/wcd9xxx/wcd9xxx-irq.h create mode 100644 include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h create mode 100644 include/linux/mfd/wcd9xxx/wcd9xxx-utils.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/linux/soundwire/soundwire.h create mode 100644 include/linux/soundwire/swr-wcd.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/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-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/uapi/Kbuild create mode 100644 include/uapi/linux/Kbuild 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/Kbuild create mode 100644 include/uapi/sound/audio_effects.h create mode 100644 include/uapi/sound/audio_slimslave.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 include/uapi/sound/voice_params.h create mode 100644 include/uapi/sound/wcd-dsp-glink.h create mode 100644 sound/Makefile create mode 100644 sound/soc/codecs/Makefile create mode 100644 sound/soc/codecs/audio-ext-clk-up.c create mode 100644 sound/soc/codecs/audio-ext-clk-up.h create mode 100644 sound/soc/codecs/audio-ext-clk.c create mode 100644 sound/soc/codecs/msm_stub.c create mode 100644 sound/soc/codecs/sdm660_cdc/Kconfig create mode 100644 sound/soc/codecs/sdm660_cdc/Makefile create mode 100644 sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c create mode 100644 sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h create mode 100644 sound/soc/codecs/sdm660_cdc/msm-cdc-common.h create mode 100644 sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c create mode 100644 sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h create mode 100644 sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.c create mode 100644 sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.h create mode 100644 sound/soc/codecs/sdm660_cdc/sdm660-cdc-registers.h create mode 100644 sound/soc/codecs/sdm660_cdc/sdm660-regmap.c create mode 100644 sound/soc/codecs/wcd-dsp-mgr.c create mode 100644 sound/soc/codecs/wcd-dsp-utils.c create mode 100644 sound/soc/codecs/wcd-dsp-utils.h create mode 100644 sound/soc/codecs/wcd-mbhc-adc.c create mode 100644 sound/soc/codecs/wcd-mbhc-adc.h create mode 100644 sound/soc/codecs/wcd-mbhc-legacy.c create mode 100644 sound/soc/codecs/wcd-mbhc-legacy.h create mode 100644 sound/soc/codecs/wcd-mbhc-v2-api.h create mode 100644 sound/soc/codecs/wcd-mbhc-v2.c create mode 100644 sound/soc/codecs/wcd-mbhc-v2.h create mode 100644 sound/soc/codecs/wcd-spi-registers.h create mode 100644 sound/soc/codecs/wcd-spi.c create mode 100644 sound/soc/codecs/wcd9335.c create mode 100644 sound/soc/codecs/wcd9335.h create mode 100644 sound/soc/codecs/wcd934x/Makefile create mode 100644 sound/soc/codecs/wcd934x/wcd934x-dsd.c create mode 100644 sound/soc/codecs/wcd934x/wcd934x-dsd.h create mode 100644 sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c create mode 100644 sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h create mode 100644 sound/soc/codecs/wcd934x/wcd934x-mbhc.c create mode 100644 sound/soc/codecs/wcd934x/wcd934x-mbhc.h create mode 100644 sound/soc/codecs/wcd934x/wcd934x-routing.h create mode 100644 sound/soc/codecs/wcd934x/wcd934x.c create mode 100644 sound/soc/codecs/wcd934x/wcd934x.h create mode 100644 sound/soc/codecs/wcd9xxx-common-v2.c create mode 100644 sound/soc/codecs/wcd9xxx-common-v2.h create mode 100644 sound/soc/codecs/wcd9xxx-resmgr-v2.c create mode 100644 sound/soc/codecs/wcd9xxx-resmgr-v2.h create mode 100644 sound/soc/codecs/wcd9xxx-soc-init.c create mode 100644 sound/soc/codecs/wcd_cmi_api.h create mode 100644 sound/soc/codecs/wcd_cpe_core.c create mode 100644 sound/soc/codecs/wcd_cpe_core.h create mode 100644 sound/soc/codecs/wcd_cpe_services.c create mode 100644 sound/soc/codecs/wcd_cpe_services.h create mode 100644 sound/soc/codecs/wcdcal-hwdep.c create mode 100644 sound/soc/codecs/wcdcal-hwdep.h create mode 100644 sound/soc/codecs/wsa881x-analog.c create mode 100644 sound/soc/codecs/wsa881x-analog.h create mode 100644 sound/soc/codecs/wsa881x-irq.c create mode 100644 sound/soc/codecs/wsa881x-irq.h create mode 100644 sound/soc/codecs/wsa881x-registers-analog.h create mode 100644 sound/soc/codecs/wsa881x-registers.h create mode 100644 sound/soc/codecs/wsa881x-regmap-analog.c create mode 100644 sound/soc/codecs/wsa881x-regmap.c create mode 100644 sound/soc/codecs/wsa881x-tables-analog.c create mode 100644 sound/soc/codecs/wsa881x-tables.c create mode 100644 sound/soc/codecs/wsa881x-temp-sensor.c create mode 100644 sound/soc/codecs/wsa881x-temp-sensor.h create mode 100644 sound/soc/codecs/wsa881x.c create mode 100644 sound/soc/codecs/wsa881x.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/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-compress-q6-v2.c 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.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-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/msm-transcode-loopback-q6-v2.c 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 create mode 100644 sound/soc/msm/sdm660-common.c create mode 100644 sound/soc/msm/sdm660-common.h create mode 100644 sound/soc/msm/sdm660-ext-dai-links.c create mode 100644 sound/soc/msm/sdm660-external.c create mode 100644 sound/soc/msm/sdm660-external.h create mode 100644 sound/soc/msm/sdm660-internal.c create mode 100644 sound/soc/msm/sdm660-internal.h create mode 100644 sound/soc/msm/sdm845.c diff --git a/Makefile b/Makefile new file mode 100644 index 000000000000..b8428deb5c05 --- /dev/null +++ b/Makefile @@ -0,0 +1,23 @@ +# auto-detect subdirs +ifeq ($(CONFIG_ARCH_SDM845), y) +include $(srctree)/techpack/audio/config/sdm845auto.conf +export +endif + +# Use USERINCLUDE when you must reference the UAPI directories only. +USERINCLUDE += \ + -I$(srctree)/techpack/audio/include/uapi \ + +# Use LINUXINCLUDE when you must reference the include/ directory. +# Needed to be compatible with the O= option +LINUXINCLUDE += \ + -I$(srctree)/techpack/audio/include/uapi \ + -I$(srctree)/techpack/audio/include + +ifeq ($(CONFIG_ARCH_SDM845), y) +LINUXINCLUDE += \ + -include $(srctree)/techpack/audio/config/sdm845autoconf.h +endif + +obj-y += drivers/ +obj-y += sound/ diff --git a/NOTICE b/NOTICE new file mode 100644 index 000000000000..9ad222017a76 --- /dev/null +++ b/NOTICE @@ -0,0 +1,24 @@ +Copyright (c) 2009-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. +________________________________________ + +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. diff --git a/drivers/Makefile b/drivers/Makefile new file mode 100644 index 000000000000..b48069c04d47 --- /dev/null +++ b/drivers/Makefile @@ -0,0 +1,7 @@ + +obj-y += mfd/ +obj-y += misc/ +obj-y += soc/ +obj-y += soundwire/ +obj-y += base/ +obj-y += pinctrl/ diff --git a/drivers/base/Makefile b/drivers/base/Makefile new file mode 100644 index 000000000000..76e50f9816bf --- /dev/null +++ b/drivers/base/Makefile @@ -0,0 +1,2 @@ + +obj-y += regmap/ diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile new file mode 100644 index 000000000000..a23e35ed8459 --- /dev/null +++ b/drivers/base/regmap/Makefile @@ -0,0 +1,2 @@ + +obj-$(CONFIG_REGMAP_SWR) += regmap-swr.o diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h new file mode 120000 index 000000000000..e163b7862be6 --- /dev/null +++ b/drivers/base/regmap/internal.h @@ -0,0 +1 @@ +../../../../../drivers/base/regmap/internal.h \ No newline at end of file diff --git a/drivers/base/regmap/regmap-swr.c b/drivers/base/regmap/regmap-swr.c new file mode 100644 index 000000000000..be1eb00ad876 --- /dev/null +++ b/drivers/base/regmap/regmap-swr.c @@ -0,0 +1,216 @@ +/* + * 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 "internal.h" + +static int regmap_swr_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_len) +{ + struct device *dev = context; + struct swr_device *swr = to_swr_device(dev); + struct regmap *map = dev_get_regmap(dev, NULL); + size_t addr_bytes; + size_t val_bytes; + int i, ret = 0; + u16 reg_addr = 0; + u8 *value; + + if (map == NULL) { + dev_err(dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + addr_bytes = map->format.reg_bytes; + if (swr == NULL) { + dev_err(dev, "%s: swr device is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != addr_bytes) { + dev_err(dev, "%s: reg size %zd bytes not supported\n", + __func__, reg_size); + return -EINVAL; + } + reg_addr = *(u16 *)reg; + val_bytes = map->format.val_bytes; + /* val_len = val_bytes * val_count */ + for (i = 0; i < (val_len / val_bytes); i++) { + value = (u8 *)val + (val_bytes * i); + ret = swr_write(swr, swr->dev_num, (reg_addr + i), value); + if (ret < 0) { + dev_err(dev, "%s: write reg 0x%x failed, err %d\n", + __func__, (reg_addr + i), ret); + break; + } + } + return ret; +} + +static int regmap_swr_raw_multi_reg_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct swr_device *swr = to_swr_device(dev); + struct regmap *map = dev_get_regmap(dev, NULL); + size_t addr_bytes; + size_t val_bytes; + size_t pad_bytes; + size_t num_regs; + int i = 0; + int ret = 0; + u16 *reg; + u8 *val; + u8 *buf; + + if (swr == NULL) { + dev_err(dev, "%s: swr device is NULL\n", __func__); + return -EINVAL; + } + + if (map == NULL) { + dev_err(dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + + addr_bytes = map->format.reg_bytes; + val_bytes = map->format.val_bytes; + pad_bytes = map->format.pad_bytes; + + if (addr_bytes + val_bytes + pad_bytes == 0) { + dev_err(dev, "%s: sum of addr, value and pad is 0\n", __func__); + return -EINVAL; + } + num_regs = count / (addr_bytes + val_bytes + pad_bytes); + + reg = kcalloc(num_regs, sizeof(u16), GFP_KERNEL); + if (!reg) + return -ENOMEM; + + val = kcalloc(num_regs, sizeof(u8), GFP_KERNEL); + if (!val) { + ret = -ENOMEM; + goto mem_fail; + } + + buf = (u8 *)data; + for (i = 0; i < num_regs; i++) { + reg[i] = *(u16 *)buf; + buf += (map->format.reg_bytes + map->format.pad_bytes); + val[i] = *buf; + buf += map->format.val_bytes; + } + ret = swr_bulk_write(swr, swr->dev_num, reg, val, num_regs); + if (ret) + dev_err(dev, "%s: multi reg write failed\n", __func__); + + kfree(val); +mem_fail: + kfree(reg); + return ret; +} + +static int regmap_swr_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct regmap *map = dev_get_regmap(dev, NULL); + size_t addr_bytes; + size_t val_bytes; + size_t pad_bytes; + + if (map == NULL) { + dev_err(dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + addr_bytes = map->format.reg_bytes; + val_bytes = map->format.val_bytes; + pad_bytes = map->format.pad_bytes; + + WARN_ON(count < addr_bytes); + + if (count > (addr_bytes + val_bytes + pad_bytes)) + return regmap_swr_raw_multi_reg_write(context, data, count); + else + return regmap_swr_gather_write(context, data, addr_bytes, + (data + addr_bytes), + (count - addr_bytes)); +} + +static int regmap_swr_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct swr_device *swr = to_swr_device(dev); + struct regmap *map = dev_get_regmap(dev, NULL); + size_t addr_bytes; + int ret = 0; + u16 reg_addr = 0; + + if (map == NULL) { + dev_err(dev, "%s: regmap is NULL\n", __func__); + return -EINVAL; + } + addr_bytes = map->format.reg_bytes; + if (swr == NULL) { + dev_err(dev, "%s: swr is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != addr_bytes) { + dev_err(dev, "%s: register size %zd bytes not supported\n", + __func__, reg_size); + return -EINVAL; + } + reg_addr = *(u16 *)reg; + ret = swr_read(swr, swr->dev_num, reg_addr, val, val_size); + if (ret < 0) + dev_err(dev, "%s: codec reg 0x%x read failed %d\n", + __func__, reg_addr, ret); + return ret; +} + +static struct regmap_bus regmap_swr = { + .write = regmap_swr_write, + .gather_write = regmap_swr_gather_write, + .read = regmap_swr_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +struct regmap *__regmap_init_swr(struct swr_device *swr, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) +{ + return __regmap_init(&swr->dev, ®map_swr, &swr->dev, config, + lock_key, lock_name); +} +EXPORT_SYMBOL(__regmap_init_swr); + +struct regmap *__devm_regmap_init_swr(struct swr_device *swr, + const struct regmap_config *config, + struct lock_class_key *lock_key, + const char *lock_name) +{ + return __devm_regmap_init(&swr->dev, ®map_swr, &swr->dev, config, + lock_key, lock_name); +} +EXPORT_SYMBOL(__devm_regmap_init_swr); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile new file mode 100644 index 000000000000..745691af1673 --- /dev/null +++ b/drivers/mfd/Makefile @@ -0,0 +1,8 @@ +wcd-core-objs := wcd9xxx-rst.o wcd9xxx-core-init.o \ + wcd9xxx-core.o wcd9xxx-irq.o \ + wcd9xxx-slimslave.o wcd9xxx-utils.o \ + wcd934x-regmap.o wcd934x-tables.o \ + wcd9335-regmap.o wcd9335-tables.o \ + msm-cdc-pinctrl.o msm-cdc-supply.o +obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd-core.o + diff --git a/drivers/mfd/msm-cdc-pinctrl.c b/drivers/mfd/msm-cdc-pinctrl.c new file mode 100644 index 000000000000..859a75f93bb5 --- /dev/null +++ b/drivers/mfd/msm-cdc-pinctrl.c @@ -0,0 +1,253 @@ +/* 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 + +struct msm_cdc_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_active; + struct pinctrl_state *pinctrl_sleep; + int gpio; + bool state; +}; + +static struct msm_cdc_pinctrl_info *msm_cdc_pinctrl_get_gpiodata( + struct device_node *np) +{ + struct platform_device *pdev; + struct msm_cdc_pinctrl_info *gpio_data; + + if (!np) { + pr_err("%s: device node is null\n", __func__); + return NULL; + } + + pdev = of_find_device_by_node(np); + if (!pdev) { + pr_err("%s: platform device not found!\n", __func__); + return NULL; + } + + gpio_data = dev_get_drvdata(&pdev->dev); + if (!gpio_data) + dev_err(&pdev->dev, "%s: cannot find cdc gpio info\n", + __func__); + + return gpio_data; +} + +/* + * msm_cdc_get_gpio_state: select pinctrl sleep state + * @np: pointer to struct device_node + * + * Returns error code for failure and GPIO value on success + */ +int msm_cdc_get_gpio_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + int value = -EINVAL; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return value; + + if (gpio_is_valid(gpio_data->gpio)) + value = gpio_get_value_cansleep(gpio_data->gpio); + + return value; +} +EXPORT_SYMBOL(msm_cdc_get_gpio_state); + +/* + * msm_cdc_pinctrl_select_sleep_state: select pinctrl sleep state + * @np: pointer to struct device_node + * + * Returns error code for failure + */ +int msm_cdc_pinctrl_select_sleep_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return -EINVAL; + + if (!gpio_data->pinctrl_sleep) { + pr_err("%s: pinctrl sleep state is null\n", __func__); + return -EINVAL; + } + gpio_data->state = false; + + return pinctrl_select_state(gpio_data->pinctrl, + gpio_data->pinctrl_sleep); +} +EXPORT_SYMBOL(msm_cdc_pinctrl_select_sleep_state); + +/* + * msm_cdc_pinctrl_select_active_state: select pinctrl active state + * @np: pointer to struct device_node + * + * Returns error code for failure + */ +int msm_cdc_pinctrl_select_active_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return -EINVAL; + + if (!gpio_data->pinctrl_active) { + pr_err("%s: pinctrl active state is null\n", __func__); + return -EINVAL; + } + gpio_data->state = true; + + return pinctrl_select_state(gpio_data->pinctrl, + gpio_data->pinctrl_active); +} +EXPORT_SYMBOL(msm_cdc_pinctrl_select_active_state); + +/* + * msm_cdc_pinctrl_get_state: get curren pinctrl state + * @np: pointer to struct device_node + * + * Returns 0 for sleep state, 1 for active state + */ +bool msm_cdc_pinctrl_get_state(struct device_node *np) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = msm_cdc_pinctrl_get_gpiodata(np); + if (!gpio_data) + return -EINVAL; + + return gpio_data->state; +} +EXPORT_SYMBOL(msm_cdc_pinctrl_get_state); + +static int msm_cdc_pinctrl_probe(struct platform_device *pdev) +{ + int ret = 0; + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = devm_kzalloc(&pdev->dev, + sizeof(struct msm_cdc_pinctrl_info), + GFP_KERNEL); + if (!gpio_data) + return -ENOMEM; + + gpio_data->pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(gpio_data->pinctrl)) { + dev_err(&pdev->dev, "%s: Cannot get cdc gpio pinctrl:%ld\n", + __func__, PTR_ERR(gpio_data->pinctrl)); + ret = PTR_ERR(gpio_data->pinctrl); + goto err_pctrl_get; + } + + gpio_data->pinctrl_active = pinctrl_lookup_state( + gpio_data->pinctrl, "aud_active"); + if (IS_ERR_OR_NULL(gpio_data->pinctrl_active)) { + dev_err(&pdev->dev, "%s: Cannot get aud_active pinctrl state:%ld\n", + __func__, PTR_ERR(gpio_data->pinctrl_active)); + ret = PTR_ERR(gpio_data->pinctrl_active); + goto err_lookup_state; + } + + gpio_data->pinctrl_sleep = pinctrl_lookup_state( + gpio_data->pinctrl, "aud_sleep"); + if (IS_ERR_OR_NULL(gpio_data->pinctrl_sleep)) { + dev_err(&pdev->dev, "%s: Cannot get aud_sleep pinctrl state:%ld\n", + __func__, PTR_ERR(gpio_data->pinctrl_sleep)); + ret = PTR_ERR(gpio_data->pinctrl_sleep); + goto err_lookup_state; + } + /* skip setting to sleep state for LPI_TLMM GPIOs */ + if (!of_property_read_bool(pdev->dev.of_node, "qcom,lpi-gpios")) { + /* Set pinctrl state to aud_sleep by default */ + ret = pinctrl_select_state(gpio_data->pinctrl, + gpio_data->pinctrl_sleep); + if (ret) + dev_err(&pdev->dev, "%s: set cdc gpio sleep state fail: %d\n", + __func__, ret); + } + + gpio_data->gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,cdc-rst-n-gpio", 0); + if (gpio_is_valid(gpio_data->gpio)) { + ret = gpio_request(gpio_data->gpio, "MSM_CDC_RESET"); + if (ret) { + dev_err(&pdev->dev, "%s: Failed to request gpio %d\n", + __func__, gpio_data->gpio); + goto err_lookup_state; + } + } + + dev_set_drvdata(&pdev->dev, gpio_data); + return 0; + +err_lookup_state: + devm_pinctrl_put(gpio_data->pinctrl); +err_pctrl_get: + devm_kfree(&pdev->dev, gpio_data); + return ret; +} + +static int msm_cdc_pinctrl_remove(struct platform_device *pdev) +{ + struct msm_cdc_pinctrl_info *gpio_data; + + gpio_data = dev_get_drvdata(&pdev->dev); + + if (gpio_data && gpio_data->pinctrl) + devm_pinctrl_put(gpio_data->pinctrl); + + devm_kfree(&pdev->dev, gpio_data); + + return 0; +} + +static const struct of_device_id msm_cdc_pinctrl_match[] = { + {.compatible = "qcom,msm-cdc-pinctrl"}, + {} +}; + +static struct platform_driver msm_cdc_pinctrl_driver = { + .driver = { + .name = "msm-cdc-pinctrl", + .owner = THIS_MODULE, + .of_match_table = msm_cdc_pinctrl_match, + }, + .probe = msm_cdc_pinctrl_probe, + .remove = msm_cdc_pinctrl_remove, +}; + +int msm_cdc_pinctrl_drv_init(void) +{ + return platform_driver_register(&msm_cdc_pinctrl_driver); +} + +void msm_cdc_pinctrl_drv_exit(void) +{ + platform_driver_unregister(&msm_cdc_pinctrl_driver); +} +MODULE_DESCRIPTION("MSM CODEC pin control platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/msm-cdc-supply.c b/drivers/mfd/msm-cdc-supply.c new file mode 100644 index 000000000000..9c7ebf78a00e --- /dev/null +++ b/drivers/mfd/msm-cdc-supply.c @@ -0,0 +1,456 @@ +/* + * 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 + +#define CODEC_DT_MAX_PROP_SIZE 40 + +static int msm_cdc_dt_parse_vreg_info(struct device *dev, + struct cdc_regulator *cdc_vreg, + const char *name, bool is_ond) +{ + char prop_name[CODEC_DT_MAX_PROP_SIZE]; + struct device_node *regulator_node = NULL; + const __be32 *prop; + int len, rc; + u32 prop_val; + + /* Parse supply name */ + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", name); + + regulator_node = of_parse_phandle(dev->of_node, prop_name, 0); + if (!regulator_node) { + dev_err(dev, "%s: Looking up %s property in node %s failed", + __func__, prop_name, dev->of_node->full_name); + rc = -EINVAL; + goto done; + } + cdc_vreg->name = name; + cdc_vreg->ondemand = is_ond; + + /* Parse supply - voltage */ + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-voltage", name); + prop = of_get_property(dev->of_node, prop_name, &len); + if (!prop || (len != (2 * sizeof(__be32)))) { + dev_err(dev, "%s: %s %s property\n", __func__, + prop ? "invalid format" : "no", prop_name); + rc = -EINVAL; + goto done; + } else { + cdc_vreg->min_uV = be32_to_cpup(&prop[0]); + cdc_vreg->max_uV = be32_to_cpup(&prop[1]); + } + + /* Parse supply - current */ + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-current", name); + rc = of_property_read_u32(dev->of_node, prop_name, &prop_val); + if (rc) { + dev_err(dev, "%s: Looking up %s property in node %s failed", + __func__, prop_name, dev->of_node->full_name); + goto done; + } + cdc_vreg->optimum_uA = prop_val; + + dev_info(dev, "%s: %s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n", + __func__, cdc_vreg->name, cdc_vreg->min_uV, cdc_vreg->max_uV, + cdc_vreg->optimum_uA, cdc_vreg->ondemand); + +done: + return rc; +} + +static int msm_cdc_parse_supplies(struct device *dev, + struct cdc_regulator *cdc_reg, + const char *sup_list, int sup_cnt, + bool is_ond) +{ + int idx, rc = 0; + const char *name = NULL; + + for (idx = 0; idx < sup_cnt; idx++) { + rc = of_property_read_string_index(dev->of_node, sup_list, idx, + &name); + if (rc) { + dev_err(dev, "%s: read string %s[%d] error (%d)\n", + __func__, sup_list, idx, rc); + goto done; + } + + dev_dbg(dev, "%s: Found cdc supply %s as part of %s\n", + __func__, name, sup_list); + + rc = msm_cdc_dt_parse_vreg_info(dev, &cdc_reg[idx], name, + is_ond); + if (rc) { + dev_err(dev, "%s: parse %s vreg info failed (%d)\n", + __func__, name, rc); + goto done; + } + } + +done: + return rc; +} + +static int msm_cdc_check_supply_param(struct device *dev, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + if (!dev) { + pr_err("%s: device is NULL\n", __func__); + return -ENODEV; + } + + if (!cdc_vreg || (num_supplies <= 0)) { + dev_err(dev, "%s: supply check failed: vreg: %pK, num_supplies: %d\n", + __func__, cdc_vreg, num_supplies); + return -EINVAL; + } + + return 0; +} + +/* + * msm_cdc_disable_static_supplies: + * Disable codec static supplies + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply disable is failed + */ +int msm_cdc_disable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + int rc, i; + + if ((!dev) || (!supplies) || (!cdc_vreg)) { + pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + for (i = 0; i < num_supplies; i++) { + if (cdc_vreg[i].ondemand) + continue; + + rc = regulator_disable(supplies[i].consumer); + if (rc) + dev_err(dev, "%s: failed to disable supply %s, err:%d\n", + __func__, supplies[i].supply, rc); + else + dev_dbg(dev, "%s: disabled regulator %s\n", + __func__, supplies[i].supply); + } + + return rc; +} +EXPORT_SYMBOL(msm_cdc_disable_static_supplies); + +/* + * msm_cdc_release_supplies: + * Release codec power supplies + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply disable is failed + */ +int msm_cdc_release_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + int rc = 0; + int i; + + if ((!dev) || (!supplies) || (!cdc_vreg)) { + pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + msm_cdc_disable_static_supplies(dev, supplies, cdc_vreg, + num_supplies); + for (i = 0; i < num_supplies; i++) { + if (regulator_count_voltages(supplies[i].consumer) < 0) + continue; + + regulator_set_voltage(supplies[i].consumer, 0, + cdc_vreg[i].max_uV); + regulator_set_load(supplies[i].consumer, 0); + devm_regulator_put(supplies[i].consumer); + supplies[i].consumer = NULL; + } + devm_kfree(dev, supplies); + + return rc; +} +EXPORT_SYMBOL(msm_cdc_release_supplies); + +/* + * msm_cdc_enable_static_supplies: + * Enable codec static supplies + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply enable is failed + */ +int msm_cdc_enable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + int rc, i; + + if ((!dev) || (!supplies) || (!cdc_vreg)) { + pr_err("%s: either dev or supplies or cdc_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + for (i = 0; i < num_supplies; i++) { + if (cdc_vreg[i].ondemand) + continue; + + rc = regulator_enable(supplies[i].consumer); + if (rc) { + dev_err(dev, "%s: failed to enable supply %s, rc: %d\n", + __func__, supplies[i].supply, rc); + break; + } + } + + while (rc && i--) + if (!cdc_vreg[i].ondemand) + regulator_disable(supplies[i].consumer); + + return rc; +} +EXPORT_SYMBOL(msm_cdc_enable_static_supplies); + +/* + * msm_cdc_init_supplies: + * Initialize codec static supplies with regulator get + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * + * Return error code if supply init is failed + */ +int msm_cdc_init_supplies(struct device *dev, + struct regulator_bulk_data **supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies) +{ + struct regulator_bulk_data *vsup; + int rc; + int i; + + if (!dev || !cdc_vreg) { + pr_err("%s: device pointer or dce_vreg is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + vsup = devm_kcalloc(dev, num_supplies, + sizeof(struct regulator_bulk_data), + GFP_KERNEL); + if (!vsup) + return -ENOMEM; + + for (i = 0; i < num_supplies; i++) { + if (!cdc_vreg[i].name) { + dev_err(dev, "%s: supply name not defined\n", + __func__); + rc = -EINVAL; + goto err_supply; + } + vsup[i].supply = cdc_vreg[i].name; + } + + rc = devm_regulator_bulk_get(dev, num_supplies, vsup); + if (rc) { + dev_err(dev, "%s: failed to get supplies (%d)\n", + __func__, rc); + goto err_supply; + } + + /* Set voltage and current on regulators */ + for (i = 0; i < num_supplies; i++) { + if (regulator_count_voltages(vsup[i].consumer) < 0) + continue; + + rc = regulator_set_voltage(vsup[i].consumer, + cdc_vreg[i].min_uV, + cdc_vreg[i].max_uV); + if (rc) { + dev_err(dev, "%s: set regulator voltage failed for %s, err:%d\n", + __func__, vsup[i].supply, rc); + goto err_set_supply; + } + rc = regulator_set_load(vsup[i].consumer, + cdc_vreg[i].optimum_uA); + if (rc < 0) { + dev_err(dev, "%s: set regulator optimum mode failed for %s, err:%d\n", + __func__, vsup[i].supply, rc); + goto err_set_supply; + } + } + + *supplies = vsup; + + return 0; + +err_set_supply: + for (i = 0; i < num_supplies; i++) + devm_regulator_put(vsup[i].consumer); +err_supply: + devm_kfree(dev, vsup); + return rc; +} +EXPORT_SYMBOL(msm_cdc_init_supplies); + +/* + * msm_cdc_get_power_supplies: + * Get codec power supplies from device tree. + * Allocate memory to hold regulator data for + * all power supplies. + * + * @dev: pointer to codec device + * @cdc_vreg: pointer to codec regulator + * @total_num_supplies: total number of supplies read from DT + * + * Return error code if supply disable is failed + */ +int msm_cdc_get_power_supplies(struct device *dev, + struct cdc_regulator **cdc_vreg, + int *total_num_supplies) +{ + const char *static_prop_name = "qcom,cdc-static-supplies"; + const char *ond_prop_name = "qcom,cdc-on-demand-supplies"; + const char *cp_prop_name = "qcom,cdc-cp-supplies"; + int static_sup_cnt = 0; + int ond_sup_cnt = 0; + int cp_sup_cnt = 0; + int num_supplies = 0; + struct cdc_regulator *cdc_reg; + int rc; + + if (!dev) { + pr_err("%s: device pointer is NULL\n", __func__); + return -EINVAL; + } + static_sup_cnt = of_property_count_strings(dev->of_node, + static_prop_name); + if (static_sup_cnt < 0) { + dev_err(dev, "%s: Failed to get static supplies(%d)\n", + __func__, static_sup_cnt); + rc = static_sup_cnt; + goto err_supply_cnt; + } + ond_sup_cnt = of_property_count_strings(dev->of_node, ond_prop_name); + if (ond_sup_cnt < 0) + ond_sup_cnt = 0; + + cp_sup_cnt = of_property_count_strings(dev->of_node, + cp_prop_name); + if (cp_sup_cnt < 0) + cp_sup_cnt = 0; + + num_supplies = static_sup_cnt + ond_sup_cnt + cp_sup_cnt; + if (num_supplies <= 0) { + dev_err(dev, "%s: supply count is 0 or negative\n", __func__); + rc = -EINVAL; + goto err_supply_cnt; + } + + cdc_reg = devm_kcalloc(dev, num_supplies, + sizeof(struct cdc_regulator), + GFP_KERNEL); + if (!cdc_reg) { + rc = -ENOMEM; + goto err_mem_alloc; + } + + rc = msm_cdc_parse_supplies(dev, cdc_reg, static_prop_name, + static_sup_cnt, false); + if (rc) { + dev_err(dev, "%s: failed to parse static supplies(%d)\n", + __func__, rc); + goto err_sup; + } + + rc = msm_cdc_parse_supplies(dev, &cdc_reg[static_sup_cnt], + ond_prop_name, ond_sup_cnt, + true); + if (rc) { + dev_err(dev, "%s: failed to parse demand supplies(%d)\n", + __func__, rc); + goto err_sup; + } + + rc = msm_cdc_parse_supplies(dev, + &cdc_reg[static_sup_cnt + ond_sup_cnt], + cp_prop_name, cp_sup_cnt, true); + if (rc) { + dev_err(dev, "%s: failed to parse cp supplies(%d)\n", + __func__, rc); + goto err_sup; + } + + *cdc_vreg = cdc_reg; + *total_num_supplies = num_supplies; + + return 0; + +err_sup: + devm_kfree(dev, cdc_reg); +err_supply_cnt: +err_mem_alloc: + return rc; +} +EXPORT_SYMBOL(msm_cdc_get_power_supplies); diff --git a/drivers/mfd/wcd9335-regmap.c b/drivers/mfd/wcd9335-regmap.c new file mode 100644 index 000000000000..1762374418c0 --- /dev/null +++ b/drivers/mfd/wcd9335-regmap.c @@ -0,0 +1,1611 @@ +/* + * 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 "wcd9xxx-regmap.h" + +static const struct reg_sequence wcd9335_1_x_defaults[] = { + { WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, 0x1f, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x00, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG, 0x00, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG, 0x00, 0x00 }, + { WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x14, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_MASK, 0x3f, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL, 0x00, 0x00 }, + { WCD9335_BIAS_VBG_FINE_ADJ, 0x55, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_2, 0x6c, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_3, 0x2d, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_8, 0x6c, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_10, 0x6c, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_2, 0x77, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_3, 0x77, 0x00 }, + { WCD9335_SIDO_SIDO_TEST_2, 0x00, 0x00 }, + { WCD9335_MBHC_ZDET_ANA_CTL, 0x00, 0x00 }, + { WCD9335_MBHC_FSM_DEBUG, 0xc0, 0x00 }, + { WCD9335_TX_1_2_ATEST_REFCTL, 0x08, 0x00 }, + { WCD9335_TX_3_4_ATEST_REFCTL, 0x08, 0x00 }, + { WCD9335_TX_5_6_ATEST_REFCTL, 0x08, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_1, 0x67, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_4, 0x5f, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_9, 0x50, 0x00 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_1, 0x65, 0x00 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0x40, 0x00 }, + { WCD9335_RX_BIAS_HPH_PA, 0xaa, 0x00 }, + { WCD9335_RX_BIAS_HPH_LOWPOWER, 0x62, 0x00 }, + { WCD9335_HPH_PA_CTL2, 0x40, 0x00 }, + { WCD9335_HPH_L_EN, 0x00, 0x00 }, + { WCD9335_HPH_R_EN, 0x00, 0x00 }, + { WCD9335_HPH_R_ATEST, 0x50, 0x00 }, + { WCD9335_HPH_RDAC_LDO_CTL, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG0, 0x00, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG1, 0x00, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC2, 0x00, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC3, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER2_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER3_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER4_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER5_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER6_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER7_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_COMPANDER8_CTL7, 0x0c, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CFG1, 0x04, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x0e, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC0, 0x00, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC1, 0x00, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC0_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC1_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC2_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC3_CLK_RST_CTL_0, 0x00, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, 0x00, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_2, 0x00, 0x00 }, +}; + +static const struct reg_sequence wcd9335_2_0_defaults[] = { + { WCD9335_CODEC_RPM_CLK_GATE, 0x07, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, 0x3f, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x01, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x10, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG, 0x08, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG, 0x08, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG, 0x08, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG, 0x08, 0x00 }, + { WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x13, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_MASK, 0xff, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL, 0x40, 0x00 }, + { WCD9335_BIAS_VBG_FINE_ADJ, 0xc5, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_2, 0x92, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_3, 0x35, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_8, 0x6e, 0x00 }, + { WCD9335_SIDO_SIDO_CCL_10, 0x6e, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_2, 0x55, 0x00 }, + { WCD9335_SIDO_SIDO_DRIVER_3, 0x55, 0x00 }, + { WCD9335_SIDO_SIDO_TEST_2, 0x0f, 0x00 }, + { WCD9335_MBHC_ZDET_ANA_CTL, 0x0f, 0x00 }, + { WCD9335_TX_1_2_ATEST_REFCTL, 0x0a, 0x00 }, + { WCD9335_TX_3_4_ATEST_REFCTL, 0x0a, 0x00 }, + { WCD9335_TX_5_6_ATEST_REFCTL, 0x0a, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_1, 0xeb, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_4, 0x7f, 0x00 }, + { WCD9335_FLYBACK_VNEG_CTRL_9, 0x64, 0x00 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_1, 0xed, 0x00 }, + { WCD9335_RX_BIAS_HPH_PA, 0x9a, 0x00 }, + { WCD9335_RX_BIAS_HPH_LOWPOWER, 0x82, 0x00 }, + { WCD9335_HPH_PA_CTL2, 0x50, 0x00 }, + { WCD9335_HPH_L_EN, 0x80, 0x00 }, + { WCD9335_HPH_R_EN, 0x80, 0x00 }, + { WCD9335_HPH_R_ATEST, 0x54, 0x00 }, + { WCD9335_HPH_RDAC_LDO_CTL, 0x33, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG0, 0x10, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CFG1, 0x02, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC2, 0x01, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC3, 0x3c, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER2_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER3_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER4_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER5_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER6_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER7_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_COMPANDER8_CTL7, 0x08, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CFG1, 0x44, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x1e, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC0, 0xfc, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC1, 0x08, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x08, 0x00 }, + { WCD9335_SPLINE_SRC0_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_SPLINE_SRC1_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_SPLINE_SRC2_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_SPLINE_SRC3_CLK_RST_CTL_0, 0x20, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x0c, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, 0x10, 0x00 }, + { WCD9335_TEST_DEBUG_NPL_DLY_TEST_2, 0x60, 0x00 }, + { WCD9335_DATA_HUB_NATIVE_FIFO_SYNC, 0x00, 0x00 }, + { WCD9335_DATA_HUB_NATIVE_FIFO_STATUS, 0x00, 0x00 }, + { WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD, 0x60, 0x00 }, + { WCD9335_CPE_SS_TX_PP_CFG, 0x3C, 0x00 }, + { WCD9335_CPE_SS_SVA_CFG, 0x00, 0x00 }, + { WCD9335_MBHC_FSM_STATUS, 0x00, 0x00 }, + { WCD9335_FLYBACK_CTRL_1, 0x45, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC7, 0x25, 0x00 }, + { WCD9335_SPLINE_SRC0_STATUS, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC1_STATUS, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC2_STATUS, 0x00, 0x00 }, + { WCD9335_SPLINE_SRC3_STATUS, 0x00, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT, 0x00, 0x00 }, +}; + +static const struct reg_default wcd9335_defaults[] = { + /* Page #0 registers */ + { WCD9335_PAGE0_PAGE_REGISTER, 0x00 }, + { WCD9335_CODEC_RPM_CLK_BYPASS, 0x00 }, + { WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x00 }, + { WCD9335_CODEC_RPM_RST_CTL, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x07 }, + { WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3, 0x00 }, + { WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN, 0x01 }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1, 0xff }, + { WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2, 0xff }, + { WCD9335_CODEC_RPM_INT_MASK, 0x3f }, + { WCD9335_CODEC_RPM_INT_STATUS, 0x00 }, + { WCD9335_CODEC_RPM_INT_CLEAR, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2, 0x07 }, + { WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3, 0x01 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO, 0x0d }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL, 0xCC }, + { WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL, 0x06 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL, 0x06 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL, 0x06 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB, 0x00 }, + { WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, 0x0c }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, 0x0c }, + { WCD9335_DATA_HUB_DATA_HUB_I2S_CLK, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG, 0x00 }, + { WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG, 0x00 }, + { WCD9335_INTR_CFG, 0x00 }, + { WCD9335_INTR_CLR_COMMIT, 0x00 }, + { WCD9335_INTR_PIN1_MASK0, 0xff }, + { WCD9335_INTR_PIN1_MASK1, 0xff }, + { WCD9335_INTR_PIN1_MASK2, 0xff }, + { WCD9335_INTR_PIN1_MASK3, 0xff }, + { WCD9335_INTR_PIN1_STATUS0, 0x00 }, + { WCD9335_INTR_PIN1_STATUS1, 0x00 }, + { WCD9335_INTR_PIN1_STATUS2, 0x00 }, + { WCD9335_INTR_PIN1_STATUS3, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR0, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR1, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR2, 0x00 }, + { WCD9335_INTR_PIN1_CLEAR3, 0x00 }, + { WCD9335_INTR_PIN2_MASK0, 0xff }, + { WCD9335_INTR_PIN2_MASK1, 0xff }, + { WCD9335_INTR_PIN2_MASK2, 0xff }, + { WCD9335_INTR_PIN2_MASK3, 0xff }, + { WCD9335_INTR_PIN2_STATUS0, 0x00 }, + { WCD9335_INTR_PIN2_STATUS1, 0x00 }, + { WCD9335_INTR_PIN2_STATUS2, 0x00 }, + { WCD9335_INTR_PIN2_STATUS3, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR0, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR1, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR2, 0x00 }, + { WCD9335_INTR_PIN2_CLEAR3, 0x00 }, + { WCD9335_INTR_LEVEL0, 0x03 }, + { WCD9335_INTR_LEVEL1, 0xe0 }, + { WCD9335_INTR_LEVEL2, 0x10 }, + { WCD9335_INTR_LEVEL3, 0x80 }, + { WCD9335_INTR_BYPASS0, 0x00 }, + { WCD9335_INTR_BYPASS1, 0x00 }, + { WCD9335_INTR_BYPASS2, 0x00 }, + { WCD9335_INTR_BYPASS3, 0x00 }, + { WCD9335_INTR_SET0, 0x00 }, + { WCD9335_INTR_SET1, 0x00 }, + { WCD9335_INTR_SET2, 0x00 }, + { WCD9335_INTR_SET3, 0x00 }, + /* Page #1 registers */ + { WCD9335_PAGE1_PAGE_REGISTER, 0x00 }, + { WCD9335_CPE_FLL_USER_CTL_0, 0x71 }, + { WCD9335_CPE_FLL_USER_CTL_1, 0x34 }, + { WCD9335_CPE_FLL_USER_CTL_2, 0x0b }, + { WCD9335_CPE_FLL_USER_CTL_3, 0x02 }, + { WCD9335_CPE_FLL_USER_CTL_4, 0x04 }, + { WCD9335_CPE_FLL_USER_CTL_5, 0x02 }, + { WCD9335_CPE_FLL_USER_CTL_6, 0x64 }, + { WCD9335_CPE_FLL_USER_CTL_7, 0x00 }, + { WCD9335_CPE_FLL_USER_CTL_8, 0x94 }, + { WCD9335_CPE_FLL_USER_CTL_9, 0x70 }, + { WCD9335_CPE_FLL_L_VAL_CTL_0, 0x40 }, + { WCD9335_CPE_FLL_L_VAL_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD9335_CPE_FLL_CONFIG_CTL_0, 0x6b }, + { WCD9335_CPE_FLL_CONFIG_CTL_1, 0x05 }, + { WCD9335_CPE_FLL_CONFIG_CTL_2, 0x08 }, + { WCD9335_CPE_FLL_CONFIG_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_CONFIG_CTL_4, 0x10 }, + { WCD9335_CPE_FLL_TEST_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_2, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_4, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_5, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_6, 0x00 }, + { WCD9335_CPE_FLL_TEST_CTL_7, 0x33 }, + { WCD9335_CPE_FLL_FREQ_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_FREQ_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_FREQ_CTL_2, 0x00 }, + { WCD9335_CPE_FLL_FREQ_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_0, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_1, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_2, 0x00 }, + { WCD9335_CPE_FLL_SSC_CTL_3, 0x00 }, + { WCD9335_CPE_FLL_FLL_MODE, 0x20 }, + { WCD9335_CPE_FLL_STATUS_0, 0x00 }, + { WCD9335_CPE_FLL_STATUS_1, 0x00 }, + { WCD9335_CPE_FLL_STATUS_2, 0x00 }, + { WCD9335_CPE_FLL_STATUS_3, 0x00 }, + { WCD9335_I2S_FLL_USER_CTL_0, 0x41 }, + { WCD9335_I2S_FLL_USER_CTL_1, 0x94 }, + { WCD9335_I2S_FLL_USER_CTL_2, 0x08 }, + { WCD9335_I2S_FLL_USER_CTL_3, 0x02 }, + { WCD9335_I2S_FLL_USER_CTL_4, 0x04 }, + { WCD9335_I2S_FLL_USER_CTL_5, 0x02 }, + { WCD9335_I2S_FLL_USER_CTL_6, 0x40 }, + { WCD9335_I2S_FLL_USER_CTL_7, 0x00 }, + { WCD9335_I2S_FLL_USER_CTL_8, 0x5f }, + { WCD9335_I2S_FLL_USER_CTL_9, 0x02 }, + { WCD9335_I2S_FLL_L_VAL_CTL_0, 0x40 }, + { WCD9335_I2S_FLL_L_VAL_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD9335_I2S_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD9335_I2S_FLL_CONFIG_CTL_0, 0x6b }, + { WCD9335_I2S_FLL_CONFIG_CTL_1, 0x05 }, + { WCD9335_I2S_FLL_CONFIG_CTL_2, 0x08 }, + { WCD9335_I2S_FLL_CONFIG_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_CONFIG_CTL_4, 0x30 }, + { WCD9335_I2S_FLL_TEST_CTL_0, 0x80 }, + { WCD9335_I2S_FLL_TEST_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_2, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_4, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_5, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_6, 0x00 }, + { WCD9335_I2S_FLL_TEST_CTL_7, 0xff }, + { WCD9335_I2S_FLL_FREQ_CTL_0, 0x00 }, + { WCD9335_I2S_FLL_FREQ_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_FREQ_CTL_2, 0x00 }, + { WCD9335_I2S_FLL_FREQ_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_0, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_1, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_2, 0x00 }, + { WCD9335_I2S_FLL_SSC_CTL_3, 0x00 }, + { WCD9335_I2S_FLL_FLL_MODE, 0x00 }, + { WCD9335_I2S_FLL_STATUS_0, 0x00 }, + { WCD9335_I2S_FLL_STATUS_1, 0x00 }, + { WCD9335_I2S_FLL_STATUS_2, 0x00 }, + { WCD9335_I2S_FLL_STATUS_3, 0x00 }, + { WCD9335_SB_FLL_USER_CTL_0, 0x41 }, + { WCD9335_SB_FLL_USER_CTL_1, 0x94 }, + { WCD9335_SB_FLL_USER_CTL_2, 0x08 }, + { WCD9335_SB_FLL_USER_CTL_3, 0x02 }, + { WCD9335_SB_FLL_USER_CTL_4, 0x04 }, + { WCD9335_SB_FLL_USER_CTL_5, 0x02 }, + { WCD9335_SB_FLL_USER_CTL_6, 0x40 }, + { WCD9335_SB_FLL_USER_CTL_7, 0x00 }, + { WCD9335_SB_FLL_USER_CTL_8, 0x5e }, + { WCD9335_SB_FLL_USER_CTL_9, 0x01 }, + { WCD9335_SB_FLL_L_VAL_CTL_0, 0x40 }, + { WCD9335_SB_FLL_L_VAL_CTL_1, 0x00 }, + { WCD9335_SB_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD9335_SB_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD9335_SB_FLL_CONFIG_CTL_0, 0x6b }, + { WCD9335_SB_FLL_CONFIG_CTL_1, 0x05 }, + { WCD9335_SB_FLL_CONFIG_CTL_2, 0x08 }, + { WCD9335_SB_FLL_CONFIG_CTL_3, 0x00 }, + { WCD9335_SB_FLL_CONFIG_CTL_4, 0x10 }, + { WCD9335_SB_FLL_TEST_CTL_0, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_1, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_2, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_3, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_4, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_5, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_6, 0x00 }, + { WCD9335_SB_FLL_TEST_CTL_7, 0xff }, + { WCD9335_SB_FLL_FREQ_CTL_0, 0x00 }, + { WCD9335_SB_FLL_FREQ_CTL_1, 0x00 }, + { WCD9335_SB_FLL_FREQ_CTL_2, 0x00 }, + { WCD9335_SB_FLL_FREQ_CTL_3, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_0, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_1, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_2, 0x00 }, + { WCD9335_SB_FLL_SSC_CTL_3, 0x00 }, + { WCD9335_SB_FLL_FLL_MODE, 0x00 }, + { WCD9335_SB_FLL_STATUS_0, 0x00 }, + { WCD9335_SB_FLL_STATUS_1, 0x00 }, + { WCD9335_SB_FLL_STATUS_2, 0x00 }, + { WCD9335_SB_FLL_STATUS_3, 0x00 }, + /* Page #2 registers */ + { WCD9335_PAGE2_PAGE_REGISTER, 0x00 }, + { WCD9335_CPE_SS_MEM_PTR_0, 0x00 }, + { WCD9335_CPE_SS_MEM_PTR_1, 0x00 }, + { WCD9335_CPE_SS_MEM_PTR_2, 0x00 }, + { WCD9335_CPE_SS_MEM_CTRL, 0x08 }, + { WCD9335_CPE_SS_MEM_BANK_0, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_1, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_2, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_3, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_4, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_5, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_6, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_7, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_8, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_9, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_10, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_11, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_12, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_13, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_14, 0x00 }, + { WCD9335_CPE_SS_MEM_BANK_15, 0x00 }, + { WCD9335_CPE_SS_INBOX1_TRG, 0x00 }, + { WCD9335_CPE_SS_INBOX2_TRG, 0x00 }, + { WCD9335_CPE_SS_INBOX1_0, 0x00 }, + { WCD9335_CPE_SS_INBOX1_1, 0x00 }, + { WCD9335_CPE_SS_INBOX1_2, 0x00 }, + { WCD9335_CPE_SS_INBOX1_3, 0x00 }, + { WCD9335_CPE_SS_INBOX1_4, 0x00 }, + { WCD9335_CPE_SS_INBOX1_5, 0x00 }, + { WCD9335_CPE_SS_INBOX1_6, 0x00 }, + { WCD9335_CPE_SS_INBOX1_7, 0x00 }, + { WCD9335_CPE_SS_INBOX1_8, 0x00 }, + { WCD9335_CPE_SS_INBOX1_9, 0x00 }, + { WCD9335_CPE_SS_INBOX1_10, 0x00 }, + { WCD9335_CPE_SS_INBOX1_11, 0x00 }, + { WCD9335_CPE_SS_INBOX1_12, 0x00 }, + { WCD9335_CPE_SS_INBOX1_13, 0x00 }, + { WCD9335_CPE_SS_INBOX1_14, 0x00 }, + { WCD9335_CPE_SS_INBOX1_15, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_0, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_1, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_2, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_3, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_4, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_5, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_6, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_7, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_8, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_9, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_10, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_11, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_12, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_13, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_14, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_15, 0x00 }, + { WCD9335_CPE_SS_INBOX2_0, 0x00 }, + { WCD9335_CPE_SS_INBOX2_1, 0x00 }, + { WCD9335_CPE_SS_INBOX2_2, 0x00 }, + { WCD9335_CPE_SS_INBOX2_3, 0x00 }, + { WCD9335_CPE_SS_INBOX2_4, 0x00 }, + { WCD9335_CPE_SS_INBOX2_5, 0x00 }, + { WCD9335_CPE_SS_INBOX2_6, 0x00 }, + { WCD9335_CPE_SS_INBOX2_7, 0x00 }, + { WCD9335_CPE_SS_INBOX2_8, 0x00 }, + { WCD9335_CPE_SS_INBOX2_9, 0x00 }, + { WCD9335_CPE_SS_INBOX2_10, 0x00 }, + { WCD9335_CPE_SS_INBOX2_11, 0x00 }, + { WCD9335_CPE_SS_INBOX2_12, 0x00 }, + { WCD9335_CPE_SS_INBOX2_13, 0x00 }, + { WCD9335_CPE_SS_INBOX2_14, 0x00 }, + { WCD9335_CPE_SS_INBOX2_15, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_0, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_1, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_2, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_3, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_4, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_5, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_6, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_7, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_8, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_9, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_10, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_11, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_12, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_13, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_14, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_15, 0x00 }, + { WCD9335_CPE_SS_OUTBOX1_ACK, 0x00 }, + { WCD9335_CPE_SS_OUTBOX2_ACK, 0x00 }, + { WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3c }, + { WCD9335_CPE_SS_US_BUF_INT_PERIOD, 0x60 }, + { WCD9335_CPE_SS_CFG, 0x41 }, + { WCD9335_CPE_SS_US_EC_MUX_CFG, 0x00 }, + { WCD9335_CPE_SS_MAD_CTL, 0x00 }, + { WCD9335_CPE_SS_CPAR_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC0_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC1_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC2_CTL, 0x00 }, + { WCD9335_CPE_SS_DMIC_CFG, 0x80 }, + { WCD9335_CPE_SS_CPAR_CFG, 0x00 }, + { WCD9335_CPE_SS_WDOG_CFG, 0x01 }, + { WCD9335_CPE_SS_BACKUP_INT, 0x00 }, + { WCD9335_CPE_SS_STATUS, 0x00 }, + { WCD9335_CPE_SS_CPE_OCD_CFG, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_STATUS, 0x00 }, + { WCD9335_CPE_SS_SS_ERROR_INT_CLEAR, 0x00 }, + { WCD9335_SOC_MAD_MAIN_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_MAIN_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_3, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_4, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_5, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_6, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_7, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_CTL_8, 0x00 }, + { WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_3, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_4, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_5, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_6, 0x00 }, + { WCD9335_SOC_MAD_ULTR_CTL_7, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_1, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_2, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_3, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_4, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_5, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_6, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_7, 0x00 }, + { WCD9335_SOC_MAD_BEACON_CTL_8, 0x00 }, + { WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR, 0x00 }, + { WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL, 0x00 }, + { WCD9335_SOC_MAD_INP_SEL, 0x00 }, + /* Page #6 registers */ + { WCD9335_PAGE6_PAGE_REGISTER, 0x00 }, + { WCD9335_ANA_BIAS, 0x00 }, + { WCD9335_ANA_CLK_TOP, 0x00 }, + { WCD9335_ANA_RCO, 0x30 }, + { WCD9335_ANA_BUCK_VOUT_A, 0xb4 }, + { WCD9335_ANA_BUCK_VOUT_D, 0xb4 }, + { WCD9335_ANA_BUCK_CTL, 0x00 }, + { WCD9335_ANA_BUCK_STATUS, 0xe0 }, + { WCD9335_ANA_RX_SUPPLIES, 0x00 }, + { WCD9335_ANA_HPH, 0x00 }, + { WCD9335_ANA_EAR, 0x00 }, + { WCD9335_ANA_LO_1_2, 0x00 }, + { WCD9335_ANA_LO_3_4, 0x00 }, + { WCD9335_ANA_MAD_SETUP, 0x81 }, + { WCD9335_ANA_AMIC1, 0x20 }, + { WCD9335_ANA_AMIC2, 0x00 }, + { WCD9335_ANA_AMIC3, 0x20 }, + { WCD9335_ANA_AMIC4, 0x00 }, + { WCD9335_ANA_AMIC5, 0x20 }, + { WCD9335_ANA_AMIC6, 0x00 }, + { WCD9335_ANA_MBHC_MECH, 0x39 }, + { WCD9335_ANA_MBHC_ELECT, 0x08 }, + { WCD9335_ANA_MBHC_ZDET, 0x00 }, + { WCD9335_ANA_MBHC_RESULT_1, 0x00 }, + { WCD9335_ANA_MBHC_RESULT_2, 0x00 }, + { WCD9335_ANA_MBHC_RESULT_3, 0x00 }, + { WCD9335_ANA_MBHC_BTN0, 0x00 }, + { WCD9335_ANA_MBHC_BTN1, 0x10 }, + { WCD9335_ANA_MBHC_BTN2, 0x20 }, + { WCD9335_ANA_MBHC_BTN3, 0x30 }, + { WCD9335_ANA_MBHC_BTN4, 0x40 }, + { WCD9335_ANA_MBHC_BTN5, 0x50 }, + { WCD9335_ANA_MBHC_BTN6, 0x60 }, + { WCD9335_ANA_MBHC_BTN7, 0x70 }, + { WCD9335_ANA_MICB1, 0x10 }, + { WCD9335_ANA_MICB2, 0x10 }, + { WCD9335_ANA_MICB2_RAMP, 0x00 }, + { WCD9335_ANA_MICB3, 0x10 }, + { WCD9335_ANA_MICB4, 0x10 }, + { WCD9335_ANA_VBADC, 0x00 }, + { WCD9335_BIAS_CTL, 0x28 }, + { WCD9335_CLOCK_TEST_CTL, 0x00 }, + { WCD9335_RCO_CTRL_1, 0x44 }, + { WCD9335_RCO_CTRL_2, 0x44 }, + { WCD9335_RCO_CAL, 0x00 }, + { WCD9335_RCO_CAL_1, 0x00 }, + { WCD9335_RCO_CAL_2, 0x00 }, + { WCD9335_RCO_TEST_CTRL, 0x00 }, + { WCD9335_RCO_CAL_OUT_1, 0x00 }, + { WCD9335_RCO_CAL_OUT_2, 0x00 }, + { WCD9335_RCO_CAL_OUT_3, 0x00 }, + { WCD9335_RCO_CAL_OUT_4, 0x00 }, + { WCD9335_RCO_CAL_OUT_5, 0x00 }, + { WCD9335_SIDO_SIDO_MODE_1, 0x84 }, + { WCD9335_SIDO_SIDO_MODE_2, 0xfe }, + { WCD9335_SIDO_SIDO_MODE_3, 0xf6 }, + { WCD9335_SIDO_SIDO_MODE_4, 0x56 }, + { WCD9335_SIDO_SIDO_VCL_1, 0x00 }, + { WCD9335_SIDO_SIDO_VCL_2, 0x6c }, + { WCD9335_SIDO_SIDO_VCL_3, 0x44 }, + { WCD9335_SIDO_SIDO_CCL_1, 0x57 }, + { WCD9335_SIDO_SIDO_CCL_4, 0x61 }, + { WCD9335_SIDO_SIDO_CCL_5, 0x6d }, + { WCD9335_SIDO_SIDO_CCL_6, 0x60 }, + { WCD9335_SIDO_SIDO_CCL_7, 0x6f }, + { WCD9335_SIDO_SIDO_CCL_9, 0x6e }, + { WCD9335_SIDO_SIDO_FILTER_1, 0x92 }, + { WCD9335_SIDO_SIDO_FILTER_2, 0x54 }, + { WCD9335_SIDO_SIDO_DRIVER_1, 0x77 }, + { WCD9335_SIDO_SIDO_CAL_CODE_EXT_1, 0x9c }, + { WCD9335_SIDO_SIDO_CAL_CODE_EXT_2, 0x82 }, + { WCD9335_SIDO_SIDO_CAL_CODE_OUT_1, 0x00 }, + { WCD9335_SIDO_SIDO_CAL_CODE_OUT_2, 0x00 }, + { WCD9335_SIDO_SIDO_TEST_1, 0x00 }, + { WCD9335_MBHC_CTL_1, 0x32 }, + { WCD9335_MBHC_CTL_2, 0x01 }, + { WCD9335_MBHC_PLUG_DETECT_CTL, 0x69 }, + { WCD9335_MBHC_ZDET_RAMP_CTL, 0x00 }, + { WCD9335_MBHC_TEST_CTL, 0x00 }, + { WCD9335_VBADC_SUBBLOCK_EN, 0xfe }, + { WCD9335_VBADC_IBIAS_FE, 0x54 }, + { WCD9335_VBADC_BIAS_ADC, 0x51 }, + { WCD9335_VBADC_FE_CTRL, 0x1c }, + { WCD9335_VBADC_ADC_REF, 0x20 }, + { WCD9335_VBADC_ADC_IO, 0x80 }, + { WCD9335_VBADC_ADC_SAR, 0xff }, + { WCD9335_VBADC_DEBUG, 0x00 }, + { WCD9335_VBADC_ADC_DOUTMSB, 0x00 }, + { WCD9335_VBADC_ADC_DOUTLSB, 0x00 }, + { WCD9335_LDOH_MODE, 0x2b }, + { WCD9335_LDOH_BIAS, 0x68 }, + { WCD9335_LDOH_STB_LOADS, 0x00 }, + { WCD9335_LDOH_SLOWRAMP, 0x50 }, + { WCD9335_MICB1_TEST_CTL_1, 0x1a }, + { WCD9335_MICB1_TEST_CTL_2, 0x18 }, + { WCD9335_MICB1_TEST_CTL_3, 0xa4 }, + { WCD9335_MICB2_TEST_CTL_1, 0x1a }, + { WCD9335_MICB2_TEST_CTL_2, 0x18 }, + { WCD9335_MICB2_TEST_CTL_3, 0x24 }, + { WCD9335_MICB3_TEST_CTL_1, 0x1a }, + { WCD9335_MICB3_TEST_CTL_2, 0x18 }, + { WCD9335_MICB3_TEST_CTL_3, 0xa4 }, + { WCD9335_MICB4_TEST_CTL_1, 0x1a }, + { WCD9335_MICB4_TEST_CTL_2, 0x18 }, + { WCD9335_MICB4_TEST_CTL_3, 0xa4 }, + { WCD9335_TX_COM_ADC_VCM, 0x39 }, + { WCD9335_TX_COM_BIAS_ATEST, 0xc0 }, + { WCD9335_TX_COM_ADC_INT1_IB, 0x6f }, + { WCD9335_TX_COM_ADC_INT2_IB, 0x4f }, + { WCD9335_TX_COM_TXFE_DIV_CTL, 0x2e }, + { WCD9335_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD9335_TX_COM_TXFE_DIV_STOP_9P6M, 0xc7 }, + { WCD9335_TX_COM_TXFE_DIV_STOP_12P288M, 0xff }, + { WCD9335_TX_1_2_TEST_EN, 0xcc }, + { WCD9335_TX_1_2_ADC_IB, 0x09 }, + { WCD9335_TX_1_2_TEST_CTL, 0x38 }, + { WCD9335_TX_1_2_TEST_BLK_EN, 0xff }, + { WCD9335_TX_1_2_TXFE_CLKDIV, 0x00 }, + { WCD9335_TX_1_2_SAR1_ERR, 0x00 }, + { WCD9335_TX_1_2_SAR2_ERR, 0x00 }, + { WCD9335_TX_3_4_TEST_EN, 0xcc }, + { WCD9335_TX_3_4_ADC_IB, 0x09 }, + { WCD9335_TX_3_4_TEST_CTL, 0x38 }, + { WCD9335_TX_3_4_TEST_BLK_EN, 0xff }, + { WCD9335_TX_3_4_TXFE_CLKDIV, 0x00 }, + { WCD9335_TX_3_4_SAR1_ERR, 0x00 }, + { WCD9335_TX_3_4_SAR2_ERR, 0x00 }, + { WCD9335_TX_5_6_TEST_EN, 0xcc }, + { WCD9335_TX_5_6_ADC_IB, 0x09 }, + { WCD9335_TX_5_6_TEST_CTL, 0x38 }, + { WCD9335_TX_5_6_TEST_BLK_EN, 0xff }, + { WCD9335_TX_5_6_TXFE_CLKDIV, 0x00 }, + { WCD9335_TX_5_6_SAR1_ERR, 0x00 }, + { WCD9335_TX_5_6_SAR2_ERR, 0x00 }, + { WCD9335_CLASSH_MODE_1, 0x40 }, + { WCD9335_CLASSH_MODE_2, 0x3a }, + { WCD9335_CLASSH_MODE_3, 0x00 }, + { WCD9335_CLASSH_CTRL_VCL_1, 0x70 }, + { WCD9335_CLASSH_CTRL_VCL_2, 0xa2 }, + { WCD9335_CLASSH_CTRL_CCL_1, 0x51 }, + { WCD9335_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD9335_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD9335_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD9335_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD9335_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD9335_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD9335_CLASSH_SPARE, 0x00 }, + { WCD9335_FLYBACK_EN, 0x4e }, + { WCD9335_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD9335_FLYBACK_VNEG_CTRL_3, 0x74 }, + { WCD9335_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD9335_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD9335_FLYBACK_VNEG_CTRL_7, 0xa9 }, + { WCD9335_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_2, 0x50 }, + { WCD9335_FLYBACK_VNEG_DAC_CTRL_3, 0xa6 }, + { WCD9335_FLYBACK_TEST_CTL, 0x00 }, + { WCD9335_RX_AUX_SW_CTL, 0x00 }, + { WCD9335_RX_PA_AUX_IN_CONN, 0x00 }, + { WCD9335_RX_TIMER_DIV, 0x74 }, + { WCD9335_RX_OCP_CTL, 0x1f }, + { WCD9335_RX_OCP_COUNT, 0x77 }, + { WCD9335_RX_BIAS_EAR_DAC, 0xa0 }, + { WCD9335_RX_BIAS_EAR_AMP, 0xaa }, + { WCD9335_RX_BIAS_HPH_LDO, 0xa9 }, + { WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8a }, + { WCD9335_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD9335_RX_BIAS_HPH_CNP1, 0x86 }, + { WCD9335_RX_BIAS_DIFFLO_PA, 0x80 }, + { WCD9335_RX_BIAS_DIFFLO_REF, 0x88 }, + { WCD9335_RX_BIAS_DIFFLO_LDO, 0x88 }, + { WCD9335_RX_BIAS_SELO_DAC_PA, 0xa8 }, + { WCD9335_RX_BIAS_BUCK_RST, 0x08 }, + { WCD9335_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD9335_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD9335_RX_BIAS_FLYB_BUFF, 0xaa }, + { WCD9335_RX_BIAS_FLYB_MID_RST, 0x44 }, + { WCD9335_HPH_L_STATUS, 0x04 }, + { WCD9335_HPH_R_STATUS, 0x04 }, + { WCD9335_HPH_CNP_EN, 0x80 }, + { WCD9335_HPH_CNP_WG_CTL, 0xda }, + { WCD9335_HPH_CNP_WG_TIME, 0x15 }, + { WCD9335_HPH_OCP_CTL, 0x28 }, + { WCD9335_HPH_AUTO_CHOP, 0x12 }, + { WCD9335_HPH_CHOP_CTL, 0x83 }, + { WCD9335_HPH_PA_CTL1, 0x46 }, + { WCD9335_HPH_L_TEST, 0x00 }, + { WCD9335_HPH_L_ATEST, 0x50 }, + { WCD9335_HPH_R_TEST, 0x00 }, + { WCD9335_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD9335_HPH_RDAC_CLK_CTL2, 0x9b }, + { WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD9335_HPH_REFBUFF_UHQA_CTL, 0xa8 }, + { WCD9335_HPH_REFBUFF_LP_CTL, 0x00 }, + { WCD9335_HPH_L_DAC_CTL, 0x00 }, + { WCD9335_HPH_R_DAC_CTL, 0x00 }, + { WCD9335_EAR_EN_REG, 0x60 }, + { WCD9335_EAR_CMBUFF, 0x0d }, + { WCD9335_EAR_ICTL, 0x40 }, + { WCD9335_EAR_EN_DBG_CTL, 0x00 }, + { WCD9335_EAR_CNP, 0xe0 }, + { WCD9335_EAR_DAC_CTL_ATEST, 0x00 }, + { WCD9335_EAR_STATUS_REG, 0x04 }, + { WCD9335_EAR_OUT_SHORT, 0x00 }, + { WCD9335_DIFF_LO_MISC, 0x03 }, + { WCD9335_DIFF_LO_LO2_COMPANDER, 0x00 }, + { WCD9335_DIFF_LO_LO1_COMPANDER, 0x00 }, + { WCD9335_DIFF_LO_COMMON, 0x40 }, + { WCD9335_DIFF_LO_BYPASS_EN, 0x00 }, + { WCD9335_DIFF_LO_CNP, 0x20 }, + { WCD9335_DIFF_LO_CORE_OUT_PROG, 0x00 }, + { WCD9335_DIFF_LO_LDO_OUT_PROG, 0x00 }, + { WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x9b }, + { WCD9335_DIFF_LO_COM_PA_FREQ, 0xb0 }, + { WCD9335_DIFF_LO_RESERVED_REG, 0x60 }, + { WCD9335_DIFF_LO_LO1_STATUS_1, 0x00 }, + { WCD9335_DIFF_LO_LO1_STATUS_2, 0x00 }, + { WCD9335_SE_LO_COM1, 0x80 }, + { WCD9335_SE_LO_COM2, 0x04 }, + { WCD9335_SE_LO_LO3_GAIN, 0x20 }, + { WCD9335_SE_LO_LO3_CTRL, 0x04 }, + { WCD9335_SE_LO_LO4_GAIN, 0x20 }, + { WCD9335_SE_LO_LO4_CTRL, 0x04 }, + { WCD9335_SE_LO_LO3_STATUS, 0x00 }, + { WCD9335_SE_LO_LO4_STATUS, 0x00 }, + /* Page #10 registers */ + { WCD9335_PAGE10_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x00 }, + { WCD9335_CDC_ANC0_MODE_1_CTL, 0x00 }, + { WCD9335_CDC_ANC0_MODE_2_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FF_SHIFT, 0x00 }, + { WCD9335_CDC_ANC0_FB_SHIFT, 0x00 }, + { WCD9335_CDC_ANC0_LPF_FF_A_CTL, 0x00 }, + { WCD9335_CDC_ANC0_LPF_FF_B_CTL, 0x00 }, + { WCD9335_CDC_ANC0_LPF_FB_CTL, 0x00 }, + { WCD9335_CDC_ANC0_SMLPF_CTL, 0x00 }, + { WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL, 0x00 }, + { WCD9335_CDC_ANC0_IIR_ADAPT_CTL, 0x00 }, + { WCD9335_CDC_ANC0_IIR_COEFF_1_CTL, 0x00 }, + { WCD9335_CDC_ANC0_IIR_COEFF_2_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FF_A_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FF_B_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC0_FB_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x00 }, + { WCD9335_CDC_ANC1_MODE_1_CTL, 0x00 }, + { WCD9335_CDC_ANC1_MODE_2_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FF_SHIFT, 0x00 }, + { WCD9335_CDC_ANC1_FB_SHIFT, 0x00 }, + { WCD9335_CDC_ANC1_LPF_FF_A_CTL, 0x00 }, + { WCD9335_CDC_ANC1_LPF_FF_B_CTL, 0x00 }, + { WCD9335_CDC_ANC1_LPF_FB_CTL, 0x00 }, + { WCD9335_CDC_ANC1_SMLPF_CTL, 0x00 }, + { WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL, 0x00 }, + { WCD9335_CDC_ANC1_IIR_ADAPT_CTL, 0x00 }, + { WCD9335_CDC_ANC1_IIR_COEFF_1_CTL, 0x00 }, + { WCD9335_CDC_ANC1_IIR_COEFF_2_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FF_A_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FF_B_GAIN_CTL, 0x00 }, + { WCD9335_CDC_ANC1_FB_GAIN_CTL, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX0_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX0_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX0_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX1_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX1_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX1_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX2_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX2_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX2_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX3_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX3_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX3_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX4_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX4_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX4_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX5_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX5_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX5_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX6_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX6_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX6_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX7_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX7_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX7_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_CTL, 0x04 }, + { WCD9335_CDC_TX8_TX_VOL_CTL, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_192_CTL, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_192_CFG, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC0, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC1, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC4, 0x20 }, + { WCD9335_CDC_TX8_TX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_TX8_TX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x00 }, + /* Page #11 registers */ + { WCD9335_PAGE11_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER1_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER1_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER1_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER1_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER1_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER1_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER2_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER2_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER2_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER2_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER2_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER2_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER2_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER3_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER3_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER3_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER3_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER3_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER3_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER3_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER4_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER4_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER4_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER4_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER4_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER4_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER4_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER5_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER5_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER5_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER5_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER5_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER5_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER5_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER6_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER6_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER6_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER6_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER6_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER6_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER6_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER7_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER7_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER7_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER7_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER7_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER7_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER7_CTL6, 0x01 }, + { WCD9335_CDC_COMPANDER8_CTL0, 0x60 }, + { WCD9335_CDC_COMPANDER8_CTL1, 0xdb }, + { WCD9335_CDC_COMPANDER8_CTL2, 0xff }, + { WCD9335_CDC_COMPANDER8_CTL3, 0x35 }, + { WCD9335_CDC_COMPANDER8_CTL4, 0xff }, + { WCD9335_CDC_COMPANDER8_CTL5, 0x00 }, + { WCD9335_CDC_COMPANDER8_CTL6, 0x01 }, + { WCD9335_CDC_RX0_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX0_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX0_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX0_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX0_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX1_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX1_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX1_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC4, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX1_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX2_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX2_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX2_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC4, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX2_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX3_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX3_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX3_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX3_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX4_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX4_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX4_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX4_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX5_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX5_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX5_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX5_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX6_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX6_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX6_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX6_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX7_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX7_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX7_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX7_RX_PATH_MIX_SEC1, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CTL, 0x04 }, + { WCD9335_CDC_RX8_RX_PATH_CFG0, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_CFG2, 0x8f }, + { WCD9335_CDC_RX8_RX_VOL_CTL, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_CTL, 0x04 }, + { WCD9335_CDC_RX8_RX_VOL_MIX_CTL, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC2, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC3, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC5, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC6, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_SEC7, 0x00 }, + { WCD9335_CDC_RX8_RX_PATH_MIX_SEC1, 0x00 }, + /* Page #12 registers */ + { WCD9335_PAGE12_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_CLSH_CRC, 0x00 }, + { WCD9335_CDC_CLSH_DLY_CTRL, 0x03 }, + { WCD9335_CDC_CLSH_DECAY_CTRL, 0x02 }, + { WCD9335_CDC_CLSH_HPH_V_PA, 0x1c }, + { WCD9335_CDC_CLSH_EAR_V_PA, 0x39 }, + { WCD9335_CDC_CLSH_HPH_V_HD, 0x0c }, + { WCD9335_CDC_CLSH_EAR_V_HD, 0x0c }, + { WCD9335_CDC_CLSH_K1_MSB, 0x01 }, + { WCD9335_CDC_CLSH_K1_LSB, 0x00 }, + { WCD9335_CDC_CLSH_K2_MSB, 0x00 }, + { WCD9335_CDC_CLSH_K2_LSB, 0x80 }, + { WCD9335_CDC_CLSH_IDLE_CTRL, 0x00 }, + { WCD9335_CDC_CLSH_IDLE_HPH, 0x00 }, + { WCD9335_CDC_CLSH_IDLE_EAR, 0x00 }, + { WCD9335_CDC_CLSH_TEST0, 0x07 }, + { WCD9335_CDC_CLSH_TEST1, 0x00 }, + { WCD9335_CDC_CLSH_OVR_VREF, 0x00 }, + { WCD9335_CDC_BOOST0_BOOST_PATH_CTL, 0x00 }, + { WCD9335_CDC_BOOST0_BOOST_CTL, 0xb2 }, + { WCD9335_CDC_BOOST0_BOOST_CFG1, 0x00 }, + { WCD9335_CDC_BOOST0_BOOST_CFG2, 0x00 }, + { WCD9335_CDC_BOOST1_BOOST_PATH_CTL, 0x00 }, + { WCD9335_CDC_BOOST1_BOOST_CTL, 0xb2 }, + { WCD9335_CDC_BOOST1_BOOST_CFG1, 0x00 }, + { WCD9335_CDC_BOOST1_BOOST_CFG2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_DATA_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_0, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_1, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_2, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_RD_DATA_3, 0x00 }, + { WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG, 0x0f }, + { WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS, 0x03 }, + { WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_CFG, 0x0a }, + { WCD9335_CDC_VBAT_VBAT_ADC_CAL1, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_ADC_CAL2, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_ADC_CAL3, 0x04 }, + { WCD9335_CDC_VBAT_VBAT_PK_EST1, 0xe0 }, + { WCD9335_CDC_VBAT_VBAT_PK_EST2, 0x01 }, + { WCD9335_CDC_VBAT_VBAT_PK_EST3, 0x40 }, + { WCD9335_CDC_VBAT_VBAT_RF_PROC1, 0x2a }, + { WCD9335_CDC_VBAT_VBAT_RF_PROC2, 0x86 }, + { WCD9335_CDC_VBAT_VBAT_TAC1, 0x70 }, + { WCD9335_CDC_VBAT_VBAT_TAC2, 0x18 }, + { WCD9335_CDC_VBAT_VBAT_TAC3, 0x18 }, + { WCD9335_CDC_VBAT_VBAT_TAC4, 0x03 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD1, 0x01 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD2, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD3, 0x64 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD4, 0x01 }, + { WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON, 0x00 }, + { WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL, 0x00 }, + { WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04 }, + { WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00 }, + { WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, 0x04 }, + { WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1, 0x00 }, + /* Page #13 registers */ + { WCD9335_PAGE13_PAGE_REGISTER, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 0x00 }, + { WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0x00 }, + { WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 0x00 }, + { WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, + { WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_CTL, 0x08 }, + { WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1, 0x4b }, + { WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_STATUS, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD, 0x00 }, + { WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_CTL, 0x40 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_CTL, 0x40 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL, 0x00 }, + { WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG0, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG1, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG2, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG3, 0x18 }, + { WCD9335_CDC_TOP_TOP_CFG4, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG5, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG6, 0x00 }, + { WCD9335_CDC_TOP_TOP_CFG7, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHL_COMP_RD_MSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_HPHR_COMP_RD_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_LUT, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB, 0x00 }, + { WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB, 0x00 }, + /* Page #0x80 registers */ + { WCD9335_PAGE80_PAGE_REGISTER, 0x00 }, + { WCD9335_TLMM_BIST_MODE_PINCFG, 0x00 }, + { WCD9335_TLMM_RF_PA_ON_PINCFG, 0x00 }, + { WCD9335_TLMM_INTR1_PINCFG, 0x00 }, + { WCD9335_TLMM_INTR2_PINCFG, 0x00 }, + { WCD9335_TLMM_SWR_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_SWR_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_SLIMBUS_DATA2_PINCFG, 0x00 }, + { WCD9335_TLMM_I2C_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_I2C_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_SD0_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_SD1_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_SCK_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_RX_WS_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_SD0_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_SD1_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_SCK_PINCFG, 0x00 }, + { WCD9335_TLMM_I2S_TX_WS_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC1_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC1_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC2_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC2_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC3_CLK_PINCFG, 0x00 }, + { WCD9335_TLMM_DMIC3_DATA_PINCFG, 0x00 }, + { WCD9335_TLMM_JTDI_PINCFG, 0x00 }, + { WCD9335_TLMM_JTDO_PINCFG, 0x00 }, + { WCD9335_TLMM_JTMS_PINCFG, 0x00 }, + { WCD9335_TLMM_JTCK_PINCFG, 0x00 }, + { WCD9335_TLMM_JTRST_PINCFG, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_0, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_1, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_2, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_OE_3, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_0, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_1, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_2, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_CTL_DATA_3, 0x00 }, + { WCD9335_TEST_DEBUG_PAD_DRVCTL, 0x00 }, + { WCD9335_TEST_DEBUG_PIN_STATUS, 0x00 }, + { WCD9335_TEST_DEBUG_MEM_CTRL, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_BUS_SEL, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_JTAG, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_EN_1, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_EN_2, 0x00 }, + { WCD9335_TEST_DEBUG_DEBUG_EN_3, 0x00 }, +}; + +/* + * wcd9335_regmap_register_patch: Update register defaults based on version + * @regmap: handle to wcd9xxx regmap + * @version: wcd9335 version + * + * Returns error code in case of failure or 0 for success + */ +int wcd9335_regmap_register_patch(struct regmap *regmap, int version) +{ + int rc; + + if (!regmap) { + pr_err("%s: regmap struct is NULL\n", __func__); + return -EINVAL; + } + + switch (version) { + case TASHA_VERSION_1_0: + case TASHA_VERSION_1_1: + regcache_cache_only(regmap, true); + rc = regmap_multi_reg_write(regmap, wcd9335_1_x_defaults, + ARRAY_SIZE(wcd9335_1_x_defaults)); + regcache_cache_only(regmap, false); + break; + case TASHA_VERSION_2_0: + regcache_cache_only(regmap, true); + rc = regmap_multi_reg_write(regmap, wcd9335_2_0_defaults, + ARRAY_SIZE(wcd9335_2_0_defaults)); + regcache_cache_only(regmap, false); + break; + default: + pr_err("%s: unknown version: %d\n", __func__, version); + rc = -EINVAL; + break; + } + + return rc; +} +EXPORT_SYMBOL(wcd9335_regmap_register_patch); + +static bool wcd9335_is_readable_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + /* + * Get the page number from MSB of codec register. If its 0x80, assign + * the corresponding page index PAGE_0x80. + */ + pg_num = reg >> 0x8; + if (pg_num == 0x80) + pg_num = PAGE_0X80; + else if (pg_num >= 0xE) + return false; + + reg_tbl = wcd9335_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl) + return reg_tbl[reg_offset]; + else + return false; +} + +static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg) +{ + /* + * registers from 0x000 to 0x0FF are volatile because + * this space contains registers related to interrupt + * status, mask etc + */ + if (reg < 0x100) + return true; + + /* IIR Coeff registers are not cacheable */ + if ((reg >= WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL) && + (reg <= WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)) + return true; + + if ((reg >= WCD9335_CDC_ANC0_IIR_COEFF_1_CTL) && + (reg <= WCD9335_CDC_ANC0_FB_GAIN_CTL)) + return true; + + if ((reg >= WCD9335_CDC_ANC1_IIR_COEFF_1_CTL) && + (reg <= WCD9335_CDC_ANC1_FB_GAIN_CTL)) + return true; + /* + * CPE inbox and outbox registers are volatile + * since they can be updated in the codec hardware + * to indicate CPE status + */ + if (reg >= WCD9335_CPE_SS_MEM_PTR_0 && + reg <= WCD9335_CPE_SS_OUTBOX2_ACK) + return true; + + if (reg >= WCD9335_RCO_CAL_OUT_1 && + reg <= WCD9335_RCO_CAL_OUT_5) + return true; + + switch (reg) { + case WCD9335_CPE_SS_INBOX1_TRG: + case WCD9335_CPE_SS_INBOX2_TRG: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_0: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_1: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_2: + case WCD9335_SWR_AHB_BRIDGE_WR_DATA_3: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2: + case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_0: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_1: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_2: + case WCD9335_SWR_AHB_BRIDGE_RD_DATA_3: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2: + case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3: + case WCD9335_ANA_BIAS: + case WCD9335_ANA_CLK_TOP: + case WCD9335_ANA_RCO: + case WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL: + case WCD9335_ANA_MBHC_RESULT_3: + case WCD9335_ANA_MBHC_RESULT_2: + case WCD9335_ANA_MBHC_RESULT_1: + case WCD9335_ANA_MBHC_MECH: + case WCD9335_ANA_MBHC_ELECT: + case WCD9335_ANA_MBHC_ZDET: + case WCD9335_ANA_MICB2: + case WCD9335_CPE_SS_SS_ERROR_INT_STATUS: + case WCD9335_CPE_SS_SS_ERROR_INT_MASK: + case WCD9335_CPE_SS_SS_ERROR_INT_CLEAR: + case WCD9335_CPE_SS_STATUS: + case WCD9335_CPE_SS_BACKUP_INT: + case WCD9335_CPE_SS_CFG: + case WCD9335_SOC_MAD_MAIN_CTL_1: + case WCD9335_SOC_MAD_AUDIO_CTL_3: + case WCD9335_SOC_MAD_AUDIO_CTL_4: + case WCD9335_FLYBACK_EN: + case WCD9335_ANA_RX_SUPPLIES: + case WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL: + case WCD9335_SIDO_SIDO_CCL_2: + case WCD9335_SIDO_SIDO_CCL_4: + case WCD9335_DATA_HUB_NATIVE_FIFO_STATUS: + case WCD9335_MBHC_FSM_STATUS: + case WCD9335_SPLINE_SRC0_STATUS: + case WCD9335_SPLINE_SRC1_STATUS: + case WCD9335_SPLINE_SRC2_STATUS: + case WCD9335_SPLINE_SRC3_STATUS: + case WCD9335_SIDO_SIDO_TEST_2: + case WCD9335_SIDO_SIDO_CCL_8: + case WCD9335_BIAS_VBG_FINE_ADJ: + case WCD9335_VBADC_ADC_DOUTMSB: + case WCD9335_VBADC_ADC_DOUTLSB: + case WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL: + case WCD9335_ANA_BUCK_CTL: + return true; + default: + return false; + } +} + +struct regmap_config wcd9335_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd9335_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd9335_defaults), + .max_register = WCD9335_MAX_REGISTER, + .volatile_reg = wcd9335_is_volatile_register, + .readable_reg = wcd9335_is_readable_register, + .can_multi_write = true, +}; diff --git a/drivers/mfd/wcd9335-tables.c b/drivers/mfd/wcd9335-tables.c new file mode 100644 index 000000000000..f5c32efe76ef --- /dev/null +++ b/drivers/mfd/wcd9335-tables.c @@ -0,0 +1,1325 @@ +/* + * 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 + +#define WCD9335_REG(reg) ((reg) & 0xFF) + +const u8 wcd9335_page0_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE0_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_CLK_BYPASS)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_CLK_GATE)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_CLK_MCLK_CFG)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_RST_CTL)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_INT_MASK)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_INT_STATUS)] = 1, + [WCD9335_REG(WCD9335_CODEC_RPM_INT_CLEAR)] = 0, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB)] = 1, + [WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_I2S_CLK)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_NATIVE_FIFO_SYNC)] = 1, + [WCD9335_REG(WCD9335_DATA_HUB_NATIVE_FIFO_STATUS)] = 1, + [WCD9335_REG(WCD9335_INTR_CFG)] = 1, + [WCD9335_REG(WCD9335_INTR_CLR_COMMIT)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_MASK3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_STATUS3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR0)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR1)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR2)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN1_CLEAR3)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_MASK3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS0)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS1)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS2)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_STATUS3)] = 1, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR0)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR1)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR2)] = 0, + [WCD9335_REG(WCD9335_INTR_PIN2_CLEAR3)] = 0, + [WCD9335_REG(WCD9335_INTR_LEVEL0)] = 1, + [WCD9335_REG(WCD9335_INTR_LEVEL1)] = 1, + [WCD9335_REG(WCD9335_INTR_LEVEL2)] = 1, + [WCD9335_REG(WCD9335_INTR_LEVEL3)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS0)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS1)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS2)] = 1, + [WCD9335_REG(WCD9335_INTR_BYPASS3)] = 1, + [WCD9335_REG(WCD9335_INTR_SET0)] = 1, + [WCD9335_REG(WCD9335_INTR_SET1)] = 1, + [WCD9335_REG(WCD9335_INTR_SET2)] = 1, + [WCD9335_REG(WCD9335_INTR_SET3)] = 1, +}; + +const u8 wcd9335_page1_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE1_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_4)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_5)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_6)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_7)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_8)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_9)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_L_VAL_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_L_VAL_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_DSM_FRAC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_DSM_FRAC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_4)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_4)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_5)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_6)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_7)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_3)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_FLL_MODE)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_0)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_CPE_FLL_STATUS_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_4)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_5)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_6)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_7)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_8)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_9)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_L_VAL_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_L_VAL_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_DSM_FRAC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_DSM_FRAC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_4)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_4)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_5)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_6)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_7)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_3)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_FLL_MODE)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_0)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_I2S_FLL_STATUS_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_8)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_USER_CTL_9)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_L_VAL_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_L_VAL_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_DSM_FRAC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_DSM_FRAC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_FLL_MODE)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_0)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_SB_FLL_STATUS_3)] = 1, +}; + +const u8 wcd9335_page2_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE2_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_CTRL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_3)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_4)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_5)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_6)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_7)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_8)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_9)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_10)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_11)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_12)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_13)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_14)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_15)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_TRG)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_TRG)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_0)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_1)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_2)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_3)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_4)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_5)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_6)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_7)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_8)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_9)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_10)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_11)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_12)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_13)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_14)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX1_15)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_3)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_4)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_5)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_6)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_7)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_8)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_9)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_10)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_11)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_12)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_13)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_14)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_15)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_0)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_1)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_2)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_3)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_4)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_5)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_6)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_7)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_8)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_9)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_10)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_11)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_12)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_13)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_14)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_INBOX2_15)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_0)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_1)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_2)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_3)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_4)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_5)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_6)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_7)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_8)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_9)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_10)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_11)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_12)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_13)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_14)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_15)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_ACK)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_ACK)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_EC_BUF_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_US_BUF_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_US_EC_MUX_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_MAD_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPAR_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_TX_PP_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC0_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC1_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC2_CTL)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_DMIC_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SVA_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPAR_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_WDOG_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_BACKUP_INT)] = 0, + [WCD9335_REG(WCD9335_CPE_SS_STATUS)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_CPE_OCD_CFG)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_MASK)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_STATUS)] = 1, + [WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_CLEAR)] = 0, + [WCD9335_REG(WCD9335_SOC_MAD_MAIN_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_MAIN_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_8)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_1)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_2)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_3)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_4)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_5)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_6)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_7)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_8)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL)] = 1, + [WCD9335_REG(WCD9335_SOC_MAD_INP_SEL)] = 1, +}; + +const u8 wcd9335_page6_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE6_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_ANA_BIAS)] = 1, + [WCD9335_REG(WCD9335_ANA_CLK_TOP)] = 1, + [WCD9335_REG(WCD9335_ANA_RCO)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_VOUT_A)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_VOUT_D)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_CTL)] = 1, + [WCD9335_REG(WCD9335_ANA_BUCK_STATUS)] = 1, + [WCD9335_REG(WCD9335_ANA_RX_SUPPLIES)] = 1, + [WCD9335_REG(WCD9335_ANA_HPH)] = 1, + [WCD9335_REG(WCD9335_ANA_EAR)] = 1, + [WCD9335_REG(WCD9335_ANA_LO_1_2)] = 1, + [WCD9335_REG(WCD9335_ANA_LO_3_4)] = 1, + [WCD9335_REG(WCD9335_ANA_MAD_SETUP)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC1)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC2)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC3)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC4)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC5)] = 1, + [WCD9335_REG(WCD9335_ANA_AMIC6)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_MECH)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_ELECT)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_ZDET)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_RESULT_1)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_RESULT_2)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_RESULT_3)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN0)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN1)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN2)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN3)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN4)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN5)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN6)] = 1, + [WCD9335_REG(WCD9335_ANA_MBHC_BTN7)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB1)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB2)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB2_RAMP)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB3)] = 1, + [WCD9335_REG(WCD9335_ANA_MICB4)] = 1, + [WCD9335_REG(WCD9335_ANA_VBADC)] = 1, + [WCD9335_REG(WCD9335_BIAS_CTL)] = 1, + [WCD9335_REG(WCD9335_BIAS_VBG_FINE_ADJ)] = 1, + [WCD9335_REG(WCD9335_CLOCK_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_RCO_CTRL_1)] = 1, + [WCD9335_REG(WCD9335_RCO_CTRL_2)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_1)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_2)] = 1, + [WCD9335_REG(WCD9335_RCO_TEST_CTRL)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_1)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_2)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_3)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_4)] = 1, + [WCD9335_REG(WCD9335_RCO_CAL_OUT_5)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_MODE_4)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_VCL_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_VCL_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_VCL_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_4)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_5)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_6)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_7)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_8)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_9)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CCL_10)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_FILTER_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_FILTER_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_3)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_EXT_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_EXT_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_OUT_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_OUT_2)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_TEST_1)] = 1, + [WCD9335_REG(WCD9335_SIDO_SIDO_TEST_2)] = 1, + [WCD9335_REG(WCD9335_MBHC_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MBHC_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MBHC_PLUG_DETECT_CTL)] = 1, + [WCD9335_REG(WCD9335_MBHC_ZDET_ANA_CTL)] = 1, + [WCD9335_REG(WCD9335_MBHC_ZDET_RAMP_CTL)] = 1, + [WCD9335_REG(WCD9335_MBHC_FSM_DEBUG)] = 1, + [WCD9335_REG(WCD9335_MBHC_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_VBADC_SUBBLOCK_EN)] = 1, + [WCD9335_REG(WCD9335_VBADC_IBIAS_FE)] = 1, + [WCD9335_REG(WCD9335_VBADC_BIAS_ADC)] = 1, + [WCD9335_REG(WCD9335_VBADC_FE_CTRL)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_REF)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_IO)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_SAR)] = 1, + [WCD9335_REG(WCD9335_VBADC_DEBUG)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_DOUTMSB)] = 1, + [WCD9335_REG(WCD9335_VBADC_ADC_DOUTLSB)] = 1, + [WCD9335_REG(WCD9335_LDOH_MODE)] = 1, + [WCD9335_REG(WCD9335_LDOH_BIAS)] = 1, + [WCD9335_REG(WCD9335_LDOH_STB_LOADS)] = 1, + [WCD9335_REG(WCD9335_LDOH_SLOWRAMP)] = 1, + [WCD9335_REG(WCD9335_MICB1_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB1_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB1_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_MICB2_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB2_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB2_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_MICB3_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB3_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB3_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_MICB4_TEST_CTL_1)] = 1, + [WCD9335_REG(WCD9335_MICB4_TEST_CTL_2)] = 1, + [WCD9335_REG(WCD9335_MICB4_TEST_CTL_3)] = 1, + [WCD9335_REG(WCD9335_TX_COM_ADC_VCM)] = 1, + [WCD9335_REG(WCD9335_TX_COM_BIAS_ATEST)] = 1, + [WCD9335_REG(WCD9335_TX_COM_ADC_INT1_IB)] = 1, + [WCD9335_REG(WCD9335_TX_COM_ADC_INT2_IB)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_START)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_STOP_9P6M)] = 1, + [WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_STOP_12P288M)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TEST_EN)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_ADC_IB)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_ATEST_REFCTL)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TEST_BLK_EN)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_TXFE_CLKDIV)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_SAR1_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_1_2_SAR2_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TEST_EN)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_ADC_IB)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_ATEST_REFCTL)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TEST_BLK_EN)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_TXFE_CLKDIV)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_SAR1_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_3_4_SAR2_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TEST_EN)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_ADC_IB)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_ATEST_REFCTL)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TEST_BLK_EN)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_TXFE_CLKDIV)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_SAR1_ERR)] = 1, + [WCD9335_REG(WCD9335_TX_5_6_SAR2_ERR)] = 1, + [WCD9335_REG(WCD9335_CLASSH_MODE_1)] = 1, + [WCD9335_REG(WCD9335_CLASSH_MODE_2)] = 1, + [WCD9335_REG(WCD9335_CLASSH_MODE_3)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_VCL_1)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_VCL_2)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_1)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_2)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_3)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_4)] = 1, + [WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_5)] = 1, + [WCD9335_REG(WCD9335_CLASSH_BUCK_TMUX_A_D)] = 1, + [WCD9335_REG(WCD9335_CLASSH_BUCK_SW_DRV_CNTL)] = 1, + [WCD9335_REG(WCD9335_CLASSH_SPARE)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_EN)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_1)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_2)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_3)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_4)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_5)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_6)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_7)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_8)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_9)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_1)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_2)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_3)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_4)] = 1, + [WCD9335_REG(WCD9335_FLYBACK_TEST_CTL)] = 1, + [WCD9335_REG(WCD9335_RX_AUX_SW_CTL)] = 1, + [WCD9335_REG(WCD9335_RX_PA_AUX_IN_CONN)] = 1, + [WCD9335_REG(WCD9335_RX_TIMER_DIV)] = 1, + [WCD9335_REG(WCD9335_RX_OCP_CTL)] = 1, + [WCD9335_REG(WCD9335_RX_OCP_COUNT)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_EAR_DAC)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_EAR_AMP)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_LDO)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_PA)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_RDAC_LDO)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_CNP1)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_HPH_LOWPOWER)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_PA)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_REF)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_LDO)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_SELO_DAC_PA)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_BUCK_RST)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_BUCK_VREF_ERRAMP)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_FLYB_ERRAMP)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_FLYB_BUFF)] = 1, + [WCD9335_REG(WCD9335_RX_BIAS_FLYB_MID_RST)] = 1, + [WCD9335_REG(WCD9335_HPH_L_STATUS)] = 1, + [WCD9335_REG(WCD9335_HPH_R_STATUS)] = 1, + [WCD9335_REG(WCD9335_HPH_CNP_EN)] = 1, + [WCD9335_REG(WCD9335_HPH_CNP_WG_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_CNP_WG_TIME)] = 1, + [WCD9335_REG(WCD9335_HPH_OCP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_AUTO_CHOP)] = 1, + [WCD9335_REG(WCD9335_HPH_CHOP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_PA_CTL1)] = 1, + [WCD9335_REG(WCD9335_HPH_PA_CTL2)] = 1, + [WCD9335_REG(WCD9335_HPH_L_EN)] = 1, + [WCD9335_REG(WCD9335_HPH_L_TEST)] = 1, + [WCD9335_REG(WCD9335_HPH_L_ATEST)] = 1, + [WCD9335_REG(WCD9335_HPH_R_EN)] = 1, + [WCD9335_REG(WCD9335_HPH_R_TEST)] = 1, + [WCD9335_REG(WCD9335_HPH_R_ATEST)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_CLK_CTL1)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_CLK_CTL2)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_LDO_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_REFBUFF_UHQA_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_REFBUFF_LP_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_L_DAC_CTL)] = 1, + [WCD9335_REG(WCD9335_HPH_R_DAC_CTL)] = 1, + [WCD9335_REG(WCD9335_EAR_EN_REG)] = 1, + [WCD9335_REG(WCD9335_EAR_CMBUFF)] = 1, + [WCD9335_REG(WCD9335_EAR_ICTL)] = 1, + [WCD9335_REG(WCD9335_EAR_EN_DBG_CTL)] = 1, + [WCD9335_REG(WCD9335_EAR_CNP)] = 1, + [WCD9335_REG(WCD9335_EAR_DAC_CTL_ATEST)] = 1, + [WCD9335_REG(WCD9335_EAR_STATUS_REG)] = 1, + [WCD9335_REG(WCD9335_EAR_OUT_SHORT)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_MISC)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO2_COMPANDER)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO1_COMPANDER)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_COMMON)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_BYPASS_EN)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_CNP)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_CORE_OUT_PROG)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LDO_OUT_PROG)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_COM_PA_FREQ)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_RESERVED_REG)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO1_STATUS_1)] = 1, + [WCD9335_REG(WCD9335_DIFF_LO_LO1_STATUS_2)] = 1, + [WCD9335_REG(WCD9335_SE_LO_COM1)] = 1, + [WCD9335_REG(WCD9335_SE_LO_COM2)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO3_GAIN)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO3_CTRL)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO4_GAIN)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO4_CTRL)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO3_STATUS)] = 1, + [WCD9335_REG(WCD9335_SE_LO_LO4_STATUS)] = 1, +}; + +const u8 wcd9335_page10_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE10_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_CLK_RESET_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_MODE_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_MODE_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FF_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FB_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_LPF_FF_A_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_LPF_FF_B_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_LPF_FB_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_SMLPF_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_IIR_ADAPT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_IIR_COEFF_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_IIR_COEFF_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FF_A_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FF_B_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC0_FB_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_CLK_RESET_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_MODE_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_MODE_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FF_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FB_SHIFT)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_LPF_FF_A_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_LPF_FF_B_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_LPF_FB_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_SMLPF_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_IIR_ADAPT_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_IIR_COEFF_1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_IIR_COEFF_2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FF_A_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FF_B_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_ANC1_FB_GAIN_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_192_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_192_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0)] = 1, +}; + +const u8 wcd9335_page11_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE11_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL0)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL1)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL2)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL3)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL4)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL5)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL6)] = 1, + [WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC4)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_VOL_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_VOL_MIX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC5)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC6)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC7)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_SEC0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_SEC1)] = 1, +}; + +const u8 wcd9335_page12_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE12_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_CRC)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_DLY_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_DECAY_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_HPH_V_PA)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_EAR_V_PA)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_HPH_V_HD)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_EAR_V_HD)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K1_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K1_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K2_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_K2_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_IDLE_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_IDLE_HPH)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_IDLE_EAR)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_TEST0)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_TEST1)] = 1, + [WCD9335_REG(WCD9335_CDC_CLSH_OVR_VREF)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CFG2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_0)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_1)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_2)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_3)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG)] = 1, + [WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_CFG)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_RF_PROC1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_RF_PROC2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC4)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD2)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD3)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD4)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_DEBUG1)] = 1, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON)] = 0, + [WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC0_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC0_STATUS)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC1_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC1_STATUS)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC2_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC2_STATUS)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC3_CLK_RST_CTL_0)] = 1, + [WCD9335_REG(WCD9335_SPLINE_SRC3_STATUS)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1)] = 1, +}; + +const u8 wcd9335_page13_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE13_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_ANC_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL)] = 1, + [WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_STATUS)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD)] = 1, + [WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG0)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG1)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG2)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG3)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG4)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG5)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG6)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG7)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_RD_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_RD_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_LUT)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB)] = 1, + [WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB)] = 1, +}; + +const u8 wcd9335_page_0x80_reg_readable[WCD9335_PAGE_SIZE] = { + [WCD9335_REG(WCD9335_PAGE80_PAGE_REGISTER)] = 1, + [WCD9335_REG(WCD9335_TLMM_BIST_MODE_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_RF_PA_ON_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_INTR1_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_INTR2_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_SWR_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_SWR_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_SLIMBUS_DATA2_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2C_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2C_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_SD0_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_SD1_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_SCK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_RX_WS_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_SD0_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_SD1_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_SCK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_I2S_TX_WS_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC1_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC1_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC2_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC2_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC3_CLK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_DMIC3_DATA_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTDI_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTDO_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTMS_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTCK_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TLMM_JTRST_PINCFG)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_0)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_3)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_0)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_3)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PAD_DRVCTL)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_PIN_STATUS)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_NPL_DLY_TEST_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_NPL_DLY_TEST_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_MEM_CTRL)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_BUS_SEL)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_JTAG)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_1)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_2)] = 1, + [WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_3)] = 1, +}; + +const u8 *wcd9335_reg[WCD9335_NUM_PAGES] = { + [PAGE_0] = wcd9335_page0_reg_readable, + [PAGE_1] = wcd9335_page1_reg_readable, + [PAGE_2] = wcd9335_page2_reg_readable, + [PAGE_6] = wcd9335_page6_reg_readable, + [PAGE_10] = wcd9335_page10_reg_readable, + [PAGE_11] = wcd9335_page11_reg_readable, + [PAGE_12] = wcd9335_page12_reg_readable, + [PAGE_13] = wcd9335_page13_reg_readable, + [PAGE_0X80] = wcd9335_page_0x80_reg_readable, +}; diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c new file mode 100644 index 000000000000..27249eeec013 --- /dev/null +++ b/drivers/mfd/wcd934x-regmap.c @@ -0,0 +1,1957 @@ +/* + * 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 "wcd9xxx-regmap.h" + + +static const struct reg_sequence wcd934x_1_1_defaults[] = { + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x01 }, + { WCD934X_BIAS_VBG_FINE_ADJ, 0x75 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0E }, + { WCD934X_EAR_DAC_CTL_ATEST, 0x08 }, + { WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x17 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x81 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x81 }, +}; + +static const struct reg_default wcd934x_defaults[] = { + { WCD934X_PAGE0_PAGE_REGISTER, 0x00 }, + { WCD934X_CODEC_RPM_CLK_BYPASS, 0x00 }, + { WCD934X_CODEC_RPM_CLK_GATE, 0x1f }, + { WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x00 }, + { WCD934X_CODEC_RPM_CLK_MCLK2_CFG, 0x02 }, + { WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL, 0x00 }, + { WCD934X_CODEC_RPM_RST_CTL, 0x00 }, + { WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x04 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2, 0x08 }, + { WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3, 0x01 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x10 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO, 0x0d }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL, 0xcc }, + { WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL, 0xcc }, + { WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE, 0x00 }, + { WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA, 0x00 }, + { WCD934X_DATA_HUB_RX0_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX1_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX2_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX3_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX4_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX5_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX6_CFG, 0x00 }, + { WCD934X_DATA_HUB_RX7_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX0_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX1_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX2_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX3_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX4_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX5_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX6_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX7_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX8_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX9_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX10_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX14_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_SB_TX15_INP_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_TX0_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_TX1_0_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_TX1_1_CFG, 0x00 }, + { WCD934X_DATA_HUB_I2S_0_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_1_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_2_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_3_CTL, 0x0c }, + { WCD934X_DATA_HUB_I2S_CLKSRC_CTL, 0x00 }, + { WCD934X_DATA_HUB_I2S_COMMON_CTL, 0x00 }, + { WCD934X_DATA_HUB_I2S_0_TDM_CTL, 0x00 }, + { WCD934X_DATA_HUB_I2S_STATUS, 0x00 }, + { WCD934X_DMA_RDMA_CTL_0, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_0, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_0, 0xff }, + { WCD934X_DMA_RDMA_CTL_1, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_1, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_1, 0xff }, + { WCD934X_DMA_RDMA_CTL_2, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_2, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_2, 0xff }, + { WCD934X_DMA_RDMA_CTL_3, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_3, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_3, 0xff }, + { WCD934X_DMA_RDMA_CTL_4, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_RDMA_4, 0xff }, + { WCD934X_DMA_CH_0_1_CFG_RDMA_4, 0xff }, + { WCD934X_DMA_RDMA4_PRT_CFG, 0x00 }, + { WCD934X_DMA_RDMA_SBTX0_7_CFG, 0x00 }, + { WCD934X_DMA_RDMA_SBTX8_11_CFG, 0x00 }, + { WCD934X_DMA_WDMA_CTL_0, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_0, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_0, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_0, 0x00 }, + { WCD934X_DMA_WDMA_CTL_1, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_1, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_1, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_1, 0x00 }, + { WCD934X_DMA_WDMA_CTL_2, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_2, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_2, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_2, 0x00 }, + { WCD934X_DMA_WDMA_CTL_3, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_3, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_3, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_3, 0x00 }, + { WCD934X_DMA_WDMA_CTL_4, 0x00 }, + { WCD934X_DMA_CH_4_5_CFG_WDMA_4, 0x00 }, + { WCD934X_DMA_CH_2_3_CFG_WDMA_4, 0x00 }, + { WCD934X_DMA_CH_0_1_CFG_WDMA_4, 0x00 }, + { WCD934X_DMA_WDMA0_PRT_CFG, 0x00 }, + { WCD934X_DMA_WDMA3_PRT_CFG, 0x00 }, + { WCD934X_DMA_WDMA4_PRT0_3_CFG, 0x00 }, + { WCD934X_DMA_WDMA4_PRT4_7_CFG, 0x00 }, + { WCD934X_PAGE1_PAGE_REGISTER, 0x00 }, + { WCD934X_CPE_FLL_USER_CTL_0, 0x71 }, + { WCD934X_CPE_FLL_USER_CTL_1, 0x34 }, + { WCD934X_CPE_FLL_USER_CTL_2, 0x0b }, + { WCD934X_CPE_FLL_USER_CTL_3, 0x02 }, + { WCD934X_CPE_FLL_USER_CTL_4, 0x04 }, + { WCD934X_CPE_FLL_USER_CTL_5, 0x02 }, + { WCD934X_CPE_FLL_USER_CTL_6, 0x6e }, + { WCD934X_CPE_FLL_USER_CTL_7, 0x00 }, + { WCD934X_CPE_FLL_USER_CTL_8, 0x94 }, + { WCD934X_CPE_FLL_USER_CTL_9, 0x50 }, + { WCD934X_CPE_FLL_L_VAL_CTL_0, 0x53 }, + { WCD934X_CPE_FLL_L_VAL_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD934X_CPE_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD934X_CPE_FLL_CONFIG_CTL_0, 0x6b }, + { WCD934X_CPE_FLL_CONFIG_CTL_1, 0x05 }, + { WCD934X_CPE_FLL_CONFIG_CTL_2, 0x08 }, + { WCD934X_CPE_FLL_CONFIG_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_CONFIG_CTL_4, 0x10 }, + { WCD934X_CPE_FLL_TEST_CTL_0, 0x80 }, + { WCD934X_CPE_FLL_TEST_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_2, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_4, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_5, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_6, 0x00 }, + { WCD934X_CPE_FLL_TEST_CTL_7, 0x33 }, + { WCD934X_CPE_FLL_FREQ_CTL_0, 0x00 }, + { WCD934X_CPE_FLL_FREQ_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_FREQ_CTL_2, 0x00 }, + { WCD934X_CPE_FLL_FREQ_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_0, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_1, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_2, 0x00 }, + { WCD934X_CPE_FLL_SSC_CTL_3, 0x00 }, + { WCD934X_CPE_FLL_FLL_MODE, 0x20 }, + { WCD934X_CPE_FLL_STATUS_0, 0x00 }, + { WCD934X_CPE_FLL_STATUS_1, 0x00 }, + { WCD934X_CPE_FLL_STATUS_2, 0x00 }, + { WCD934X_CPE_FLL_STATUS_3, 0x00 }, + { WCD934X_I2S_FLL_USER_CTL_0, 0x41 }, + { WCD934X_I2S_FLL_USER_CTL_1, 0x94 }, + { WCD934X_I2S_FLL_USER_CTL_2, 0x08 }, + { WCD934X_I2S_FLL_USER_CTL_3, 0x02 }, + { WCD934X_I2S_FLL_USER_CTL_4, 0x04 }, + { WCD934X_I2S_FLL_USER_CTL_5, 0x02 }, + { WCD934X_I2S_FLL_USER_CTL_6, 0x40 }, + { WCD934X_I2S_FLL_USER_CTL_7, 0x00 }, + { WCD934X_I2S_FLL_USER_CTL_8, 0x5f }, + { WCD934X_I2S_FLL_USER_CTL_9, 0x02 }, + { WCD934X_I2S_FLL_L_VAL_CTL_0, 0x40 }, + { WCD934X_I2S_FLL_L_VAL_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD934X_I2S_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD934X_I2S_FLL_CONFIG_CTL_0, 0x6b }, + { WCD934X_I2S_FLL_CONFIG_CTL_1, 0x05 }, + { WCD934X_I2S_FLL_CONFIG_CTL_2, 0x08 }, + { WCD934X_I2S_FLL_CONFIG_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_CONFIG_CTL_4, 0x30 }, + { WCD934X_I2S_FLL_TEST_CTL_0, 0x80 }, + { WCD934X_I2S_FLL_TEST_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_2, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_4, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_5, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_6, 0x00 }, + { WCD934X_I2S_FLL_TEST_CTL_7, 0xff }, + { WCD934X_I2S_FLL_FREQ_CTL_0, 0x00 }, + { WCD934X_I2S_FLL_FREQ_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_FREQ_CTL_2, 0x00 }, + { WCD934X_I2S_FLL_FREQ_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_0, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_1, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_2, 0x00 }, + { WCD934X_I2S_FLL_SSC_CTL_3, 0x00 }, + { WCD934X_I2S_FLL_FLL_MODE, 0x00 }, + { WCD934X_I2S_FLL_STATUS_0, 0x00 }, + { WCD934X_I2S_FLL_STATUS_1, 0x00 }, + { WCD934X_I2S_FLL_STATUS_2, 0x00 }, + { WCD934X_I2S_FLL_STATUS_3, 0x00 }, + { WCD934X_SB_FLL_USER_CTL_0, 0x41 }, + { WCD934X_SB_FLL_USER_CTL_1, 0x94 }, + { WCD934X_SB_FLL_USER_CTL_2, 0x08 }, + { WCD934X_SB_FLL_USER_CTL_3, 0x02 }, + { WCD934X_SB_FLL_USER_CTL_4, 0x04 }, + { WCD934X_SB_FLL_USER_CTL_5, 0x02 }, + { WCD934X_SB_FLL_USER_CTL_6, 0x40 }, + { WCD934X_SB_FLL_USER_CTL_7, 0x00 }, + { WCD934X_SB_FLL_USER_CTL_8, 0x5e }, + { WCD934X_SB_FLL_USER_CTL_9, 0x01 }, + { WCD934X_SB_FLL_L_VAL_CTL_0, 0x40 }, + { WCD934X_SB_FLL_L_VAL_CTL_1, 0x00 }, + { WCD934X_SB_FLL_DSM_FRAC_CTL_0, 0x00 }, + { WCD934X_SB_FLL_DSM_FRAC_CTL_1, 0xff }, + { WCD934X_SB_FLL_CONFIG_CTL_0, 0x6b }, + { WCD934X_SB_FLL_CONFIG_CTL_1, 0x05 }, + { WCD934X_SB_FLL_CONFIG_CTL_2, 0x08 }, + { WCD934X_SB_FLL_CONFIG_CTL_3, 0x00 }, + { WCD934X_SB_FLL_CONFIG_CTL_4, 0x10 }, + { WCD934X_SB_FLL_TEST_CTL_0, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_1, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_2, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_3, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_4, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_5, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_6, 0x00 }, + { WCD934X_SB_FLL_TEST_CTL_7, 0xff }, + { WCD934X_SB_FLL_FREQ_CTL_0, 0x00 }, + { WCD934X_SB_FLL_FREQ_CTL_1, 0x00 }, + { WCD934X_SB_FLL_FREQ_CTL_2, 0x00 }, + { WCD934X_SB_FLL_FREQ_CTL_3, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_0, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_1, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_2, 0x00 }, + { WCD934X_SB_FLL_SSC_CTL_3, 0x00 }, + { WCD934X_SB_FLL_FLL_MODE, 0x00 }, + { WCD934X_SB_FLL_STATUS_0, 0x00 }, + { WCD934X_SB_FLL_STATUS_1, 0x00 }, + { WCD934X_SB_FLL_STATUS_2, 0x00 }, + { WCD934X_SB_FLL_STATUS_3, 0x00 }, + { WCD934X_PAGE2_PAGE_REGISTER, 0x00 }, + { WCD934X_CPE_SS_CPE_CTL, 0x05 }, + { WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, 0x01 }, + { WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1, 0x00 }, + { WCD934X_CPE_SS_PWR_CPEFLL_CTL, 0x02 }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0f }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE, 0x00 }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5, 0xff }, + { WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, 0x07 }, + { WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, 0x00 }, + { WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL, 0x20 }, + { WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1, 0x00 }, + { WCD934X_CPE_SS_US_BUF_INT_PERIOD, 0x60 }, + { WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x13 }, + { WCD934X_CPE_SS_SVA_CFG, 0x41 }, + { WCD934X_CPE_SS_US_CFG, 0x00 }, + { WCD934X_CPE_SS_MAD_CTL, 0x00 }, + { WCD934X_CPE_SS_CPAR_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC0_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC1_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC2_CTL, 0x00 }, + { WCD934X_CPE_SS_DMIC_CFG, 0x80 }, + { WCD934X_CPE_SS_CPAR_CFG, 0x00 }, + { WCD934X_CPE_SS_WDOG_CFG, 0x01 }, + { WCD934X_CPE_SS_BACKUP_INT, 0x00 }, + { WCD934X_CPE_SS_STATUS, 0x00 }, + { WCD934X_CPE_SS_CPE_OCD_CFG, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xff }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0x3f }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A, 0xff }, + { WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B, 0x3f }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A, 0x00 }, + { WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B, 0x00 }, + { WCD934X_SOC_MAD_MAIN_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_MAIN_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_3, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_4, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_5, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_6, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_7, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_CTL_8, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR, 0x00 }, + { WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL, 0x40 }, + { WCD934X_SOC_MAD_ULTR_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_3, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_4, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_5, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_6, 0x00 }, + { WCD934X_SOC_MAD_ULTR_CTL_7, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_1, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_2, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_3, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_4, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_5, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_6, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_7, 0x00 }, + { WCD934X_SOC_MAD_BEACON_CTL_8, 0x00 }, + { WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR, 0x00 }, + { WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL, 0x00 }, + { WCD934X_SOC_MAD_INP_SEL, 0x00 }, + { WCD934X_PAGE4_PAGE_REGISTER, 0x00 }, + { WCD934X_INTR_CFG, 0x00 }, + { WCD934X_INTR_CLR_COMMIT, 0x00 }, + { WCD934X_INTR_PIN1_MASK0, 0xff }, + { WCD934X_INTR_PIN1_MASK1, 0xff }, + { WCD934X_INTR_PIN1_MASK2, 0xff }, + { WCD934X_INTR_PIN1_MASK3, 0xff }, + { WCD934X_INTR_PIN1_STATUS0, 0x00 }, + { WCD934X_INTR_PIN1_STATUS1, 0x00 }, + { WCD934X_INTR_PIN1_STATUS2, 0x00 }, + { WCD934X_INTR_PIN1_STATUS3, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR0, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR1, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR2, 0x00 }, + { WCD934X_INTR_PIN1_CLEAR3, 0x00 }, + { WCD934X_INTR_PIN2_MASK3, 0xff }, + { WCD934X_INTR_PIN2_STATUS3, 0x00 }, + { WCD934X_INTR_PIN2_CLEAR3, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_MASK2, 0xff }, + { WCD934X_INTR_CPESS_SUMRY_MASK3, 0xff }, + { WCD934X_INTR_CPESS_SUMRY_STATUS2, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_STATUS3, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_CLEAR2, 0x00 }, + { WCD934X_INTR_CPESS_SUMRY_CLEAR3, 0x00 }, + { WCD934X_INTR_LEVEL0, 0x03 }, + { WCD934X_INTR_LEVEL1, 0xe0 }, + { WCD934X_INTR_LEVEL2, 0x94 }, + { WCD934X_INTR_LEVEL3, 0x80 }, + { WCD934X_INTR_BYPASS0, 0x00 }, + { WCD934X_INTR_BYPASS1, 0x00 }, + { WCD934X_INTR_BYPASS2, 0x00 }, + { WCD934X_INTR_BYPASS3, 0x00 }, + { WCD934X_INTR_SET0, 0x00 }, + { WCD934X_INTR_SET1, 0x00 }, + { WCD934X_INTR_SET2, 0x00 }, + { WCD934X_INTR_SET3, 0x00 }, + { WCD934X_INTR_CODEC_MISC_MASK, 0x7f }, + { WCD934X_INTR_CODEC_MISC_STATUS, 0x00 }, + { WCD934X_INTR_CODEC_MISC_CLEAR, 0x00 }, + { WCD934X_PAGE5_PAGE_REGISTER, 0x00 }, + { WCD934X_SLNQ_DIG_DEVICE, 0x49 }, + { WCD934X_SLNQ_DIG_REVISION, 0x01 }, + { WCD934X_SLNQ_DIG_H_COMMAND, 0x00 }, + { WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB, 0x40 }, + { WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB, 0x40 }, + { WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB, 0x40 }, + { WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_COMM_CTL, 0x00 }, + { WCD934X_SLNQ_DIG_FRAME_CTRL, 0x01 }, + { WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2, 0x77 }, + { WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4, 0x77 }, + { WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5, 0x70 }, + { WCD934X_SLNQ_DIG_SW_EVENT_RD, 0x00 }, + { WCD934X_SLNQ_DIG_SW_EVENT_CTRL, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_SELECT_1, 0x12 }, + { WCD934X_SLNQ_DIG_PDM_SELECT_2, 0x34 }, + { WCD934X_SLNQ_DIG_PDM_SELECT_3, 0x55 }, + { WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ, 0x01 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL, 0x11 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB, 0x00 }, + { WCD934X_SLNQ_DIG_RAM_CNTRL, 0x01 }, + { WCD934X_SLNQ_DIG_SRAM_BANK, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_0, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_4, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_5, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_6, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_7, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_8, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_9, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_F, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_10, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_11, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_12, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_13, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_14, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_15, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_16, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_17, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_18, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_19, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_1F, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_20, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_21, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_22, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_23, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_24, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_25, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_26, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_27, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_28, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_29, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_2F, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_30, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_31, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_32, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_33, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_34, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_35, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_36, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_37, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_38, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_39, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3A, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3B, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3C, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3D, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3E, 0x00 }, + { WCD934X_SLNQ_DIG_SRAM_BYTE_3F, 0x00 }, + { WCD934X_SLNQ_DIG_TOP_CTRL1, 0x00 }, + { WCD934X_SLNQ_DIG_TOP_CTRL2, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_CTRL, 0x00 }, + { WCD934X_SLNQ_DIG_PDM_MUTE_CTRL, 0x20 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL, 0x00 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS, 0x00 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_FS, 0x00 }, + { WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL, 0x00 }, + { WCD934X_SLNQ_DIG_GPOUT_ENABLE, 0x00 }, + { WCD934X_SLNQ_DIG_GPOUT_VAL, 0x00 }, + { WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK, 0x00 }, + { WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS, 0x00 }, + { WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR, 0x00 }, + { WCD934X_SLNQ_DIG_IP_TESTING, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNTRL, 0x0f }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNT, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK0, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK1, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK2, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK3, 0xff }, + { WCD934X_SLNQ_DIG_INTERRUPT_MASK4, 0x1f }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS0, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS1, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS2, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS3, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_STATUS4, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR0, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR1, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR2, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR3, 0x00 }, + { WCD934X_SLNQ_DIG_INTERRUPT_CLR4, 0x00 }, + { WCD934X_ANA_PAGE_REGISTER, 0x00 }, + { WCD934X_ANA_BIAS, 0x00 }, + { WCD934X_ANA_RCO, 0x00 }, + { WCD934X_ANA_PAGE6_SPARE2, 0x00 }, + { WCD934X_ANA_PAGE6_SPARE3, 0x00 }, + { WCD934X_ANA_BUCK_CTL, 0x00 }, + { WCD934X_ANA_BUCK_STATUS, 0x00 }, + { WCD934X_ANA_RX_SUPPLIES, 0x00 }, + { WCD934X_ANA_HPH, 0x0c }, + { WCD934X_ANA_EAR, 0x00 }, + { WCD934X_ANA_LO_1_2, 0x3c }, + { WCD934X_ANA_MAD_SETUP, 0x01 }, + { WCD934X_ANA_AMIC1, 0x20 }, + { WCD934X_ANA_AMIC2, 0x00 }, + { WCD934X_ANA_AMIC3, 0x20 }, + { WCD934X_ANA_AMIC4, 0x00 }, + { WCD934X_ANA_MBHC_MECH, 0x39 }, + { WCD934X_ANA_MBHC_ELECT, 0x08 }, + { WCD934X_ANA_MBHC_ZDET, 0x00 }, + { WCD934X_ANA_MBHC_RESULT_1, 0x00 }, + { WCD934X_ANA_MBHC_RESULT_2, 0x00 }, + { WCD934X_ANA_MBHC_RESULT_3, 0x00 }, + { WCD934X_ANA_MBHC_BTN0, 0x00 }, + { WCD934X_ANA_MBHC_BTN1, 0x10 }, + { WCD934X_ANA_MBHC_BTN2, 0x20 }, + { WCD934X_ANA_MBHC_BTN3, 0x30 }, + { WCD934X_ANA_MBHC_BTN4, 0x40 }, + { WCD934X_ANA_MBHC_BTN5, 0x50 }, + { WCD934X_ANA_MBHC_BTN6, 0x60 }, + { WCD934X_ANA_MBHC_BTN7, 0x70 }, + { WCD934X_ANA_MICB1, 0x10 }, + { WCD934X_ANA_MICB2, 0x10 }, + { WCD934X_ANA_MICB2_RAMP, 0x00 }, + { WCD934X_ANA_MICB3, 0x10 }, + { WCD934X_ANA_MICB4, 0x10 }, + { WCD934X_ANA_VBADC, 0x00 }, + { WCD934X_BIAS_CTL, 0x28 }, + { WCD934X_BIAS_VBG_FINE_ADJ, 0x65 }, + { WCD934X_RCO_CTRL_1, 0x44 }, + { WCD934X_RCO_CTRL_2, 0x48 }, + { WCD934X_RCO_CAL, 0x00 }, + { WCD934X_RCO_CAL_1, 0x00 }, + { WCD934X_RCO_CAL_2, 0x00 }, + { WCD934X_RCO_TEST_CTRL, 0x00 }, + { WCD934X_RCO_CAL_OUT_1, 0x00 }, + { WCD934X_RCO_CAL_OUT_2, 0x00 }, + { WCD934X_RCO_CAL_OUT_3, 0x00 }, + { WCD934X_RCO_CAL_OUT_4, 0x00 }, + { WCD934X_RCO_CAL_OUT_5, 0x00 }, + { WCD934X_SIDO_MODE_1, 0x84 }, + { WCD934X_SIDO_MODE_2, 0xfe }, + { WCD934X_SIDO_MODE_3, 0xf6 }, + { WCD934X_SIDO_MODE_4, 0x56 }, + { WCD934X_SIDO_VCL_1, 0x00 }, + { WCD934X_SIDO_VCL_2, 0x6c }, + { WCD934X_SIDO_VCL_3, 0x44 }, + { WCD934X_SIDO_CCL_1, 0x57 }, + { WCD934X_SIDO_CCL_2, 0x92 }, + { WCD934X_SIDO_CCL_3, 0x35 }, + { WCD934X_SIDO_CCL_4, 0x61 }, + { WCD934X_SIDO_CCL_5, 0x6d }, + { WCD934X_SIDO_CCL_6, 0x60 }, + { WCD934X_SIDO_CCL_7, 0x6f }, + { WCD934X_SIDO_CCL_8, 0x6f }, + { WCD934X_SIDO_CCL_9, 0x6e }, + { WCD934X_SIDO_CCL_10, 0x26 }, + { WCD934X_SIDO_FILTER_1, 0x92 }, + { WCD934X_SIDO_FILTER_2, 0x54 }, + { WCD934X_SIDO_DRIVER_1, 0x77 }, + { WCD934X_SIDO_DRIVER_2, 0x55 }, + { WCD934X_SIDO_DRIVER_3, 0x55 }, + { WCD934X_SIDO_CAL_CODE_EXT_1, 0x9c }, + { WCD934X_SIDO_CAL_CODE_EXT_2, 0x82 }, + { WCD934X_SIDO_CAL_CODE_OUT_1, 0x00 }, + { WCD934X_SIDO_CAL_CODE_OUT_2, 0x00 }, + { WCD934X_SIDO_TEST_1, 0x00 }, + { WCD934X_SIDO_TEST_2, 0x00 }, + { WCD934X_MBHC_CTL_CLK, 0x30 }, + { WCD934X_MBHC_CTL_ANA, 0x00 }, + { WCD934X_MBHC_CTL_SPARE_1, 0x00 }, + { WCD934X_MBHC_CTL_SPARE_2, 0x00 }, + { WCD934X_MBHC_CTL_BCS, 0x00 }, + { WCD934X_MBHC_STATUS_SPARE_1, 0x00 }, + { WCD934X_MBHC_TEST_CTL, 0x00 }, + { WCD934X_VBADC_SUBBLOCK_EN, 0xde }, + { WCD934X_VBADC_IBIAS_FE, 0x58 }, + { WCD934X_VBADC_BIAS_ADC, 0x51 }, + { WCD934X_VBADC_FE_CTRL, 0x1c }, + { WCD934X_VBADC_ADC_REF, 0x20 }, + { WCD934X_VBADC_ADC_IO, 0x80 }, + { WCD934X_VBADC_ADC_SAR, 0xff }, + { WCD934X_VBADC_DEBUG, 0x00 }, + { WCD934X_LDOH_MODE, 0x2b }, + { WCD934X_LDOH_BIAS, 0x68 }, + { WCD934X_LDOH_STB_LOADS, 0x00 }, + { WCD934X_LDOH_SLOWRAMP, 0x50 }, + { WCD934X_MICB1_TEST_CTL_1, 0x1a }, + { WCD934X_MICB1_TEST_CTL_2, 0x18 }, + { WCD934X_MICB1_TEST_CTL_3, 0xa4 }, + { WCD934X_MICB2_TEST_CTL_1, 0x1a }, + { WCD934X_MICB2_TEST_CTL_2, 0x18 }, + { WCD934X_MICB2_TEST_CTL_3, 0xa4 }, + { WCD934X_MICB3_TEST_CTL_1, 0x1a }, + { WCD934X_MICB3_TEST_CTL_2, 0x18 }, + { WCD934X_MICB3_TEST_CTL_3, 0xa4 }, + { WCD934X_MICB4_TEST_CTL_1, 0x1a }, + { WCD934X_MICB4_TEST_CTL_2, 0x18 }, + { WCD934X_MICB4_TEST_CTL_3, 0xa4 }, + { WCD934X_TX_COM_ADC_VCM, 0x39 }, + { WCD934X_TX_COM_BIAS_ATEST, 0xc0 }, + { WCD934X_TX_COM_ADC_INT1_IB, 0x6f }, + { WCD934X_TX_COM_ADC_INT2_IB, 0x4f }, + { WCD934X_TX_COM_TXFE_DIV_CTL, 0x2e }, + { WCD934X_TX_COM_TXFE_DIV_START, 0x00 }, + { WCD934X_TX_COM_TXFE_DIV_STOP_9P6M, 0xc7 }, + { WCD934X_TX_COM_TXFE_DIV_STOP_12P288M, 0xff }, + { WCD934X_TX_1_2_TEST_EN, 0xcc }, + { WCD934X_TX_1_2_ADC_IB, 0x09 }, + { WCD934X_TX_1_2_ATEST_REFCTL, 0x0a }, + { WCD934X_TX_1_2_TEST_CTL, 0x38 }, + { WCD934X_TX_1_2_TEST_BLK_EN, 0xff }, + { WCD934X_TX_1_2_TXFE_CLKDIV, 0x00 }, + { WCD934X_TX_1_2_SAR1_ERR, 0x00 }, + { WCD934X_TX_1_2_SAR2_ERR, 0x00 }, + { WCD934X_TX_3_4_TEST_EN, 0xcc }, + { WCD934X_TX_3_4_ADC_IB, 0x09 }, + { WCD934X_TX_3_4_ATEST_REFCTL, 0x0a }, + { WCD934X_TX_3_4_TEST_CTL, 0x38 }, + { WCD934X_TX_3_4_TEST_BLK_EN, 0xff }, + { WCD934X_TX_3_4_TXFE_CLKDIV, 0x00 }, + { WCD934X_TX_3_4_SAR1_ERR, 0x00 }, + { WCD934X_TX_3_4_SAR2_ERR, 0x00 }, + { WCD934X_CLASSH_MODE_1, 0x40 }, + { WCD934X_CLASSH_MODE_2, 0x3a }, + { WCD934X_CLASSH_MODE_3, 0x00 }, + { WCD934X_CLASSH_CTRL_VCL_1, 0x70 }, + { WCD934X_CLASSH_CTRL_VCL_2, 0x82 }, + { WCD934X_CLASSH_CTRL_CCL_1, 0x31 }, + { WCD934X_CLASSH_CTRL_CCL_2, 0x80 }, + { WCD934X_CLASSH_CTRL_CCL_3, 0x80 }, + { WCD934X_CLASSH_CTRL_CCL_4, 0x51 }, + { WCD934X_CLASSH_CTRL_CCL_5, 0x00 }, + { WCD934X_CLASSH_BUCK_TMUX_A_D, 0x00 }, + { WCD934X_CLASSH_BUCK_SW_DRV_CNTL, 0x77 }, + { WCD934X_CLASSH_SPARE, 0x00 }, + { WCD934X_FLYBACK_EN, 0x4e }, + { WCD934X_FLYBACK_VNEG_CTRL_1, 0x0b }, + { WCD934X_FLYBACK_VNEG_CTRL_2, 0x45 }, + { WCD934X_FLYBACK_VNEG_CTRL_3, 0x74 }, + { WCD934X_FLYBACK_VNEG_CTRL_4, 0x7f }, + { WCD934X_FLYBACK_VNEG_CTRL_5, 0x83 }, + { WCD934X_FLYBACK_VNEG_CTRL_6, 0x98 }, + { WCD934X_FLYBACK_VNEG_CTRL_7, 0xa9 }, + { WCD934X_FLYBACK_VNEG_CTRL_8, 0x68 }, + { WCD934X_FLYBACK_VNEG_CTRL_9, 0x64 }, + { WCD934X_FLYBACK_VNEGDAC_CTRL_1, 0xed }, + { WCD934X_FLYBACK_VNEGDAC_CTRL_2, 0xf0 }, + { WCD934X_FLYBACK_VNEGDAC_CTRL_3, 0xa6 }, + { WCD934X_FLYBACK_CTRL_1, 0x65 }, + { WCD934X_FLYBACK_TEST_CTL, 0x00 }, + { WCD934X_RX_AUX_SW_CTL, 0x00 }, + { WCD934X_RX_PA_AUX_IN_CONN, 0x00 }, + { WCD934X_RX_TIMER_DIV, 0x32 }, + { WCD934X_RX_OCP_CTL, 0x1f }, + { WCD934X_RX_OCP_COUNT, 0x77 }, + { WCD934X_RX_BIAS_EAR_DAC, 0xa0 }, + { WCD934X_RX_BIAS_EAR_AMP, 0xaa }, + { WCD934X_RX_BIAS_HPH_LDO, 0xa9 }, + { WCD934X_RX_BIAS_HPH_PA, 0xaa }, + { WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8a }, + { WCD934X_RX_BIAS_HPH_RDAC_LDO, 0x88 }, + { WCD934X_RX_BIAS_HPH_CNP1, 0x82 }, + { WCD934X_RX_BIAS_HPH_LOWPOWER, 0x82 }, + { WCD934X_RX_BIAS_DIFFLO_PA, 0x80 }, + { WCD934X_RX_BIAS_DIFFLO_REF, 0x88 }, + { WCD934X_RX_BIAS_DIFFLO_LDO, 0x88 }, + { WCD934X_RX_BIAS_SELO_DAC_PA, 0xa8 }, + { WCD934X_RX_BIAS_BUCK_RST, 0x08 }, + { WCD934X_RX_BIAS_BUCK_VREF_ERRAMP, 0x44 }, + { WCD934X_RX_BIAS_FLYB_ERRAMP, 0x40 }, + { WCD934X_RX_BIAS_FLYB_BUFF, 0xaa }, + { WCD934X_RX_BIAS_FLYB_MID_RST, 0x14 }, + { WCD934X_HPH_L_STATUS, 0x04 }, + { WCD934X_HPH_R_STATUS, 0x04 }, + { WCD934X_HPH_CNP_EN, 0x80 }, + { WCD934X_HPH_CNP_WG_CTL, 0x9a }, + { WCD934X_HPH_CNP_WG_TIME, 0x14 }, + { WCD934X_HPH_OCP_CTL, 0x28 }, + { WCD934X_HPH_AUTO_CHOP, 0x16 }, + { WCD934X_HPH_CHOP_CTL, 0x83 }, + { WCD934X_HPH_PA_CTL1, 0x46 }, + { WCD934X_HPH_PA_CTL2, 0x50 }, + { WCD934X_HPH_L_EN, 0x80 }, + { WCD934X_HPH_L_TEST, 0xe0 }, + { WCD934X_HPH_L_ATEST, 0x50 }, + { WCD934X_HPH_R_EN, 0x80 }, + { WCD934X_HPH_R_TEST, 0xe0 }, + { WCD934X_HPH_R_ATEST, 0x54 }, + { WCD934X_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD934X_HPH_RDAC_CLK_CTL2, 0x9b }, + { WCD934X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD934X_HPH_REFBUFF_UHQA_CTL, 0xa8 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0a }, + { WCD934X_HPH_L_DAC_CTL, 0x00 }, + { WCD934X_HPH_R_DAC_CTL, 0x00 }, + { WCD934X_EAR_EN_REG, 0x60 }, + { WCD934X_EAR_CMBUFF, 0x05 }, + { WCD934X_EAR_ICTL, 0x40 }, + { WCD934X_EAR_EN_DBG_CTL, 0x00 }, + { WCD934X_EAR_CNP, 0xe0 }, + { WCD934X_EAR_DAC_CTL_ATEST, 0x00 }, + { WCD934X_EAR_STATUS_REG, 0x04 }, + { WCD934X_EAR_EAR_MISC, 0x28 }, + { WCD934X_DIFF_LO_MISC, 0x03 }, + { WCD934X_DIFF_LO_LO2_COMPANDER, 0x00 }, + { WCD934X_DIFF_LO_LO1_COMPANDER, 0x00 }, + { WCD934X_DIFF_LO_COMMON, 0x40 }, + { WCD934X_DIFF_LO_BYPASS_EN, 0x00 }, + { WCD934X_DIFF_LO_CNP, 0x20 }, + { WCD934X_DIFF_LO_CORE_OUT_PROG, 0xa0 }, + { WCD934X_DIFF_LO_LDO_OUT_PROG, 0x00 }, + { WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x8b }, + { WCD934X_DIFF_LO_COM_PA_FREQ, 0xb0 }, + { WCD934X_DIFF_LO_RESERVED_REG, 0x60 }, + { WCD934X_DIFF_LO_LO1_STATUS_1, 0x00 }, + { WCD934X_DIFF_LO_LO1_STATUS_2, 0x00 }, + { WCD934X_ANA_NEW_PAGE_REGISTER, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD934X_SLNQ_ANA_EN, 0x02 }, + { WCD934X_SLNQ_ANA_STATUS, 0x00 }, + { WCD934X_SLNQ_ANA_LDO_CONFIG, 0xea }, + { WCD934X_SLNQ_ANA_LDO_OCP_CONFIG, 0x95 }, + { WCD934X_SLNQ_ANA_TX_LDO_CONFIG, 0xb6 }, + { WCD934X_SLNQ_ANA_TX_DRV_CONFIG, 0x26 }, + { WCD934X_SLNQ_ANA_RX_CONFIG_1, 0x64 }, + { WCD934X_SLNQ_ANA_RX_CONFIG_2, 0x40 }, + { WCD934X_SLNQ_ANA_PLL_ENABLES, 0x00 }, + { WCD934X_SLNQ_ANA_PLL_PRESET, 0x08 }, + { WCD934X_SLNQ_ANA_PLL_STATUS, 0x00 }, + { WCD934X_CLK_SYS_PLL_ENABLES, 0x00 }, + { WCD934X_CLK_SYS_PLL_PRESET, 0x00 }, + { WCD934X_CLK_SYS_PLL_STATUS, 0x00 }, + { WCD934X_CLK_SYS_MCLK_PRG, 0x00 }, + { WCD934X_CLK_SYS_MCLK2_PRG1, 0x00 }, + { WCD934X_CLK_SYS_MCLK2_PRG2, 0x00 }, + { WCD934X_CLK_SYS_XO_PRG, 0x00 }, + { WCD934X_CLK_SYS_XO_CAP_XTP, 0x00 }, + { WCD934X_CLK_SYS_XO_CAP_XTM, 0x00 }, + { WCD934X_BOOST_BST_EN_DLY, 0x40 }, + { WCD934X_BOOST_CTRL_ILIM, 0x9c }, + { WCD934X_BOOST_VOUT_SETTING, 0xca }, + { WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x05 }, + { WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x0d }, + { WCD934X_SIDO_NEW_VOUT_D_FREQ1, 0x07 }, + { WCD934X_SIDO_NEW_VOUT_D_FREQ2, 0x00 }, + { WCD934X_MBHC_NEW_ELECT_REM_CLAMP_CTL, 0x00 }, + { WCD934X_MBHC_NEW_CTL_1, 0x02 }, + { WCD934X_MBHC_NEW_CTL_2, 0x05 }, + { WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0xe9 }, + { WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x0f }, + { WCD934X_MBHC_NEW_ZDET_RAMP_CTL, 0x00 }, + { WCD934X_MBHC_NEW_FSM_STATUS, 0x00 }, + { WCD934X_MBHC_NEW_ADC_RESULT, 0x00 }, + { WCD934X_TX_NEW_AMIC_4_5_SEL, 0x00 }, + { WCD934X_VBADC_NEW_ADC_MODE, 0x10 }, + { WCD934X_VBADC_NEW_ADC_DOUTMSB, 0x00 }, + { WCD934X_VBADC_NEW_ADC_DOUTLSB, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0xa0 }, + { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xfe }, + { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x02 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e }, + { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, + { WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI, 0x62 }, + { WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP, 0x01 }, + { WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP, 0x11 }, + { WCD934X_SLNQ_INT_ANA_INT_LDO_TEST, 0x0d }, + { WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1, 0x85 }, + { WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2, 0xb4 }, + { WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST, 0x16 }, + { WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_TEST, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1, 0x50 }, + { WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2, 0x04 }, + { WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RESERVED_1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_RESERVED_2, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG, 0x00 }, + { WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG, 0x00 }, + { WCD934X_CLK_SYS_INT_POST_DIV_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_POST_DIV_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_REF_DIV_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_REF_DIV_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_FILTER_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_FILTER_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_PLL_L_VAL, 0x00 }, + { WCD934X_CLK_SYS_INT_PLL_M_VAL, 0x00 }, + { WCD934X_CLK_SYS_INT_PLL_N_VAL, 0x00 }, + { WCD934X_CLK_SYS_INT_TEST_REG0, 0x00 }, + { WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG, 0x00 }, + { WCD934X_CLK_SYS_INT_VCO_PROG, 0x00 }, + { WCD934X_CLK_SYS_INT_TEST_REG1, 0x00 }, + { WCD934X_CLK_SYS_INT_LDO_LOCK_CFG, 0x00 }, + { WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0x00 }, + { WCD934X_CLK_SYS_INT_CLK_TEST1, 0x00 }, + { WCD934X_CLK_SYS_INT_CLK_TEST2, 0x00 }, + { WCD934X_CLK_SYS_INT_CLK_TEST3, 0x00 }, + { WCD934X_CLK_SYS_INT_XO_TEST1, 0x98 }, + { WCD934X_CLK_SYS_INT_XO_TEST2, 0x00 }, + { WCD934X_BOOST_INT_VCOMP_HYST, 0x02 }, + { WCD934X_BOOST_INT_VLOOP_FILTER, 0xef }, + { WCD934X_BOOST_INT_CTRL_IDELTA, 0xa8 }, + { WCD934X_BOOST_INT_CTRL_ILIM_STARTUP, 0x17 }, + { WCD934X_BOOST_INT_CTRL_MIN_ONTIME, 0x5f }, + { WCD934X_BOOST_INT_CTRL_MAX_ONTIME, 0x88 }, + { WCD934X_BOOST_INT_CTRL_TIMING, 0x0a }, + { WCD934X_BOOST_INT_TMUX_A_D, 0x00 }, + { WCD934X_BOOST_INT_SW_DRV_CNTL, 0xf8 }, + { WCD934X_BOOST_INT_SPARE1, 0x00 }, + { WCD934X_BOOST_INT_SPARE2, 0x00 }, + { WCD934X_SIDO_NEW_INT_RAMP_STATUS, 0x00 }, + { WCD934X_SIDO_NEW_INT_SPARE_1, 0x00 }, + { WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A, 0x64 }, + { WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D, 0x40 }, + { WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT, 0x24 }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL, 0x09 }, + { WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL, 0x7d }, + { WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST, 0x00 }, + { WCD934X_SIDO_NEW_INT_RAMP_CTL_A, 0x14 }, + { WCD934X_SIDO_NEW_INT_RAMP_CTL_D, 0x14 }, + { WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD, 0x33 }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1, 0x3f }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2, 0x74 }, + { WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3, 0x33 }, + { WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1, 0x1d }, + { WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2, 0x0a }, + { WCD934X_MBHC_NEW_INT_SLNQ_HPF, 0x50 }, + { WCD934X_MBHC_NEW_INT_SLNQ_REF, 0x24 }, + { WCD934X_MBHC_NEW_INT_SLNQ_COMP, 0x50 }, + { WCD934X_MBHC_NEW_INT_SPARE_2, 0x00 }, + { WCD934X_PAGE10_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_ANC0_CLK_RESET_CTL, 0x00 }, + { WCD934X_CDC_ANC0_MODE_1_CTL, 0x00 }, + { WCD934X_CDC_ANC0_MODE_2_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FF_SHIFT, 0x00 }, + { WCD934X_CDC_ANC0_FB_SHIFT, 0x00 }, + { WCD934X_CDC_ANC0_LPF_FF_A_CTL, 0x00 }, + { WCD934X_CDC_ANC0_LPF_FF_B_CTL, 0x00 }, + { WCD934X_CDC_ANC0_LPF_FB_CTL, 0x00 }, + { WCD934X_CDC_ANC0_SMLPF_CTL, 0x00 }, + { WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL, 0x00 }, + { WCD934X_CDC_ANC0_IIR_ADAPT_CTL, 0x00 }, + { WCD934X_CDC_ANC0_IIR_COEFF_1_CTL, 0x00 }, + { WCD934X_CDC_ANC0_IIR_COEFF_2_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FF_A_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FF_B_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FB_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC0_RC_COMMON_CTL, 0x00 }, + { WCD934X_CDC_ANC0_FIFO_COMMON_CTL, 0x88 }, + { WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC0_STATUS_FIFO, 0x00 }, + { WCD934X_CDC_ANC1_CLK_RESET_CTL, 0x00 }, + { WCD934X_CDC_ANC1_MODE_1_CTL, 0x00 }, + { WCD934X_CDC_ANC1_MODE_2_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FF_SHIFT, 0x00 }, + { WCD934X_CDC_ANC1_FB_SHIFT, 0x00 }, + { WCD934X_CDC_ANC1_LPF_FF_A_CTL, 0x00 }, + { WCD934X_CDC_ANC1_LPF_FF_B_CTL, 0x00 }, + { WCD934X_CDC_ANC1_LPF_FB_CTL, 0x00 }, + { WCD934X_CDC_ANC1_SMLPF_CTL, 0x00 }, + { WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL, 0x00 }, + { WCD934X_CDC_ANC1_IIR_ADAPT_CTL, 0x00 }, + { WCD934X_CDC_ANC1_IIR_COEFF_1_CTL, 0x00 }, + { WCD934X_CDC_ANC1_IIR_COEFF_2_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FF_A_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FF_B_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FB_GAIN_CTL, 0x00 }, + { WCD934X_CDC_ANC1_RC_COMMON_CTL, 0x00 }, + { WCD934X_CDC_ANC1_FIFO_COMMON_CTL, 0x88 }, + { WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR, 0x00 }, + { WCD934X_CDC_ANC1_STATUS_FIFO, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX0_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX0_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX0_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX0_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX0_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX0_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX0_TX_PATH_SEC7, 0x25 }, + { WCD934X_CDC_TX1_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX1_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX1_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX1_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX1_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX1_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX1_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX1_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX2_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX2_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX2_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX2_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX2_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX2_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX2_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX3_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX3_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX3_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX3_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX3_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX3_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX3_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX4_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX4_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX4_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX4_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX4_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX4_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX4_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX5_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX5_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX5_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX5_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX5_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX5_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX5_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX6_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX6_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX6_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX6_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX6_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX6_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX6_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX7_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX7_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX7_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX7_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX7_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX7_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX7_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_CTL, 0x04 }, + { WCD934X_CDC_TX8_TX_PATH_CFG0, 0x10 }, + { WCD934X_CDC_TX8_TX_PATH_CFG1, 0x03 }, + { WCD934X_CDC_TX8_TX_VOL_CTL, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_192_CTL, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_192_CFG, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC0, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC1, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC2, 0x01 }, + { WCD934X_CDC_TX8_TX_PATH_SEC3, 0x3c }, + { WCD934X_CDC_TX8_TX_PATH_SEC4, 0x20 }, + { WCD934X_CDC_TX8_TX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_TX8_TX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x02 }, + { WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x00 }, + { WCD934X_PAGE11_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_COMPANDER1_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER1_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER1_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER1_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER1_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER1_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER1_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER1_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER2_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER2_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER2_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER2_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER2_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER2_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER2_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER2_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER3_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER3_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER3_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER3_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER3_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER3_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER3_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER3_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER4_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER4_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER4_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER4_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER4_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER4_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER4_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER4_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER7_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER7_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER7_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER7_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER7_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER7_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER7_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER7_CTL7, 0x08 }, + { WCD934X_CDC_COMPANDER8_CTL0, 0x60 }, + { WCD934X_CDC_COMPANDER8_CTL1, 0xdb }, + { WCD934X_CDC_COMPANDER8_CTL2, 0xff }, + { WCD934X_CDC_COMPANDER8_CTL3, 0x35 }, + { WCD934X_CDC_COMPANDER8_CTL4, 0xff }, + { WCD934X_CDC_COMPANDER8_CTL5, 0x00 }, + { WCD934X_CDC_COMPANDER8_CTL6, 0x01 }, + { WCD934X_CDC_COMPANDER8_CTL7, 0x08 }, + { WCD934X_CDC_RX0_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX0_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX0_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX0_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX0_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX0_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX0_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX0_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX1_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX1_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX1_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX1_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX1_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX1_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC4, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX1_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX2_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX2_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX2_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX2_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX2_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX2_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC4, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX2_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX3_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX3_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX3_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX3_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX3_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX3_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX3_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX4_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX4_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX4_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX4_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC0, 0xfc }, + { WCD934X_CDC_RX4_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX4_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX4_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX7_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX7_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX7_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX7_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC0, 0x04 }, + { WCD934X_CDC_RX7_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX7_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX7_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_CTL, 0x04 }, + { WCD934X_CDC_RX8_RX_PATH_CFG0, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_CFG1, 0x64 }, + { WCD934X_CDC_RX8_RX_PATH_CFG2, 0x8f }, + { WCD934X_CDC_RX8_RX_VOL_CTL, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_CTL, 0x04 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 0x7e }, + { WCD934X_CDC_RX8_RX_VOL_MIX_CTL, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC0, 0x04 }, + { WCD934X_CDC_RX8_RX_PATH_SEC1, 0x08 }, + { WCD934X_CDC_RX8_RX_PATH_SEC2, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC3, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC5, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC6, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_SEC7, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, 0x08 }, + { WCD934X_CDC_RX8_RX_PATH_MIX_SEC1, 0x00 }, + { WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL, 0x00 }, + { WCD934X_PAGE12_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_CLSH_CRC, 0x00 }, + { WCD934X_CDC_CLSH_DLY_CTRL, 0x03 }, + { WCD934X_CDC_CLSH_DECAY_CTRL, 0x02 }, + { WCD934X_CDC_CLSH_HPH_V_PA, 0x1c }, + { WCD934X_CDC_CLSH_EAR_V_PA, 0x39 }, + { WCD934X_CDC_CLSH_HPH_V_HD, 0x0c }, + { WCD934X_CDC_CLSH_EAR_V_HD, 0x0c }, + { WCD934X_CDC_CLSH_K1_MSB, 0x01 }, + { WCD934X_CDC_CLSH_K1_LSB, 0x00 }, + { WCD934X_CDC_CLSH_K2_MSB, 0x00 }, + { WCD934X_CDC_CLSH_K2_LSB, 0x80 }, + { WCD934X_CDC_CLSH_IDLE_CTRL, 0x00 }, + { WCD934X_CDC_CLSH_IDLE_HPH, 0x00 }, + { WCD934X_CDC_CLSH_IDLE_EAR, 0x00 }, + { WCD934X_CDC_CLSH_TEST0, 0x07 }, + { WCD934X_CDC_CLSH_TEST1, 0x00 }, + { WCD934X_CDC_CLSH_OVR_VREF, 0x00 }, + { WCD934X_CDC_BOOST0_BOOST_PATH_CTL, 0x00 }, + { WCD934X_CDC_BOOST0_BOOST_CTL, 0xb2 }, + { WCD934X_CDC_BOOST0_BOOST_CFG1, 0x00 }, + { WCD934X_CDC_BOOST0_BOOST_CFG2, 0x00 }, + { WCD934X_CDC_BOOST1_BOOST_PATH_CTL, 0x00 }, + { WCD934X_CDC_BOOST1_BOOST_CTL, 0xb2 }, + { WCD934X_CDC_BOOST1_BOOST_CFG1, 0x00 }, + { WCD934X_CDC_BOOST1_BOOST_CFG2, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_PATH_CTL, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_CFG, 0x1a }, + { WCD934X_CDC_VBAT_VBAT_ADC_CAL1, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_ADC_CAL2, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_ADC_CAL3, 0x04 }, + { WCD934X_CDC_VBAT_VBAT_PK_EST1, 0xe0 }, + { WCD934X_CDC_VBAT_VBAT_PK_EST2, 0x01 }, + { WCD934X_CDC_VBAT_VBAT_PK_EST3, 0x40 }, + { WCD934X_CDC_VBAT_VBAT_RF_PROC1, 0x2a }, + { WCD934X_CDC_VBAT_VBAT_RF_PROC2, 0x86 }, + { WCD934X_CDC_VBAT_VBAT_TAC1, 0x70 }, + { WCD934X_CDC_VBAT_VBAT_TAC2, 0x18 }, + { WCD934X_CDC_VBAT_VBAT_TAC3, 0x18 }, + { WCD934X_CDC_VBAT_VBAT_TAC4, 0x03 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD1, 0x01 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD2, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD3, 0x64 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD4, 0x01 }, + { WCD934X_CDC_VBAT_VBAT_DEBUG1, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL, 0x00 }, + { WCD934X_CDC_VBAT_VBAT_BAN, 0x0c }, + { WCD934X_MIXING_ASRC0_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC0_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC0_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC0_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC0_STATUS_FIFO, 0x00 }, + { WCD934X_MIXING_ASRC1_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC1_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC1_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC1_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC1_STATUS_FIFO, 0x00 }, + { WCD934X_MIXING_ASRC2_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC2_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC2_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC2_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC2_STATUS_FIFO, 0x00 }, + { WCD934X_MIXING_ASRC3_CLK_RST_CTL, 0x00 }, + { WCD934X_MIXING_ASRC3_CTL0, 0x00 }, + { WCD934X_MIXING_ASRC3_CTL1, 0x00 }, + { WCD934X_MIXING_ASRC3_FIFO_CTL, 0xa8 }, + { WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_MIXING_ASRC3_STATUS_FIFO, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_DATA_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_0, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_1, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_2, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_RD_DATA_3, 0x00 }, + { WCD934X_SWR_AHB_BRIDGE_ACCESS_CFG, 0x0f }, + { WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS, 0x03 }, + { WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, 0x04 }, + { WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1, 0x00 }, + { WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, 0x04 }, + { WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1, 0x00 }, + { WCD934X_SIDETONE_ASRC0_CLK_RST_CTL, 0x00 }, + { WCD934X_SIDETONE_ASRC0_CTL0, 0x00 }, + { WCD934X_SIDETONE_ASRC0_CTL1, 0x00 }, + { WCD934X_SIDETONE_ASRC0_FIFO_CTL, 0xa8 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC0_STATUS_FIFO, 0x00 }, + { WCD934X_SIDETONE_ASRC1_CLK_RST_CTL, 0x00 }, + { WCD934X_SIDETONE_ASRC1_CTL0, 0x00 }, + { WCD934X_SIDETONE_ASRC1_CTL1, 0x00 }, + { WCD934X_SIDETONE_ASRC1_FIFO_CTL, 0xa8 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_SIDETONE_ASRC1_STATUS_FIFO, 0x00 }, + { WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL, 0x00 }, + { WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0, 0x01 }, + { WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL, 0x00 }, + { WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0, 0x01 }, + { WCD934X_EC_ASRC0_CLK_RST_CTL, 0x00 }, + { WCD934X_EC_ASRC0_CTL0, 0x00 }, + { WCD934X_EC_ASRC0_CTL1, 0x00 }, + { WCD934X_EC_ASRC0_FIFO_CTL, 0xa8 }, + { WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC0_STATUS_FIFO, 0x00 }, + { WCD934X_EC_ASRC1_CLK_RST_CTL, 0x00 }, + { WCD934X_EC_ASRC1_CTL0, 0x00 }, + { WCD934X_EC_ASRC1_CTL1, 0x00 }, + { WCD934X_EC_ASRC1_FIFO_CTL, 0xa8 }, + { WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB, 0x00 }, + { WCD934X_EC_ASRC1_STATUS_FIFO, 0x00 }, + { WCD934X_PAGE13_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0x00 }, + { WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0x00 }, + { WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0x00 }, + { WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x0c }, + { WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x00 }, + { WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL, 0x0f }, + { WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_CTL, 0x08 }, + { WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1, 0x4b }, + { WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_STATUS, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD, 0x00 }, + { WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_CTL, 0x40 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_CTL, 0x40 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL, 0x00 }, + { WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL, 0x00 }, + { WCD934X_CDC_TOP_TOP_CFG0, 0x00 }, + { WCD934X_CDC_TOP_TOP_CFG1, 0x00 }, + { WCD934X_CDC_TOP_TOP_CFG7, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHL_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_HPHR_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_LUT, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB, 0x00 }, + { WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB, 0x00 }, + { WCD934X_CDC_DSD0_PATH_CTL, 0x00 }, + { WCD934X_CDC_DSD0_CFG0, 0x00 }, + { WCD934X_CDC_DSD0_CFG1, 0x00 }, + { WCD934X_CDC_DSD0_CFG2, 0x42 }, + { WCD934X_CDC_DSD0_CFG3, 0x00 }, + { WCD934X_CDC_DSD0_CFG4, 0x02 }, + { WCD934X_CDC_DSD0_CFG5, 0x00 }, + { WCD934X_CDC_DSD1_PATH_CTL, 0x00 }, + { WCD934X_CDC_DSD1_CFG0, 0x00 }, + { WCD934X_CDC_DSD1_CFG1, 0x00 }, + { WCD934X_CDC_DSD1_CFG2, 0x42 }, + { WCD934X_CDC_DSD1_CFG3, 0x00 }, + { WCD934X_CDC_DSD1_CFG4, 0x02 }, + { WCD934X_CDC_DSD1_CFG5, 0x00 }, + { WCD934X_CDC_RX_IDLE_DET_PATH_CTL, 0x00 }, + { WCD934X_CDC_RX_IDLE_DET_CFG0, 0x07 }, + { WCD934X_CDC_RX_IDLE_DET_CFG1, 0x3c }, + { WCD934X_CDC_RX_IDLE_DET_CFG2, 0x00 }, + { WCD934X_CDC_RX_IDLE_DET_CFG3, 0x00 }, + { WCD934X_PAGE14_PAGE_REGISTER, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST0_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST0_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST0_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST0_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST1_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST1_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST1_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST1_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST2_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST2_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST2_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST2_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_CTL, 0x09 }, + { WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL, 0x06 }, + { WCD934X_CDC_RATE_EST3_RE_TIMER, 0x01 }, + { WCD934X_CDC_RATE_EST3_RE_BW_SW, 0x20 }, + { WCD934X_CDC_RATE_EST3_RE_THRESH, 0xa0 }, + { WCD934X_CDC_RATE_EST3_RE_STATUS, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1, 0x08 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2, 0x07 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5, 0x05 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5, 0x03 }, + { WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_PH_DET, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_DIAG_CLR, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32, 0x00 }, + { WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43, 0x00 }, + { WCD934X_PAGE15_PAGE_REGISTER, 0x00 }, + { WCD934X_SPLINE_SRC0_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC0_STATUS, 0x00 }, + { WCD934X_SPLINE_SRC1_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC1_STATUS, 0x00 }, + { WCD934X_SPLINE_SRC2_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC2_STATUS, 0x00 }, + { WCD934X_SPLINE_SRC3_CLK_RST_CTL_0, 0x20 }, + { WCD934X_SPLINE_SRC3_STATUS, 0x00 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x11 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x20 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2, 0x00 }, + { WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x08 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x11 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x20 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2, 0x00 }, + { WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x08 }, + { WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0, 0x00 }, + { WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1, 0x00 }, + { WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0, 0x00 }, + { WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL, 0x4c }, + { WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR, 0x00 }, + { WCD934X_PAGE80_PAGE_REGISTER, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_0, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_1, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_2, 0x00 }, + { WCD934X_CODEC_CPR_WR_DATA_3, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_0, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_1, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_2, 0x00 }, + { WCD934X_CODEC_CPR_WR_ADDR_3, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_0, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_1, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_2, 0x00 }, + { WCD934X_CODEC_CPR_RD_ADDR_3, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_0, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_1, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_2, 0x00 }, + { WCD934X_CODEC_CPR_RD_DATA_3, 0x00 }, + { WCD934X_CODEC_CPR_ACCESS_CFG, 0x0f }, + { WCD934X_CODEC_CPR_ACCESS_STATUS, 0x03 }, + { WCD934X_CODEC_CPR_NOM_CX_VDD, 0xb4 }, + { WCD934X_CODEC_CPR_SVS_CX_VDD, 0x5c }, + { WCD934X_CODEC_CPR_SVS2_CX_VDD, 0x40 }, + { WCD934X_CODEC_CPR_NOM_MX_VDD, 0xb4 }, + { WCD934X_CODEC_CPR_SVS_MX_VDD, 0xb4 }, + { WCD934X_CODEC_CPR_SVS2_MX_VDD, 0xa0 }, + { WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD, 0x28 }, + { WCD934X_CODEC_CPR_MAX_SVS2_STEP, 0x08 }, + { WCD934X_CODEC_CPR_CTL, 0x00 }, + { WCD934X_CODEC_CPR_SW_MODECHNG_STATUS, 0x00 }, + { WCD934X_CODEC_CPR_SW_MODECHNG_START, 0x00 }, + { WCD934X_CODEC_CPR_CPR_STATUS, 0x00 }, + { WCD934X_PAGE128_PAGE_REGISTER, 0x00 }, + { WCD934X_TLMM_BIST_MODE_PINCFG, 0x00 }, + { WCD934X_TLMM_RF_PA_ON_PINCFG, 0x00 }, + { WCD934X_TLMM_INTR1_PINCFG, 0x00 }, + { WCD934X_TLMM_INTR2_PINCFG, 0x00 }, + { WCD934X_TLMM_SWR_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_SWR_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_2_SCK_PINCFG, 0x00 }, + { WCD934X_TLMM_SLIMBUS_DATA1_PINCFG, 0x00 }, + { WCD934X_TLMM_SLIMBUS_DATA2_PINCFG, 0x00 }, + { WCD934X_TLMM_SLIMBUS_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2C_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2C_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_RX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_TX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_SCK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_0_WS_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_RX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_TX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_SCK_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_1_WS_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC1_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC1_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC2_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC2_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC3_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_DMIC3_DATA_PINCFG, 0x00 }, + { WCD934X_TLMM_JTCK_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO1_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO2_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO3_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO4_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_CSN_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_CLK_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_DOUT_PINCFG, 0x00 }, + { WCD934X_TLMM_SPI_S_DIN_PINCFG, 0x00 }, + { WCD934X_TLMM_BA_N_PINCFG, 0x00 }, + { WCD934X_TLMM_GPIO0_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_2_RX_PINCFG, 0x00 }, + { WCD934X_TLMM_I2S_2_WS_PINCFG, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_0, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_1, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_2, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_3, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_OE_4, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_0, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_1, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_2, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_3, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_CTL_DATA_4, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_DRVCTL_0, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_DRVCTL_1, 0x00 }, + { WCD934X_TEST_DEBUG_PIN_STATUS, 0x00 }, + { WCD934X_TEST_DEBUG_NPL_DLY_TEST_1, 0x10 }, + { WCD934X_TEST_DEBUG_NPL_DLY_TEST_2, 0x60 }, + { WCD934X_TEST_DEBUG_MEM_CTRL, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_BUS_SEL, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_JTAG, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_1, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_2, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_3, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_4, 0x00 }, + { WCD934X_TEST_DEBUG_DEBUG_EN_5, 0x00 }, + { WCD934X_TEST_DEBUG_ANA_DTEST_DIR, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3, 0x00 }, + { WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4, 0x00 }, + { WCD934X_TEST_DEBUG_SYSMEM_CTRL, 0x00 }, + { WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY, 0x00 }, + { WCD934X_TEST_DEBUG_LVAL_NOM_LOW, 0x96 }, + { WCD934X_TEST_DEBUG_LVAL_NOM_HIGH, 0x00 }, + { WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW, 0x53 }, + { WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH, 0x00 }, + { WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR, 0x00 }, + { WCD934X_TEST_DEBUG_CODEC_DIAGS, 0x00 }, +}; + +/* + * wcd934x_regmap_register_patch: Update register defaults based on version + * @regmap: handle to wcd9xxx regmap + * @version: wcd934x version + * + * Returns error code in case of failure or 0 for success + */ +int wcd934x_regmap_register_patch(struct regmap *regmap, int revision) +{ + int rc = 0; + + if (!regmap) { + pr_err("%s: regmap struct is NULL\n", __func__); + return -EINVAL; + } + + switch (revision) { + case TAVIL_VERSION_1_1: + case TAVIL_VERSION_WCD9340_1_1: + case TAVIL_VERSION_WCD9341_1_1: + regcache_cache_only(regmap, true); + rc = regmap_multi_reg_write(regmap, wcd934x_1_1_defaults, + ARRAY_SIZE(wcd934x_1_1_defaults)); + regcache_cache_only(regmap, false); + break; + } + + return rc; +} +EXPORT_SYMBOL(wcd934x_regmap_register_patch); + +static bool wcd934x_is_readable_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + /* + * Get the page number from MSB of codec register. If its 0x80, assign + * the corresponding page index PAGE_0x80. + */ + pg_num = reg >> 0x8; + if (pg_num == 0x80) + pg_num = WCD934X_PAGE_0X80; + else if (pg_num == 0x50) + pg_num = WCD934X_PAGE_0x50; + else if (pg_num > 0xF) + return false; + + reg_tbl = wcd934x_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl && reg_tbl[reg_offset]) + return true; + else + return false; +} + +static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) +{ + u8 pg_num, reg_offset; + const u8 *reg_tbl = NULL; + + pg_num = reg >> 0x8; + if (pg_num == 0x80) + pg_num = WCD934X_PAGE_0X80; + else if (pg_num == 0x50) + pg_num = WCD934X_PAGE_0x50; + else if (pg_num > 0xF) + return false; + + reg_tbl = wcd934x_reg[pg_num]; + reg_offset = reg & 0xFF; + + if (reg_tbl && reg_tbl[reg_offset] == WCD934X_READ) + return true; + + /* IIR Coeff registers are not cacheable */ + if ((reg >= WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL) && + (reg <= WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)) + return true; + + if ((reg >= WCD934X_CDC_ANC0_IIR_COEFF_1_CTL) && + (reg <= WCD934X_CDC_ANC0_FB_GAIN_CTL)) + return true; + + if ((reg >= WCD934X_CDC_ANC1_IIR_COEFF_1_CTL) && + (reg <= WCD934X_CDC_ANC1_FB_GAIN_CTL)) + return true; + + if ((reg >= WCD934X_CODEC_CPR_WR_DATA_0) && + (reg <= WCD934X_CODEC_CPR_RD_DATA_3)) + return true; + + /* + * Need to mark volatile for registers that are writable but + * only few bits are read-only + */ + switch (reg) { + case WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL: + case WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0: + case WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1: + case WCD934X_CPE_SS_CPAR_CTL: + case WCD934X_CPE_SS_STATUS: + case WCD934X_CODEC_RPM_RST_CTL: + case WCD934X_SIDO_NEW_VOUT_A_STARTUP: + case WCD934X_SIDO_NEW_VOUT_D_STARTUP: + case WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL: + case WCD934X_ANA_MBHC_MECH: + case WCD934X_ANA_MBHC_ELECT: + case WCD934X_ANA_MBHC_ZDET: + case WCD934X_ANA_MICB2: + case WCD934X_CODEC_RPM_CLK_MCLK_CFG: + case WCD934X_CLK_SYS_MCLK_PRG: + case WCD934X_CHIP_TIER_CTRL_EFUSE_CTL: + case WCD934X_ANA_BIAS: + case WCD934X_ANA_BUCK_CTL: + case WCD934X_ANA_RCO: + case WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL: + case WCD934X_CODEC_RPM_CLK_GATE: + case WCD934X_BIAS_VBG_FINE_ADJ: + case WCD934X_CODEC_CPR_SVS_CX_VDD: + case WCD934X_CODEC_CPR_SVS2_CX_VDD: + case WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL: + return true; + } + + return false; +} + +struct regmap_config wcd934x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd934x_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd934x_defaults), + .max_register = WCD934X_MAX_REGISTER, + .volatile_reg = wcd934x_is_volatile_register, + .readable_reg = wcd934x_is_readable_register, + .can_multi_write = true, +}; diff --git a/drivers/mfd/wcd934x-tables.c b/drivers/mfd/wcd934x-tables.c new file mode 100644 index 000000000000..db963d08b66e --- /dev/null +++ b/drivers/mfd/wcd934x-tables.c @@ -0,0 +1,2155 @@ +/* + * 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 + +#define WCD934X_REG(reg) ((reg) & 0xFF) + +const u8 wcd934x_page0_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE0_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_BYPASS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_GATE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_MCLK_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_CLK_MCLK2_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX0_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX1_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX2_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX3_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX4_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX5_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX6_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_RX7_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX0_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX1_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX2_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX3_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX4_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX5_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX6_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX7_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX8_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX9_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX10_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX11_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX13_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX14_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_SB_TX15_INP_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_TX0_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_TX1_0_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_TX1_1_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_0_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_3_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_CLKSRC_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_0_TDM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DATA_HUB_I2S_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA4_PRT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_SBTX0_7_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_RDMA_SBTX8_11_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA0_PRT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA3_PRT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA4_PRT0_3_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DMA_WDMA4_PRT4_7_CFG)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page1_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE1_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_FLL_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_FLL_STATUS_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_FLL_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_I2S_FLL_STATUS_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_USER_CTL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_FLL_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SB_FLL_STATUS_3)] = WCD934X_READ, +}; + +const u8 wcd934x_page2_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE2_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPEFLL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_US_BUF_INT_PERIOD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SVA_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_US_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_MAD_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPAR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC0_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_DMIC_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPAR_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_WDOG_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_BACKUP_INT)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_STATUS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_CPE_OCD_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_MAIN_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_MAIN_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SOC_MAD_INP_SEL)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page4_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE4_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CLR_COMMIT)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR0)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR1)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR2)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN1_CLEAR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN2_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_PIN2_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_PIN2_CLEAR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_MASK2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_STATUS2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_CLEAR2)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_CLEAR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_LEVEL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_BYPASS3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_SET3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CODEC_MISC_MASK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_INTR_CODEC_MISC_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_INTR_CODEC_MISC_CLEAR)] = WCD934X_WRITE, +}; + +const u8 wcd934x_page5_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE5_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEVICE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_REVISION)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_H_COMMAND)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_COMM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_FRAME_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SW_EVENT_RD)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_SW_EVENT_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_RAM_CNTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BANK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_10)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_11)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_12)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_13)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_14)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_15)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_16)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_17)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_18)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_19)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_20)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_21)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_22)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_23)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_24)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_25)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_26)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_27)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_28)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_29)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_30)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_31)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_32)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_33)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_34)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_35)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_36)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_37)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_38)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_39)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3B)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3C)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3E)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3F)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TOP_CTRL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_TOP_CTRL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_PDM_MUTE_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_FS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_GPOUT_ENABLE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_GPOUT_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_IP_TESTING)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS4)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR0)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR1)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR2)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR3)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR4)] = WCD934X_WRITE, +}; + +const u8 wcd934x_page6_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_ANA_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_BIAS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_RCO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_PAGE6_SPARE2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_PAGE6_SPARE3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_BUCK_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_BUCK_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_RX_SUPPLIES)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_HPH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_EAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_LO_1_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MAD_SETUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_AMIC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_MECH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_ELECT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_ZDET)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_RESULT_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_MBHC_RESULT_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_MBHC_RESULT_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MBHC_BTN7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB2_RAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_MICB4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_ANA_VBADC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BIAS_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BIAS_VBG_FINE_ADJ)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CTRL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_TEST_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_4)] = WCD934X_READ, + [WCD934X_REG(WCD934X_RCO_CAL_OUT_5)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_MODE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_MODE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_MODE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_MODE_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_VCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_VCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_VCL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CCL_10)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_FILTER_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_FILTER_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_DRIVER_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_DRIVER_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_DRIVER_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_EXT_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_EXT_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_OUT_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_CAL_CODE_OUT_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_TEST_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_TEST_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_CLK)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_ANA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_SPARE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_SPARE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_CTL_BCS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_STATUS_SPARE_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MBHC_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_SUBBLOCK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_IBIAS_FE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_BIAS_ADC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_FE_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_ADC_REF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_ADC_IO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_ADC_SAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_DEBUG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_BIAS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_STB_LOADS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_LDOH_SLOWRAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB1_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB1_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB1_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB2_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB2_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB2_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB3_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB3_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB3_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB4_TEST_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB4_TEST_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MICB4_TEST_CTL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_ADC_VCM)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_BIAS_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_ADC_INT1_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_ADC_INT2_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_START)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_STOP_9P6M)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_STOP_12P288M)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TEST_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_ADC_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_ATEST_REFCTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TEST_BLK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_TXFE_CLKDIV)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_1_2_SAR1_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_1_2_SAR2_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_3_4_TEST_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_ADC_IB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_ATEST_REFCTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_TEST_BLK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_TXFE_CLKDIV)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TX_3_4_SAR1_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_3_4_SAR2_ERR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CLASSH_MODE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_MODE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_MODE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_VCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_VCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_BUCK_TMUX_A_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_BUCK_SW_DRV_CNTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLASSH_SPARE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_8)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_9)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_CTRL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_FLYBACK_TEST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_AUX_SW_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_PA_AUX_IN_CONN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_TIMER_DIV)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_OCP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_OCP_COUNT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_EAR_DAC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_EAR_AMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_LDO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_RDAC_LDO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_CNP1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_HPH_LOWPOWER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_REF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_LDO)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_SELO_DAC_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_BUCK_RST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_BUCK_VREF_ERRAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_FLYB_ERRAMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_FLYB_BUFF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_BIAS_FLYB_MID_RST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_HPH_R_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_HPH_CNP_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_CNP_WG_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_CNP_WG_TIME)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_OCP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_AUTO_CHOP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_CHOP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_PA_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_PA_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_CLK_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_CLK_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_LDO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_REFBUFF_UHQA_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_REFBUFF_LP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_L_DAC_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_R_DAC_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_EN_REG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_CMBUFF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_ICTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_EN_DBG_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_CNP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_DAC_CTL_ATEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EAR_STATUS_REG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EAR_EAR_MISC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_MISC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LO2_COMPANDER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LO1_COMPANDER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_COMMON)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_BYPASS_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_CNP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_CORE_OUT_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LDO_OUT_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_COM_PA_FREQ)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_RESERVED_REG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_DIFF_LO_LO1_STATUS_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_DIFF_LO_LO1_STATUS_2)] = WCD934X_READ, +}; + +const u8 wcd934x_page7_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_ANA_NEW_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_ANA_HPH2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_ANA_HPH3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_EN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_ANA_LDO_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_LDO_OCP_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_TX_LDO_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_TX_DRV_CONFIG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_RX_CONFIG_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_RX_CONFIG_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_PLL_ENABLES)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_PLL_PRESET)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_ANA_PLL_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CLK_SYS_PLL_ENABLES)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_PLL_PRESET)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_PLL_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CLK_SYS_MCLK_PRG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_MCLK2_PRG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_MCLK2_PRG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_XO_PRG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_XO_CAP_XTP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_XO_CAP_XTM)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_BST_EN_DLY)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_CTRL_ILIM)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_VOUT_SETTING)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_A_STARTUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_STARTUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_FREQ1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_FREQ2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_ELECT_REM_CLAMP_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_CTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_CTL_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_PLUG_DETECT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_ZDET_ANA_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_ZDET_RAMP_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_FSM_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MBHC_NEW_ADC_RESULT)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TX_NEW_AMIC_4_5_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_NEW_ADC_MODE)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_VBADC_NEW_ADC_DOUTMSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_VBADC_NEW_ADC_DOUTLSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_HD2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_VREF_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_MISC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_MISC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_MISC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_TEST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RESERVED_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RESERVED_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_POST_DIV_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_POST_DIV_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_REF_DIV_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_REF_DIV_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_FILTER_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_FILTER_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_L_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_M_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_N_VAL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_TEST_REG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_VCO_PROG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_TEST_REG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_LDO_LOCK_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_XO_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CLK_SYS_INT_XO_TEST2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_VCOMP_HYST)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_VLOOP_FILTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_IDELTA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_ILIM_STARTUP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_MIN_ONTIME)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_MAX_ONTIME)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_CTRL_TIMING)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_TMUX_A_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_SW_DRV_CNTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_SPARE1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_BOOST_INT_SPARE2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_SPARE_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_CTL_A)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_CTL_D)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_HPF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_REF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_COMP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MBHC_NEW_INT_SPARE_2)] = WCD934X_READ_WRITE, + +}; + +const u8 wcd934x_page10_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE10_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_CLK_RESET_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_MODE_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_MODE_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FF_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FB_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_LPF_FF_A_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_LPF_FF_B_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_LPF_FB_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_SMLPF_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_IIR_ADAPT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_IIR_COEFF_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_IIR_COEFF_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FF_A_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FF_B_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FB_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_RC_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_FIFO_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_CLK_RESET_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_MODE_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_MODE_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FF_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FB_SHIFT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_LPF_FF_A_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_LPF_FF_B_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_LPF_FB_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_SMLPF_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_IIR_ADAPT_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_IIR_COEFF_1_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_IIR_COEFF_2_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FF_A_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FF_B_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FB_GAIN_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_RC_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_FIFO_COMMON_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_ANC1_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_192_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_192_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0)] = + WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page11_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE11_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL6)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_VOL_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC6)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page12_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE12_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_CRC)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_DLY_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_DECAY_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_HPH_V_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_EAR_V_PA)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_HPH_V_HD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_EAR_V_HD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K1_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K1_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K2_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_K2_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_IDLE_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_IDLE_HPH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_IDLE_EAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_TEST0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_TEST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLSH_OVR_VREF)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_RF_PROC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_RF_PROC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_DEBUG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_VBAT_VBAT_BAN)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_ACCESS_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FIFO)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_CTL0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_CTL1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FIFO)] = WCD934X_READ, +}; + +const u8 wcd934x_page13_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE13_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_ANC_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD)] = + WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG7)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_LUT)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DSD0_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD0_CFG5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DSD1_CFG5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_PATH_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG3)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page14_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE14_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_TIMER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_BW_SW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_THRESH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_PH_DET)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_CLR)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43)] = WCD934X_READ, +}; + +const u8 wcd934x_page15_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE15_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC0_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC0_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SPLINE_SRC1_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC1_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SPLINE_SRC2_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC2_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_SPLINE_SRC3_CLK_RST_CTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_SPLINE_SRC3_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR)] = + WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page_0x50_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_MAX_SVS2_STEP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_START)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CPR_STATUS)] = WCD934X_READ_WRITE, +}; + +const u8 wcd934x_page_0x80_reg_access[WCD934X_PAGE_SIZE] = { + [WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_0)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_1)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_2)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_3)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_CFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_NOM_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_MAX_SVS2_STEP)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CTL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_START)] = WCD934X_WRITE, + [WCD934X_REG(WCD934X_CODEC_CPR_CPR_STATUS)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_PAGE128_PAGE_REGISTER)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_BIST_MODE_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_RF_PA_ON_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_INTR1_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_INTR2_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SWR_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SWR_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_2_SCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SLIMBUS_DATA1_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SLIMBUS_DATA2_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SLIMBUS_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2C_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2C_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_RX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_TX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_SCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_0_WS_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_RX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_TX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_SCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_1_WS_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC1_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC1_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC2_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC2_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC3_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_DMIC3_DATA_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_JTCK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO1_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO2_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO3_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO4_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_CSN_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_CLK_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_DOUT_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_SPI_S_DIN_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_BA_N_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_GPIO0_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_2_RX_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TLMM_I2S_2_WS_PINCFG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_DRVCTL_0)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_DRVCTL_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PIN_STATUS)] = WCD934X_READ, + [WCD934X_REG(WCD934X_TEST_DEBUG_NPL_DLY_TEST_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_NPL_DLY_TEST_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_MEM_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_BUS_SEL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_JTAG)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_1)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_2)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_3)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_4)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_5)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_ANA_DTEST_DIR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_SYSMEM_CTRL)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_NOM_LOW)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_NOM_HIGH)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH)] = + WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR)] = WCD934X_READ_WRITE, + [WCD934X_REG(WCD934X_TEST_DEBUG_CODEC_DIAGS)] = WCD934X_READ, +}; + +const u8 * const wcd934x_reg[WCD934X_NUM_PAGES] = { + [WCD934X_PAGE_0] = wcd934x_page0_reg_access, + [WCD934X_PAGE_1] = wcd934x_page1_reg_access, + [WCD934X_PAGE_2] = wcd934x_page2_reg_access, + [WCD934X_PAGE_4] = wcd934x_page4_reg_access, + [WCD934X_PAGE_5] = wcd934x_page5_reg_access, + [WCD934X_PAGE_6] = wcd934x_page6_reg_access, + [WCD934X_PAGE_7] = wcd934x_page7_reg_access, + [WCD934X_PAGE_10] = wcd934x_page10_reg_access, + [WCD934X_PAGE_11] = wcd934x_page11_reg_access, + [WCD934X_PAGE_12] = wcd934x_page12_reg_access, + [WCD934X_PAGE_13] = wcd934x_page13_reg_access, + [WCD934X_PAGE_14] = wcd934x_page14_reg_access, + [WCD934X_PAGE_15] = wcd934x_page15_reg_access, + [WCD934X_PAGE_0x50] = wcd934x_page_0x50_reg_access, + [WCD934X_PAGE_0X80] = wcd934x_page_0x80_reg_access, +}; diff --git a/drivers/mfd/wcd9xxx-core-init.c b/drivers/mfd/wcd9xxx-core-init.c new file mode 100644 index 000000000000..7f933990682d --- /dev/null +++ b/drivers/mfd/wcd9xxx-core-init.c @@ -0,0 +1,55 @@ +/* Copyright (c) 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 + +#define NUM_DRIVERS_REG_RET 3 + +static int __init wcd9xxx_core_init(void) +{ + int ret[NUM_DRIVERS_REG_RET] = {0}; + int i = 0; + + ret[0] = msm_cdc_pinctrl_drv_init(); + if (ret[0]) + pr_err("%s: Failed init pinctrl drv: %d\n", __func__, ret[0]); + + ret[1] = wcd9xxx_irq_drv_init(); + if (ret[1]) + pr_err("%s: Failed init irq drv: %d\n", __func__, ret[1]); + + ret[2] = wcd9xxx_init(); + if (ret[2]) + pr_err("%s: Failed wcd core drv: %d\n", __func__, ret[2]); + + for (i = 0; i < NUM_DRIVERS_REG_RET; i++) { + if (ret[i]) + return ret[i]; + } + + return 0; +} +module_init(wcd9xxx_core_init); + +static void __exit wcd9xxx_core_exit(void) +{ + wcd9xxx_exit(); + wcd9xxx_irq_drv_exit(); + msm_cdc_pinctrl_drv_exit(); +} +module_exit(wcd9xxx_core_exit); + +MODULE_DESCRIPTION("WCD9XXX CODEC core init driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c new file mode 100644 index 000000000000..b373acb11005 --- /dev/null +++ b/drivers/mfd/wcd9xxx-core.c @@ -0,0 +1,1714 @@ +/* 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 +#include +#include +#include +#include +#include "wcd9xxx-regmap.h" + +#define WCD9XXX_REGISTER_START_OFFSET 0x800 +#define WCD9XXX_SLIM_RW_MAX_TRIES 3 +#define SLIMBUS_PRESENT_TIMEOUT 100 + +#define MAX_WCD9XXX_DEVICE 4 +#define WCD9XXX_I2C_GSBI_SLAVE_ID "3-000d" +#define WCD9XXX_I2C_TOP_SLAVE_ADDR 0x0d +#define WCD9XXX_ANALOG_I2C_SLAVE_ADDR 0x77 +#define WCD9XXX_DIGITAL1_I2C_SLAVE_ADDR 0x66 +#define WCD9XXX_DIGITAL2_I2C_SLAVE_ADDR 0x55 +#define WCD9XXX_I2C_TOP_LEVEL 0 +#define WCD9XXX_I2C_ANALOG 1 +#define WCD9XXX_I2C_DIGITAL_1 2 +#define WCD9XXX_I2C_DIGITAL_2 3 + +/* + * Number of return values needs to be checked for each + * registration of Slimbus of I2C bus for each codec + */ +#define NUM_WCD9XXX_REG_RET 4 + +#define SLIM_USR_MC_REPEAT_CHANGE_VALUE 0x0 +#define SLIM_REPEAT_WRITE_MAX_SLICE 16 +#define REG_BYTES 2 +#define VAL_BYTES 1 +#define WCD9XXX_PAGE_NUM(reg) (((reg) >> 8) & 0xff) +#define WCD9XXX_PAGE_SIZE 256 + +struct wcd9xxx_i2c { + struct i2c_client *client; + struct i2c_msg xfer_msg[2]; + struct mutex xfer_lock; + int mod_id; +}; + +static struct regmap_config wcd9xxx_base_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .can_multi_write = true, +}; + +static struct regmap_config wcd9xxx_i2c_base_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .can_multi_write = false, + .use_single_rw = true, +}; + +static u8 wcd9xxx_pgd_la; +static u8 wcd9xxx_inf_la; + +static const int wcd9xxx_cdc_types[] = { + [WCD9XXX] = WCD9XXX, + [WCD9330] = WCD9330, + [WCD9335] = WCD9335, + [WCD934X] = WCD934X, +}; + +static const struct of_device_id wcd9xxx_of_match[] = { + { .compatible = "qcom,tasha-i2c-pgd", + .data = (void *)&wcd9xxx_cdc_types[WCD9335]}, + { .compatible = "qcom,wcd9xxx-i2c", + .data = (void *)&wcd9xxx_cdc_types[WCD9330]}, + { } +}; +MODULE_DEVICE_TABLE(of, wcd9xxx_of_match); + +static int wcd9xxx_slim_device_up(struct slim_device *sldev); +static int wcd9xxx_slim_device_down(struct slim_device *sldev); + +struct wcd9xxx_i2c wcd9xxx_modules[MAX_WCD9XXX_DEVICE]; + +static int wcd9xxx_slim_multi_reg_write(struct wcd9xxx *wcd9xxx, + const void *data, size_t count) +{ + unsigned int reg; + struct device *dev; + u8 val[WCD9XXX_PAGE_SIZE]; + int ret = 0; + int i = 0; + int n = 0; + unsigned int page_num; + size_t num_regs = (count / (REG_BYTES + VAL_BYTES)); + struct wcd9xxx_reg_val *bulk_reg; + u8 *buf; + + dev = wcd9xxx->dev; + if (!data) { + dev_err(dev, "%s: data is NULL\n", __func__); + return -EINVAL; + } + if (num_regs == 0) + return -EINVAL; + + bulk_reg = kzalloc(num_regs * (sizeof(struct wcd9xxx_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + buf = (u8 *)data; + reg = *(u16 *)buf; + page_num = WCD9XXX_PAGE_NUM(reg); + for (i = 0, n = 0; n < num_regs; i++, n++) { + reg = *(u16 *)buf; + if (page_num != WCD9XXX_PAGE_NUM(reg)) { + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + i, false); + page_num = WCD9XXX_PAGE_NUM(reg); + i = 0; + } + buf += REG_BYTES; + val[i] = *buf; + buf += VAL_BYTES; + bulk_reg[i].reg = reg; + bulk_reg[i].buf = &val[i]; + bulk_reg[i].bytes = 1; + } + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + i, false); + if (ret) + dev_err(dev, "%s: error writing bulk regs\n", + __func__); + + kfree(bulk_reg); + return ret; +} + +/* + * wcd9xxx_interface_reg_read: Read slim interface registers + * + * @wcd9xxx: Pointer to wcd9xxx structure + * @reg: register adderss + * + * Returns register value in success and negative error code in case of failure + */ +int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg) +{ + u8 val; + int ret; + + mutex_lock(&wcd9xxx->io_lock); + ret = wcd9xxx->read_dev(wcd9xxx, reg, 1, (void *)&val, + true); + if (ret < 0) + dev_err(wcd9xxx->dev, "%s: Codec read 0x%x failed\n", + __func__, reg); + else + dev_dbg(wcd9xxx->dev, "%s: Read 0x%02x from 0x%x\n", + __func__, val, reg); + + mutex_unlock(&wcd9xxx->io_lock); + + if (ret < 0) + return ret; + else + return val; +} +EXPORT_SYMBOL(wcd9xxx_interface_reg_read); + +/* + * wcd9xxx_interface_reg_write: Write slim interface registers + * + * @wcd9xxx: Pointer to wcd9xxx structure + * @reg: register adderss + * @val: value of the register to be written + * + * Returns 0 for success and negative error code in case of failure + */ +int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg, + u8 val) +{ + int ret; + + mutex_lock(&wcd9xxx->io_lock); + ret = wcd9xxx->write_dev(wcd9xxx, reg, 1, (void *)&val, true); + dev_dbg(wcd9xxx->dev, "%s: Write %02x to 0x%x ret(%d)\n", + __func__, val, reg, ret); + mutex_unlock(&wcd9xxx->io_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_interface_reg_write); + +static int wcd9xxx_slim_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *dest, bool interface) +{ + int ret; + struct slim_ele_access msg; + int slim_read_tries = WCD9XXX_SLIM_RW_MAX_TRIES; + + msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg; + msg.num_bytes = bytes; + msg.comp = NULL; + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No read allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + return 0; + } + + while (1) { + mutex_lock(&wcd9xxx->xfer_lock); + ret = slim_request_val_element(interface ? + wcd9xxx->slim_slave : wcd9xxx->slim, + &msg, dest, bytes); + mutex_unlock(&wcd9xxx->xfer_lock); + if (likely(ret == 0) || (--slim_read_tries == 0)) + break; + usleep_range(5000, 5100); + } + + if (ret) + dev_err(wcd9xxx->dev, "%s: Error, Codec read failed (%d)\n", + __func__, ret); + + return ret; +} + +/* + * Interface specifies whether the write is to the interface or general + * registers. + */ +static int wcd9xxx_slim_write_device(struct wcd9xxx *wcd9xxx, + unsigned short reg, int bytes, void *src, bool interface) +{ + int ret; + struct slim_ele_access msg; + int slim_write_tries = WCD9XXX_SLIM_RW_MAX_TRIES; + + msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg; + msg.num_bytes = bytes; + msg.comp = NULL; + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + return 0; + } + + while (1) { + mutex_lock(&wcd9xxx->xfer_lock); + ret = slim_change_val_element(interface ? + wcd9xxx->slim_slave : wcd9xxx->slim, + &msg, src, bytes); + mutex_unlock(&wcd9xxx->xfer_lock); + if (likely(ret == 0) || (--slim_write_tries == 0)) + break; + usleep_range(5000, 5100); + } + + if (ret) + pr_err("%s: Error, Codec write failed (%d)\n", __func__, ret); + + return ret; +} + +static int wcd9xxx_slim_get_allowed_slice(struct wcd9xxx *wcd9xxx, + int bytes) +{ + int allowed_sz = bytes; + + if (likely(bytes == SLIM_REPEAT_WRITE_MAX_SLICE)) + allowed_sz = 16; + else if (bytes >= 12) + allowed_sz = 12; + else if (bytes >= 8) + allowed_sz = 8; + else if (bytes >= 6) + allowed_sz = 6; + else if (bytes >= 4) + allowed_sz = 4; + else + allowed_sz = bytes; + + return allowed_sz; +} + +/* + * wcd9xxx_slim_write_repeat: Write the same register with multiple values + * @wcd9xxx: handle to wcd core + * @reg: register to be written + * @bytes: number of bytes to be written to reg + * @src: buffer with data content to be written to reg + * This API will write reg with bytes from src in a single slimbus + * transaction. All values from 1 to 16 are supported by this API. + */ +int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src) +{ + int ret = 0, bytes_to_write = bytes, bytes_allowed; + struct slim_ele_access slim_msg; + + mutex_lock(&wcd9xxx->io_lock); + if (wcd9xxx->type == WCD9335 || wcd9xxx->type == WCD934X) { + ret = wcd9xxx_page_write(wcd9xxx, ®); + if (ret) + goto done; + } + + slim_msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg; + slim_msg.comp = NULL; + + if (unlikely(bytes > SLIM_REPEAT_WRITE_MAX_SLICE)) { + dev_err(wcd9xxx->dev, "%s: size %d not supported\n", + __func__, bytes); + ret = -EINVAL; + goto done; + } + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + ret = 0; + goto done; + } + + while (bytes_to_write > 0) { + bytes_allowed = wcd9xxx_slim_get_allowed_slice(wcd9xxx, + bytes_to_write); + + slim_msg.num_bytes = bytes_allowed; + mutex_lock(&wcd9xxx->xfer_lock); + ret = slim_user_msg(wcd9xxx->slim, wcd9xxx->slim->laddr, + SLIM_MSG_MT_DEST_REFERRED_USER, + SLIM_USR_MC_REPEAT_CHANGE_VALUE, + &slim_msg, src, bytes_allowed); + mutex_unlock(&wcd9xxx->xfer_lock); + + if (ret) { + dev_err(wcd9xxx->dev, "%s: failed, ret = %d\n", + __func__, ret); + break; + } + + bytes_to_write = bytes_to_write - bytes_allowed; + src = ((u8 *)src) + bytes_allowed; + } + +done: + mutex_unlock(&wcd9xxx->io_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_slim_write_repeat); + +/* + * wcd9xxx_slim_reserve_bw: API to reserve the slimbus bandwidth + * @wcd9xxx: Handle to the wcd9xxx core + * @bw_ops: value of the bandwidth that is requested + * @commit: Flag to indicate if bandwidth change is to be committed + * right away + */ +int wcd9xxx_slim_reserve_bw(struct wcd9xxx *wcd9xxx, + u32 bw_ops, bool commit) +{ + if (!wcd9xxx || !wcd9xxx->slim) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!wcd9xxx) ? "wcd9xxx" : "slim_device"); + return -EINVAL; + } + + return slim_reservemsg_bw(wcd9xxx->slim, bw_ops, commit); +} +EXPORT_SYMBOL(wcd9xxx_slim_reserve_bw); + +/* + * wcd9xxx_slim_bulk_write: API to write multiple registers with one descriptor + * @wcd9xxx: Handle to the wcd9xxx core + * @wcd9xxx_reg_val: structure holding register and values to be written + * @size: Indicates number of messages to be written with one descriptor + * @is_interface: Indicates whether the register is for slim interface or for + * general registers. + * @return: returns 0 if success or error information to the caller in case + * of failure. + */ +int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + unsigned int size, bool is_interface) +{ + int ret, i; + struct slim_val_inf *msgs; + unsigned short reg; + + if (!bulk_reg || !size || !wcd9xxx) { + pr_err("%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + if (!wcd9xxx->dev_up) { + dev_dbg_ratelimited( + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + __func__, wcd9xxx->dev_up); + return 0; + } + + msgs = kzalloc(size * (sizeof(struct slim_val_inf)), GFP_KERNEL); + if (!msgs) { + ret = -ENOMEM; + goto mem_fail; + } + + mutex_lock(&wcd9xxx->io_lock); + reg = bulk_reg->reg; + for (i = 0; i < size; i++) { + msgs[i].start_offset = WCD9XXX_REGISTER_START_OFFSET + + (bulk_reg->reg & 0xFF); + msgs[i].num_bytes = bulk_reg->bytes; + msgs[i].wbuf = bulk_reg->buf; + bulk_reg++; + } + ret = wcd9xxx_page_write(wcd9xxx, ®); + if (ret) { + pr_err("%s: Page write error for reg: 0x%x\n", + __func__, reg); + goto err; + } + + ret = slim_bulk_msg_write(is_interface ? + wcd9xxx->slim_slave : wcd9xxx->slim, + SLIM_MSG_MT_CORE, + SLIM_MSG_MC_CHANGE_VALUE, msgs, size, + NULL, NULL); + if (ret) + pr_err("%s: Error, Codec bulk write failed (%d)\n", + __func__, ret); + /* 100 usec sleep is needed as per HW requirement */ + usleep_range(100, 110); +err: + mutex_unlock(&wcd9xxx->io_lock); + kfree(msgs); +mem_fail: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_slim_bulk_write); + +static int wcd9xxx_num_irq_regs(const struct wcd9xxx *wcd9xxx) +{ + return (wcd9xxx->codec_type->num_irqs / 8) + + ((wcd9xxx->codec_type->num_irqs % 8) ? 1 : 0); +} + +static int wcd9xxx_regmap_init_cache(struct wcd9xxx *wcd9xxx) +{ + struct regmap_config *regmap_config; + int rc; + + regmap_config = wcd9xxx_get_regmap_config(wcd9xxx->type); + if (!regmap_config) { + dev_err(wcd9xxx->dev, "regmap config is not defined\n"); + return -EINVAL; + } + + rc = regmap_reinit_cache(wcd9xxx->regmap, regmap_config); + if (rc != 0) { + dev_err(wcd9xxx->dev, "%s:Failed to reinit register cache: %d\n", + __func__, rc); + } + + return rc; +} + +static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx) +{ + int ret = 0, i; + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + regmap_patch_fptr regmap_apply_patch = NULL; + + mutex_init(&wcd9xxx->io_lock); + mutex_init(&wcd9xxx->xfer_lock); + mutex_init(&wcd9xxx->reset_lock); + + ret = wcd9xxx_bringup(wcd9xxx->dev); + if (ret) { + ret = -EPROBE_DEFER; + goto err_bring_up; + } + + wcd9xxx->codec_type = devm_kzalloc(wcd9xxx->dev, + sizeof(struct wcd9xxx_codec_type), GFP_KERNEL); + if (!wcd9xxx->codec_type) { + ret = -ENOMEM; + goto err_bring_up; + } + ret = wcd9xxx_get_codec_info(wcd9xxx->dev); + if (ret) { + ret = -EPROBE_DEFER; + goto fail_cdc_fill; + } + wcd9xxx->version = wcd9xxx->codec_type->version; + if (!wcd9xxx->codec_type->dev || !wcd9xxx->codec_type->size) + goto fail_cdc_fill; + + core_res->parent = wcd9xxx; + core_res->dev = wcd9xxx->dev; + core_res->intr_table = wcd9xxx->codec_type->intr_tbl; + core_res->intr_table_size = wcd9xxx->codec_type->intr_tbl_size; + + for (i = 0; i < WCD9XXX_INTR_REG_MAX; i++) + wcd9xxx->core_res.intr_reg[i] = + wcd9xxx->codec_type->intr_reg[i]; + + wcd9xxx_core_res_init(&wcd9xxx->core_res, + wcd9xxx->codec_type->num_irqs, + wcd9xxx_num_irq_regs(wcd9xxx), + wcd9xxx->regmap); + + if (wcd9xxx_core_irq_init(&wcd9xxx->core_res)) + goto err; + + ret = wcd9xxx_regmap_init_cache(wcd9xxx); + if (ret) + goto err_irq; + + regmap_apply_patch = wcd9xxx_get_regmap_reg_patch( + wcd9xxx->type); + if (regmap_apply_patch) { + ret = regmap_apply_patch(wcd9xxx->regmap, + wcd9xxx->version); + if (ret) + dev_err(wcd9xxx->dev, + "Failed to register patch: %d\n", ret); + } + + ret = mfd_add_devices(wcd9xxx->dev, -1, wcd9xxx->codec_type->dev, + wcd9xxx->codec_type->size, NULL, 0, NULL); + if (ret != 0) { + dev_err(wcd9xxx->dev, "Failed to add children: %d\n", ret); + goto err_irq; + } + + ret = device_init_wakeup(wcd9xxx->dev, true); + if (ret) { + dev_err(wcd9xxx->dev, "Device wakeup init failed: %d\n", ret); + goto err_irq; + } + + return ret; +err_irq: + wcd9xxx_irq_exit(&wcd9xxx->core_res); +fail_cdc_fill: + devm_kfree(wcd9xxx->dev, wcd9xxx->codec_type); + wcd9xxx->codec_type = NULL; +err: + wcd9xxx_bringdown(wcd9xxx->dev); + wcd9xxx_core_res_deinit(&wcd9xxx->core_res); +err_bring_up: + mutex_destroy(&wcd9xxx->io_lock); + mutex_destroy(&wcd9xxx->xfer_lock); + mutex_destroy(&wcd9xxx->reset_lock); + return ret; +} + +static void wcd9xxx_device_exit(struct wcd9xxx *wcd9xxx) +{ + device_init_wakeup(wcd9xxx->dev, false); + wcd9xxx_irq_exit(&wcd9xxx->core_res); + wcd9xxx_bringdown(wcd9xxx->dev); + wcd9xxx_reset_low(wcd9xxx->dev); + wcd9xxx_core_res_deinit(&wcd9xxx->core_res); + mutex_destroy(&wcd9xxx->io_lock); + mutex_destroy(&wcd9xxx->xfer_lock); + mutex_destroy(&wcd9xxx->reset_lock); + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + slim_remove_device(wcd9xxx->slim_slave); +} + + +#ifdef CONFIG_DEBUG_FS +struct wcd9xxx *debugCodec; + +static struct dentry *debugfs_wcd9xxx_dent; +static struct dentry *debugfs_peek; +static struct dentry *debugfs_poke; +static struct dentry *debugfs_power_state; +static struct dentry *debugfs_reg_dump; + +static unsigned char read_data; + +static int codec_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int 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) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + return 0; +} + +static ssize_t wcd9xxx_slimslave_reg_show(char __user *ubuf, size_t count, + loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[25]; /* each line is 12 bytes but 25 for margin of error */ + + for (i = (int) *ppos / 12; i <= SLIM_MAX_REG_ADDR; i++) { + reg_val = wcd9xxx_interface_reg_read(debugCodec, i); + len = snprintf(tmp_buf, sizeof(tmp_buf), + "0x%.3x: 0x%.2x\n", i, reg_val); + + if ((total + len) >= count - 1) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + pr_err("%s: fail to copy reg dump\n", __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t codec_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char lbuf[8]; + char *access_str = file->private_data; + ssize_t ret_cnt; + + if (*ppos < 0 || !count) + return -EINVAL; + + if (!strcmp(access_str, "slimslave_peek")) { + snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data); + ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf, + strnlen(lbuf, 7)); + } else if (!strcmp(access_str, "slimslave_reg_dump")) { + ret_cnt = wcd9xxx_slimslave_reg_show(ubuf, count, ppos); + } else { + pr_err("%s: %s not permitted to read\n", __func__, access_str); + ret_cnt = -EPERM; + } + + return ret_cnt; +} + +static void wcd9xxx_set_reset_pin_state(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_pdata *pdata, + bool active) +{ + if (wcd9xxx->wcd_rst_np) { + if (active) + msm_cdc_pinctrl_select_active_state( + wcd9xxx->wcd_rst_np); + else + msm_cdc_pinctrl_select_sleep_state( + wcd9xxx->wcd_rst_np); + + return; + } else if (gpio_is_valid(wcd9xxx->reset_gpio)) { + gpio_direction_output(wcd9xxx->reset_gpio, + (active == true ? 1 : 0)); + } +} + +static int codec_debug_process_cdc_power(char *lbuf) +{ + long int param; + int rc; + struct wcd9xxx_pdata *pdata; + + if (wcd9xxx_get_intf_type() != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + pr_err("%s: CODEC is not in SLIMBUS mode\n", __func__); + rc = -EPERM; + goto error_intf; + } + + rc = get_parameters(lbuf, ¶m, 1); + + if (likely(!rc)) { + pdata = debugCodec->slim->dev.platform_data; + if (param == 0) { + wcd9xxx_slim_device_down(debugCodec->slim); + msm_cdc_disable_static_supplies(debugCodec->dev, + debugCodec->supplies, + pdata->regulator, + pdata->num_supplies); + wcd9xxx_set_reset_pin_state(debugCodec, pdata, false); + } else if (param == 1) { + msm_cdc_enable_static_supplies(debugCodec->dev, + debugCodec->supplies, + pdata->regulator, + pdata->num_supplies); + usleep_range(1000, 2000); + wcd9xxx_set_reset_pin_state(debugCodec, pdata, false); + usleep_range(1000, 2000); + wcd9xxx_set_reset_pin_state(debugCodec, pdata, true); + usleep_range(1000, 2000); + wcd9xxx_slim_device_up(debugCodec->slim); + } else { + pr_err("%s: invalid command %ld\n", __func__, param); + } + } + +error_intf: + return rc; +} + +static ssize_t codec_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char *access_str = filp->private_data; + char lbuf[32]; + int rc; + long int param[5]; + + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + + if (!strcmp(access_str, "slimslave_poke")) { + /* write */ + rc = get_parameters(lbuf, param, 2); + if ((param[0] <= 0x3FF) && (param[1] <= 0xFF) && + (rc == 0)) + wcd9xxx_interface_reg_write(debugCodec, param[0], + param[1]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "slimslave_peek")) { + /* read */ + rc = get_parameters(lbuf, param, 1); + if ((param[0] <= 0x3FF) && (rc == 0)) + read_data = wcd9xxx_interface_reg_read(debugCodec, + param[0]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "power_state")) { + rc = codec_debug_process_cdc_power(lbuf); + } + + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations codec_debug_ops = { + .open = codec_debug_open, + .write = codec_debug_write, + .read = codec_debug_read +}; +#endif + +static struct wcd9xxx_i2c *wcd9xxx_i2c_get_device_info(struct wcd9xxx *wcd9xxx, + u16 reg) +{ + u16 mask = 0x0f00; + int value = 0; + struct wcd9xxx_i2c *wcd9xxx_i2c = NULL; + + if (wcd9xxx->type == WCD9335) { + wcd9xxx_i2c = &wcd9xxx_modules[0]; + } else { + value = ((reg & mask) >> 8) & 0x000f; + switch (value) { + case 0: + wcd9xxx_i2c = &wcd9xxx_modules[0]; + break; + case 1: + wcd9xxx_i2c = &wcd9xxx_modules[1]; + break; + case 2: + wcd9xxx_i2c = &wcd9xxx_modules[2]; + break; + case 3: + wcd9xxx_i2c = &wcd9xxx_modules[3]; + break; + + default: + break; + } + } + return wcd9xxx_i2c; +} + +static int wcd9xxx_i2c_write_device(struct wcd9xxx *wcd9xxx, u16 reg, u8 *value, + u32 bytes) +{ + + struct i2c_msg *msg; + int ret = 0; + u8 reg_addr = 0; + u8 data[bytes + 1]; + struct wcd9xxx_i2c *wcd9xxx_i2c; + + wcd9xxx_i2c = wcd9xxx_i2c_get_device_info(wcd9xxx, reg); + if (wcd9xxx_i2c == NULL || wcd9xxx_i2c->client == NULL) { + pr_err("failed to get device info\n"); + return -ENODEV; + } + reg_addr = (u8)reg; + msg = &wcd9xxx_i2c->xfer_msg[0]; + msg->addr = wcd9xxx_i2c->client->addr; + msg->len = bytes + 1; + msg->flags = 0; + data[0] = reg; + data[1] = *value; + msg->buf = data; + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 1); + /* Try again if the write fails */ + if (ret != 1) { + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 1); + if (ret != 1) { + pr_err("failed to write the device\n"); + return ret; + } + } + pr_debug("write success register = %x val = %x\n", reg, data[1]); + return 0; +} + + +static int wcd9xxx_i2c_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, unsigned char *dest) +{ + struct i2c_msg *msg; + int ret = 0; + u8 reg_addr = 0; + struct wcd9xxx_i2c *wcd9xxx_i2c; + u8 i = 0; + + wcd9xxx_i2c = wcd9xxx_i2c_get_device_info(wcd9xxx, reg); + if (wcd9xxx_i2c == NULL || wcd9xxx_i2c->client == NULL) { + pr_err("failed to get device info\n"); + return -ENODEV; + } + for (i = 0; i < bytes; i++) { + reg_addr = (u8)reg++; + msg = &wcd9xxx_i2c->xfer_msg[0]; + msg->addr = wcd9xxx_i2c->client->addr; + msg->len = 1; + msg->flags = 0; + msg->buf = ®_addr; + + msg = &wcd9xxx_i2c->xfer_msg[1]; + msg->addr = wcd9xxx_i2c->client->addr; + msg->len = 1; + msg->flags = I2C_M_RD; + msg->buf = dest++; + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 2); + + /* Try again if read fails first time */ + if (ret != 2) { + ret = i2c_transfer(wcd9xxx_i2c->client->adapter, + wcd9xxx_i2c->xfer_msg, 2); + if (ret != 2) { + pr_err("failed to read wcd9xxx register\n"); + return ret; + } + } + } + return 0; +} + +int wcd9xxx_i2c_read(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *dest, bool interface_reg) +{ + return wcd9xxx_i2c_read_device(wcd9xxx, reg, bytes, dest); +} + +int wcd9xxx_i2c_write(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src, bool interface_reg) +{ + return wcd9xxx_i2c_write_device(wcd9xxx, reg, src, bytes); +} + +static int wcd9xxx_i2c_get_client_index(struct i2c_client *client, + int *wcd9xx_index) +{ + int ret = 0; + + switch (client->addr) { + case WCD9XXX_I2C_TOP_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_TOP_LEVEL; + break; + case WCD9XXX_ANALOG_I2C_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_ANALOG; + break; + case WCD9XXX_DIGITAL1_I2C_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_DIGITAL_1; + break; + case WCD9XXX_DIGITAL2_I2C_SLAVE_ADDR: + *wcd9xx_index = WCD9XXX_I2C_DIGITAL_2; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int wcd9xxx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wcd9xxx *wcd9xxx = NULL; + struct wcd9xxx_pdata *pdata = NULL; + int val = 0; + int ret = 0; + int wcd9xx_index = 0; + struct device *dev; + int intf_type; + const struct of_device_id *of_id; + + intf_type = wcd9xxx_get_intf_type(); + + pr_debug("%s: interface status %d\n", __func__, intf_type); + if (intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + dev_dbg(&client->dev, "%s:Codec is detected in slimbus mode\n", + __func__); + return -ENODEV; + } else if (intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = wcd9xxx_i2c_get_client_index(client, &wcd9xx_index); + if (ret != 0) + dev_err(&client->dev, "%s: I2C set codec I2C\n" + "client failed\n", __func__); + else { + dev_err(&client->dev, "%s:probe for other slaves\n" + "devices of codec I2C slave Addr = %x\n", + __func__, client->addr); + wcd9xxx_modules[wcd9xx_index].client = client; + } + return ret; + } else if (intf_type == WCD9XXX_INTERFACE_TYPE_PROBING) { + dev = &client->dev; + if (client->dev.of_node) { + dev_dbg(&client->dev, "%s:Platform data\n" + "from device tree\n", __func__); + pdata = wcd9xxx_populate_dt_data(&client->dev); + if (!pdata) { + dev_err(&client->dev, + "%s: Fail to obtain pdata from device tree\n", + __func__); + ret = -EINVAL; + goto fail; + } + client->dev.platform_data = pdata; + } else { + dev_dbg(&client->dev, "%s:Platform data from\n" + "board file\n", __func__); + pdata = client->dev.platform_data; + } + wcd9xxx = devm_kzalloc(&client->dev, sizeof(struct wcd9xxx), + GFP_KERNEL); + if (!wcd9xxx) { + ret = -ENOMEM; + goto fail; + } + + if (!pdata) { + dev_dbg(&client->dev, "no platform data?\n"); + ret = -EINVAL; + goto fail; + } + wcd9xxx->type = WCD9XXX; + if (client->dev.of_node) { + of_id = of_match_device(wcd9xxx_of_match, &client->dev); + if (of_id) { + wcd9xxx->type = *((int *)of_id->data); + dev_info(&client->dev, "%s: codec type is %d\n", + __func__, wcd9xxx->type); + } + } else { + dev_info(&client->dev, "%s: dev.of_node is NULL, default to WCD9XXX\n", + __func__); + wcd9xxx->type = WCD9XXX; + } + wcd9xxx->regmap = wcd9xxx_regmap_init(&client->dev, + &wcd9xxx_i2c_base_regmap_config); + if (IS_ERR(wcd9xxx->regmap)) { + ret = PTR_ERR(wcd9xxx->regmap); + dev_err(&client->dev, "%s: Failed to allocate register map: %d\n", + __func__, ret); + goto err_codec; + } + wcd9xxx->reset_gpio = pdata->reset_gpio; + wcd9xxx->wcd_rst_np = pdata->wcd_rst_np; + + if (!wcd9xxx->wcd_rst_np) { + pdata->use_pinctrl = false; + dev_err(&client->dev, "%s: pinctrl not used for rst_n\n", + __func__); + goto err_codec; + } + + if (i2c_check_functionality(client->adapter, + I2C_FUNC_I2C) == 0) { + dev_dbg(&client->dev, "can't talk I2C?\n"); + ret = -EIO; + goto fail; + } + dev_set_drvdata(&client->dev, wcd9xxx); + wcd9xxx->dev = &client->dev; + wcd9xxx->dev_up = true; + if (client->dev.of_node) + wcd9xxx->mclk_rate = pdata->mclk_rate; + + wcd9xxx->num_of_supplies = pdata->num_supplies; + ret = msm_cdc_init_supplies(wcd9xxx->dev, &wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (!wcd9xxx->supplies) { + dev_err(wcd9xxx->dev, "%s: Cannot init wcd supplies\n", + __func__); + goto err_codec; + } + ret = msm_cdc_enable_static_supplies(wcd9xxx->dev, + wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (ret) { + dev_err(wcd9xxx->dev, "%s: wcd static supply enable failed!\n", + __func__); + goto err_codec; + } + /* For WCD9335, it takes about 600us for the Vout_A and + * Vout_D to be ready after BUCK_SIDO is powered up\ + * SYS_RST_N shouldn't be pulled high during this time + */ + if (wcd9xxx->type == WCD9335) + usleep_range(600, 650); + else + usleep_range(5, 10); + + ret = wcd9xxx_reset(wcd9xxx->dev); + if (ret) { + pr_err("%s: Resetting Codec failed\n", __func__); + goto err_supplies; + } + + ret = wcd9xxx_i2c_get_client_index(client, &wcd9xx_index); + if (ret != 0) { + pr_err("%s:Set codec I2C client failed\n", __func__); + goto err_supplies; + } + + wcd9xxx_modules[wcd9xx_index].client = client; + wcd9xxx->read_dev = wcd9xxx_i2c_read; + wcd9xxx->write_dev = wcd9xxx_i2c_write; + if (!wcd9xxx->dev->of_node) + wcd9xxx_assign_irq(&wcd9xxx->core_res, + pdata->irq, pdata->irq_base); + + ret = wcd9xxx_device_init(wcd9xxx); + if (ret) { + pr_err("%s: error, initializing device failed (%d)\n", + __func__, ret); + goto err_device_init; + } + + ret = wcd9xxx_i2c_read(wcd9xxx, WCD9XXX_A_CHIP_STATUS, 1, + &val, 0); + if (ret < 0) + pr_err("%s: failed to read the wcd9xxx status (%d)\n", + __func__, ret); + if (val != wcd9xxx->codec_type->i2c_chip_status) + pr_err("%s: unknown chip status 0x%x\n", __func__, val); + + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_I2C); + + return ret; + } + + pr_err("%s: I2C probe in wrong state\n", __func__); + + +err_device_init: + wcd9xxx_reset_low(wcd9xxx->dev); +err_supplies: + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + pdata->regulator = NULL; + pdata->num_supplies = 0; +err_codec: + devm_kfree(&client->dev, wcd9xxx); + dev_set_drvdata(&client->dev, NULL); +fail: + return ret; +} + +static int wcd9xxx_i2c_remove(struct i2c_client *client) +{ + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_pdata *pdata = client->dev.platform_data; + + wcd9xxx = dev_get_drvdata(&client->dev); + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + wcd9xxx_device_exit(wcd9xxx); + dev_set_drvdata(&client->dev, NULL); + return 0; +} + +static int wcd9xxx_dt_parse_slim_interface_dev_info(struct device *dev, + struct slim_device *slim_ifd) +{ + int ret = 0; + struct property *prop; + + ret = of_property_read_string(dev->of_node, "qcom,cdc-slim-ifd", + &slim_ifd->name); + if (ret) { + dev_err(dev, "Looking up %s property in node %s failed", + "qcom,cdc-slim-ifd-dev", dev->of_node->full_name); + return -ENODEV; + } + prop = of_find_property(dev->of_node, + "qcom,cdc-slim-ifd-elemental-addr", NULL); + if (!prop) { + dev_err(dev, "Looking up %s property in node %s failed", + "qcom,cdc-slim-ifd-elemental-addr", + dev->of_node->full_name); + return -ENODEV; + } else if (prop->length != 6) { + dev_err(dev, "invalid codec slim ifd addr. addr length = %d\n", + prop->length); + return -ENODEV; + } + memcpy(slim_ifd->e_addr, prop->value, 6); + + return 0; +} + +static int wcd9xxx_slim_get_laddr(struct slim_device *sb, + const u8 *e_addr, u8 e_len, u8 *laddr) +{ + int ret; + const unsigned long timeout = jiffies + + msecs_to_jiffies(SLIMBUS_PRESENT_TIMEOUT); + + do { + ret = slim_get_logical_addr(sb, e_addr, e_len, laddr); + if (!ret) + break; + /* Give SLIMBUS time to report present and be ready. */ + usleep_range(1000, 1100); + pr_debug_ratelimited("%s: retyring get logical addr\n", + __func__); + } while time_before(jiffies, timeout); + + return ret; +} + +static int wcd9xxx_slim_probe(struct slim_device *slim) +{ + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_pdata *pdata; + const struct slim_device_id *device_id; + int ret = 0; + int intf_type; + + intf_type = wcd9xxx_get_intf_type(); + + wcd9xxx = devm_kzalloc(&slim->dev, sizeof(struct wcd9xxx), + GFP_KERNEL); + if (!wcd9xxx) { + ret = -ENOMEM; + goto err; + } + + if (!slim) { + ret = -EINVAL; + goto err; + } + + if (intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + dev_dbg(&slim->dev, "%s:Codec is detected in I2C mode\n", + __func__); + ret = -ENODEV; + goto err; + } + if (slim->dev.of_node) { + dev_info(&slim->dev, "Platform data from device tree\n"); + pdata = wcd9xxx_populate_dt_data(&slim->dev); + if (!pdata) { + dev_err(&slim->dev, + "%s: Fail to obtain pdata from device tree\n", + __func__); + ret = -EINVAL; + goto err; + } + + ret = wcd9xxx_dt_parse_slim_interface_dev_info(&slim->dev, + &pdata->slimbus_slave_device); + if (ret) { + dev_err(&slim->dev, "Error, parsing slim interface\n"); + devm_kfree(&slim->dev, pdata); + ret = -EINVAL; + goto err; + } + slim->dev.platform_data = pdata; + + } else { + dev_info(&slim->dev, "Platform data from board file\n"); + pdata = slim->dev.platform_data; + } + + if (!pdata) { + dev_err(&slim->dev, "Error, no platform data\n"); + ret = -EINVAL; + goto err; + } + + if (!slim->ctrl) { + dev_err(&slim->dev, "%s: Error, no SLIMBUS control data\n", + __func__); + ret = -EINVAL; + goto err_codec; + } + device_id = slim_get_device_id(slim); + if (!device_id) { + dev_err(&slim->dev, "%s: Error, no device id\n", __func__); + ret = -EINVAL; + goto err; + } + + wcd9xxx->type = device_id->driver_data; + dev_info(&slim->dev, "%s: probing for wcd type: %d, name: %s\n", + __func__, wcd9xxx->type, device_id->name); + + /* wcd9xxx members init */ + wcd9xxx->multi_reg_write = wcd9xxx_slim_multi_reg_write; + wcd9xxx->slim = slim; + slim_set_clientdata(slim, wcd9xxx); + wcd9xxx->reset_gpio = pdata->reset_gpio; + wcd9xxx->dev = &slim->dev; + wcd9xxx->mclk_rate = pdata->mclk_rate; + wcd9xxx->dev_up = true; + wcd9xxx->wcd_rst_np = pdata->wcd_rst_np; + + wcd9xxx->regmap = wcd9xxx_regmap_init(&slim->dev, + &wcd9xxx_base_regmap_config); + if (IS_ERR(wcd9xxx->regmap)) { + ret = PTR_ERR(wcd9xxx->regmap); + dev_err(&slim->dev, "%s: Failed to allocate register map: %d\n", + __func__, ret); + goto err_codec; + } + + if (!wcd9xxx->wcd_rst_np) { + pdata->use_pinctrl = false; + dev_err(&slim->dev, "%s: pinctrl not used for rst_n\n", + __func__); + goto err_codec; + } + + wcd9xxx->num_of_supplies = pdata->num_supplies; + ret = msm_cdc_init_supplies(&slim->dev, &wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (!wcd9xxx->supplies) { + dev_err(wcd9xxx->dev, "%s: Cannot init wcd supplies\n", + __func__); + goto err_codec; + } + ret = msm_cdc_enable_static_supplies(wcd9xxx->dev, + wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + if (ret) { + dev_err(wcd9xxx->dev, "%s: wcd static supply enable failed!\n", + __func__); + goto err_codec; + } + + /* + * For WCD9335, it takes about 600us for the Vout_A and + * Vout_D to be ready after BUCK_SIDO is powered up. + * SYS_RST_N shouldn't be pulled high during this time + */ + if (wcd9xxx->type == WCD9335 || wcd9xxx->type == WCD934X) + usleep_range(600, 650); + else + usleep_range(5, 10); + + ret = wcd9xxx_reset(&slim->dev); + if (ret) { + dev_err(&slim->dev, "%s: Resetting Codec failed\n", __func__); + goto err_supplies; + } + + ret = wcd9xxx_slim_get_laddr(wcd9xxx->slim, wcd9xxx->slim->e_addr, + ARRAY_SIZE(wcd9xxx->slim->e_addr), + &wcd9xxx->slim->laddr); + if (ret) { + dev_err(&slim->dev, "%s: failed to get slimbus %s logical address: %d\n", + __func__, wcd9xxx->slim->name, ret); + goto err_reset; + } + wcd9xxx->read_dev = wcd9xxx_slim_read_device; + wcd9xxx->write_dev = wcd9xxx_slim_write_device; + wcd9xxx_pgd_la = wcd9xxx->slim->laddr; + wcd9xxx->slim_slave = &pdata->slimbus_slave_device; + if (!wcd9xxx->dev->of_node) + wcd9xxx_assign_irq(&wcd9xxx->core_res, + pdata->irq, pdata->irq_base); + + ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave); + if (ret) { + dev_err(&slim->dev, "%s: error, adding SLIMBUS device failed\n", + __func__); + goto err_reset; + } + + ret = wcd9xxx_slim_get_laddr(wcd9xxx->slim_slave, + wcd9xxx->slim_slave->e_addr, + ARRAY_SIZE(wcd9xxx->slim_slave->e_addr), + &wcd9xxx->slim_slave->laddr); + if (ret) { + dev_err(&slim->dev, "%s: failed to get slimbus %s logical address: %d\n", + __func__, wcd9xxx->slim->name, ret); + goto err_slim_add; + } + wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr; + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_SLIMBUS); + + ret = wcd9xxx_device_init(wcd9xxx); + if (ret) { + dev_err(&slim->dev, "%s: error, initializing device failed (%d)\n", + __func__, ret); + goto err_slim_add; + } +#ifdef CONFIG_DEBUG_FS + debugCodec = wcd9xxx; + + debugfs_wcd9xxx_dent = debugfs_create_dir + ("wcd9xxx_core", 0); + if (!IS_ERR(debugfs_wcd9xxx_dent)) { + debugfs_peek = debugfs_create_file("slimslave_peek", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "slimslave_peek", &codec_debug_ops); + + debugfs_poke = debugfs_create_file("slimslave_poke", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "slimslave_poke", &codec_debug_ops); + + debugfs_power_state = debugfs_create_file("power_state", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "power_state", &codec_debug_ops); + + debugfs_reg_dump = debugfs_create_file("slimslave_reg_dump", + S_IFREG | 0444, debugfs_wcd9xxx_dent, + (void *) "slimslave_reg_dump", &codec_debug_ops); + } +#endif + + return ret; + +err_slim_add: + slim_remove_device(wcd9xxx->slim_slave); +err_reset: + wcd9xxx_reset_low(wcd9xxx->dev); +err_supplies: + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); +err_codec: + slim_set_clientdata(slim, NULL); +err: + devm_kfree(&slim->dev, wcd9xxx); + return ret; +} +static int wcd9xxx_slim_remove(struct slim_device *pdev) +{ + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_pdata *pdata = pdev->dev.platform_data; + +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(debugfs_wcd9xxx_dent); +#endif + wcd9xxx = slim_get_devicedata(pdev); + wcd9xxx_deinit_slimslave(wcd9xxx); + slim_remove_device(wcd9xxx->slim_slave); + msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies, + pdata->regulator, + pdata->num_supplies); + wcd9xxx_device_exit(wcd9xxx); + slim_set_clientdata(pdev, NULL); + return 0; +} + +static int wcd9xxx_device_up(struct wcd9xxx *wcd9xxx) +{ + int ret = 0; + struct wcd9xxx_core_resource *wcd9xxx_res = &wcd9xxx->core_res; + + dev_info(wcd9xxx->dev, "%s: codec bring up\n", __func__); + wcd9xxx_bringup(wcd9xxx->dev); + ret = wcd9xxx_irq_init(wcd9xxx_res); + if (ret) { + pr_err("%s: wcd9xx_irq_init failed : %d\n", __func__, ret); + } else { + if (wcd9xxx->post_reset) + ret = wcd9xxx->post_reset(wcd9xxx); + } + return ret; +} + +static int wcd9xxx_slim_device_reset(struct slim_device *sldev) +{ + int ret; + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + dev_info(wcd9xxx->dev, "%s: device reset, dev_up = %d\n", + __func__, wcd9xxx->dev_up); + if (wcd9xxx->dev_up) + return 0; + + mutex_lock(&wcd9xxx->reset_lock); + ret = wcd9xxx_reset(wcd9xxx->dev); + if (ret) + dev_err(wcd9xxx->dev, "%s: Resetting Codec failed\n", __func__); + mutex_unlock(&wcd9xxx->reset_lock); + + return ret; +} + +static int wcd9xxx_slim_device_up(struct slim_device *sldev) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + int ret = 0; + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + dev_info(wcd9xxx->dev, "%s: slim device up, dev_up = %d\n", + __func__, wcd9xxx->dev_up); + if (wcd9xxx->dev_up) + return 0; + + wcd9xxx->dev_up = true; + + mutex_lock(&wcd9xxx->reset_lock); + ret = wcd9xxx_device_up(wcd9xxx); + mutex_unlock(&wcd9xxx->reset_lock); + + return ret; +} + +static int wcd9xxx_slim_device_down(struct slim_device *sldev) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + dev_info(wcd9xxx->dev, "%s: device down, dev_up = %d\n", + __func__, wcd9xxx->dev_up); + if (!wcd9xxx->dev_up) + return 0; + + wcd9xxx->dev_up = false; + + mutex_lock(&wcd9xxx->reset_lock); + if (wcd9xxx->dev_down) + wcd9xxx->dev_down(wcd9xxx); + wcd9xxx_irq_exit(&wcd9xxx->core_res); + wcd9xxx_reset_low(wcd9xxx->dev); + mutex_unlock(&wcd9xxx->reset_lock); + + return 0; +} + +static int wcd9xxx_slim_resume(struct slim_device *sldev) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + return wcd9xxx_core_res_resume(&wcd9xxx->core_res); +} + +static int wcd9xxx_i2c_resume(struct device *dev) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + + if (wcd9xxx) + return wcd9xxx_core_res_resume(&wcd9xxx->core_res); + else + return 0; +} + +static int wcd9xxx_slim_suspend(struct slim_device *sldev, pm_message_t pmesg) +{ + struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev); + + return wcd9xxx_core_res_suspend(&wcd9xxx->core_res, pmesg); +} + +static int wcd9xxx_i2c_suspend(struct device *dev) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + pm_message_t pmesg = {0}; + + if (wcd9xxx) + return wcd9xxx_core_res_suspend(&wcd9xxx->core_res, pmesg); + else + return 0; +} + +static const struct slim_device_id wcd_slim_device_id[] = { + {"sitar-slim", 0}, + {"sitar1p1-slim", 0}, + {"tabla-slim", 0}, + {"tabla2x-slim", 0}, + {"taiko-slim-pgd", 0}, + {"tapan-slim-pgd", 0}, + {"tomtom-slim-pgd", WCD9330}, + {"tasha-slim-pgd", WCD9335}, + {"tavil-slim-pgd", WCD934X}, + {} +}; + +static struct slim_driver wcd_slim_driver = { + .driver = { + .name = "wcd-slim", + .owner = THIS_MODULE, + }, + .probe = wcd9xxx_slim_probe, + .remove = wcd9xxx_slim_remove, + .id_table = wcd_slim_device_id, + .resume = wcd9xxx_slim_resume, + .suspend = wcd9xxx_slim_suspend, + .device_up = wcd9xxx_slim_device_up, + .reset_device = wcd9xxx_slim_device_reset, + .device_down = wcd9xxx_slim_device_down, +}; + +static struct i2c_device_id wcd9xxx_id_table[] = { + {"wcd9xxx-i2c", WCD9XXX_I2C_TOP_LEVEL}, + {"wcd9xxx-i2c", WCD9XXX_I2C_ANALOG}, + {"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_1}, + {"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_2}, + {} +}; + +static struct i2c_device_id tasha_id_table[] = { + {"tasha-i2c-pgd", WCD9XXX_I2C_TOP_LEVEL}, + {} +}; + +static struct i2c_device_id tabla_id_table[] = { + {"tabla top level", WCD9XXX_I2C_TOP_LEVEL}, + {"tabla analog", WCD9XXX_I2C_ANALOG}, + {"tabla digital1", WCD9XXX_I2C_DIGITAL_1}, + {"tabla digital2", WCD9XXX_I2C_DIGITAL_2}, + {} +}; +MODULE_DEVICE_TABLE(i2c, tabla_id_table); + +static const struct dev_pm_ops wcd9xxx_i2c_pm_ops = { + .suspend = wcd9xxx_i2c_suspend, + .resume = wcd9xxx_i2c_resume, +}; + +static struct i2c_driver tabla_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tabla-i2c-core", + .pm = &wcd9xxx_i2c_pm_ops, + }, + .id_table = tabla_id_table, + .probe = wcd9xxx_i2c_probe, + .remove = wcd9xxx_i2c_remove, +}; + +static struct i2c_driver wcd9xxx_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "wcd9xxx-i2c-core", + .pm = &wcd9xxx_i2c_pm_ops, + }, + .id_table = wcd9xxx_id_table, + .probe = wcd9xxx_i2c_probe, + .remove = wcd9xxx_i2c_remove, +}; + +static struct i2c_driver wcd9335_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tasha-i2c-core", + .pm = &wcd9xxx_i2c_pm_ops, + }, + .id_table = tasha_id_table, + .probe = wcd9xxx_i2c_probe, + .remove = wcd9xxx_i2c_remove, +}; + +int wcd9xxx_init(void) +{ + int ret[NUM_WCD9XXX_REG_RET] = {0}; + int i = 0; + + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_PROBING); + + ret[0] = i2c_add_driver(&tabla_i2c_driver); + if (ret[0]) + pr_err("%s: Failed to add the tabla2x I2C driver: %d\n", + __func__, ret[0]); + + ret[1] = i2c_add_driver(&wcd9xxx_i2c_driver); + if (ret[1]) + pr_err("%s: Failed to add the wcd9xxx I2C driver: %d\n", + __func__, ret[1]); + + ret[2] = i2c_add_driver(&wcd9335_i2c_driver); + if (ret[2]) + pr_err("%s: Failed to add the wcd9335 I2C driver: %d\n", + __func__, ret[2]); + + ret[3] = slim_driver_register(&wcd_slim_driver); + if (ret[3]) + pr_err("%s: Failed to register wcd SB driver: %d\n", + __func__, ret[3]); + + for (i = 0; i < NUM_WCD9XXX_REG_RET; i++) { + if (ret[i]) + return ret[i]; + } + + return 0; +} + +void wcd9xxx_exit(void) +{ + wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_PROBING); + + i2c_del_driver(&tabla_i2c_driver); + i2c_del_driver(&wcd9xxx_i2c_driver); + i2c_del_driver(&wcd9335_i2c_driver); + slim_driver_unregister(&wcd_slim_driver); +} + +MODULE_DESCRIPTION("Codec core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c new file mode 100644 index 000000000000..092f44632e1b --- /dev/null +++ b/drivers/mfd/wcd9xxx-irq.c @@ -0,0 +1,853 @@ +/* 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 +#include +#include +#include +#include + +#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE)) +#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) + +#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 100 + +#ifndef NO_IRQ +#define NO_IRQ (-1) +#endif + +#ifdef CONFIG_OF +struct wcd9xxx_irq_drv_data { + struct irq_domain *domain; + int irq; +}; +#endif + +static int virq_to_phyirq( + struct wcd9xxx_core_resource *wcd9xxx_res, int virq); +static int phyirq_to_virq( + struct wcd9xxx_core_resource *wcd9xxx_res, int irq); +static unsigned int wcd9xxx_irq_get_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res); +static void wcd9xxx_irq_put_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res); +static int wcd9xxx_map_irq( + struct wcd9xxx_core_resource *wcd9xxx_res, int irq); + +static void wcd9xxx_irq_lock(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + mutex_lock(&wcd9xxx_res->irq_lock); +} + +static void wcd9xxx_irq_sync_unlock(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + int i; + + if ((ARRAY_SIZE(wcd9xxx_res->irq_masks_cur) > + WCD9XXX_MAX_IRQ_REGS) || + (ARRAY_SIZE(wcd9xxx_res->irq_masks_cache) > + WCD9XXX_MAX_IRQ_REGS)) { + pr_err("%s: Array Size out of bound\n", __func__); + return; + } + if (!wcd9xxx_res->wcd_core_regmap) { + pr_err("%s: Codec core regmap not defined\n", + __func__); + return; + } + + for (i = 0; i < ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); i++) { + /* If there's been a change in the mask write it back + * to the hardware. + */ + if (wcd9xxx_res->irq_masks_cur[i] != + wcd9xxx_res->irq_masks_cache[i]) { + + wcd9xxx_res->irq_masks_cache[i] = + wcd9xxx_res->irq_masks_cur[i]; + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_MASK_BASE] + i, + wcd9xxx_res->irq_masks_cur[i]); + } + } + + mutex_unlock(&wcd9xxx_res->irq_lock); +} + +static void wcd9xxx_irq_enable(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq); + int byte = BIT_BYTE(wcd9xxx_irq); + int size = ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); + + if ((byte < size) && (byte >= 0)) { + wcd9xxx_res->irq_masks_cur[byte] &= + ~(BYTE_BIT_MASK(wcd9xxx_irq)); + } else { + pr_err("%s: Array size is %d but index is %d: Out of range\n", + __func__, size, byte); + } +} + +static void wcd9xxx_irq_disable(struct irq_data *data) +{ + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq); + int byte = BIT_BYTE(wcd9xxx_irq); + int size = ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); + + if ((byte < size) && (byte >= 0)) { + wcd9xxx_res->irq_masks_cur[byte] + |= BYTE_BIT_MASK(wcd9xxx_irq); + } else { + pr_err("%s: Array size is %d but index is %d: Out of range\n", + __func__, size, byte); + } +} + +static void wcd9xxx_irq_ack(struct irq_data *data) +{ + int wcd9xxx_irq = 0; + struct wcd9xxx_core_resource *wcd9xxx_res = + irq_data_get_irq_chip_data(data); + + if (wcd9xxx_res == NULL) { + pr_err("%s: wcd9xxx_res is NULL\n", __func__); + return; + } + wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq); + pr_debug("%s: IRQ_ACK called for WCD9XXX IRQ: %d\n", + __func__, wcd9xxx_irq); +} + +static void wcd9xxx_irq_mask(struct irq_data *d) +{ + /* do nothing but required as linux calls irq_mask without NULL check */ +} + +static struct irq_chip wcd9xxx_irq_chip = { + .name = "wcd9xxx", + .irq_bus_lock = wcd9xxx_irq_lock, + .irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock, + .irq_disable = wcd9xxx_irq_disable, + .irq_enable = wcd9xxx_irq_enable, + .irq_mask = wcd9xxx_irq_mask, + .irq_ack = wcd9xxx_irq_ack, +}; + +bool wcd9xxx_lock_sleep( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + enum wcd9xxx_pm_state os; + + /* + * wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread + * and its subroutines only motly. + * but btn0_lpress_fn is not wcd9xxx_irq_thread's subroutine and + * It can race with wcd9xxx_irq_thread. + * So need to embrace wlock_holders with mutex. + * + * If system didn't resume, we can simply return false so codec driver's + * IRQ handler can return without handling IRQ. + * As interrupt line is still active, codec will have another IRQ to + * retry shortly. + */ + mutex_lock(&wcd9xxx_res->pm_lock); + if (wcd9xxx_res->wlock_holders++ == 0) { + pr_debug("%s: holding wake lock\n", __func__); + pm_qos_update_request(&wcd9xxx_res->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + pm_stay_awake(wcd9xxx_res->dev); + } + mutex_unlock(&wcd9xxx_res->pm_lock); + + if (!wait_event_timeout(wcd9xxx_res->pm_wq, + ((os = wcd9xxx_pm_cmpxchg(wcd9xxx_res, + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE)) == + WCD9XXX_PM_SLEEPABLE || + (os == WCD9XXX_PM_AWAKE)), + msecs_to_jiffies( + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) { + pr_warn("%s: system didn't resume within %dms, s %d, w %d\n", + __func__, + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, wcd9xxx_res->pm_state, + wcd9xxx_res->wlock_holders); + wcd9xxx_unlock_sleep(wcd9xxx_res); + return false; + } + wake_up_all(&wcd9xxx_res->pm_wq); + return true; +} +EXPORT_SYMBOL(wcd9xxx_lock_sleep); + +void wcd9xxx_unlock_sleep( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + mutex_lock(&wcd9xxx_res->pm_lock); + if (--wcd9xxx_res->wlock_holders == 0) { + pr_debug("%s: releasing wake lock pm_state %d -> %d\n", + __func__, wcd9xxx_res->pm_state, WCD9XXX_PM_SLEEPABLE); + /* + * if wcd9xxx_lock_sleep failed, pm_state would be still + * WCD9XXX_PM_ASLEEP, don't overwrite + */ + if (likely(wcd9xxx_res->pm_state == WCD9XXX_PM_AWAKE)) + wcd9xxx_res->pm_state = WCD9XXX_PM_SLEEPABLE; + pm_qos_update_request(&wcd9xxx_res->pm_qos_req, + PM_QOS_DEFAULT_VALUE); + pm_relax(wcd9xxx_res->dev); + } + mutex_unlock(&wcd9xxx_res->pm_lock); + wake_up_all(&wcd9xxx_res->pm_wq); +} +EXPORT_SYMBOL(wcd9xxx_unlock_sleep); + +void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + mutex_lock(&wcd9xxx_res->nested_irq_lock); +} + +void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + mutex_unlock(&wcd9xxx_res->nested_irq_lock); +} + + +static void wcd9xxx_irq_dispatch(struct wcd9xxx_core_resource *wcd9xxx_res, + struct intr_data *irqdata) +{ + int irqbit = irqdata->intr_num; + + if (!wcd9xxx_res->wcd_core_regmap) { + pr_err("%s: codec core regmap not defined\n", + __func__); + return; + } + + if (irqdata->clear_first) { + wcd9xxx_nested_irq_lock(wcd9xxx_res); + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE] + + BIT_BYTE(irqbit), + BYTE_BIT_MASK(irqbit)); + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT], + 0x02); + handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit)); + wcd9xxx_nested_irq_unlock(wcd9xxx_res); + } else { + wcd9xxx_nested_irq_lock(wcd9xxx_res); + handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit)); + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE] + + BIT_BYTE(irqbit), + BYTE_BIT_MASK(irqbit)); + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT], + 0x02); + + wcd9xxx_nested_irq_unlock(wcd9xxx_res); + } +} + +static irqreturn_t wcd9xxx_irq_thread(int irq, void *data) +{ + int ret; + int i; + struct intr_data irqdata; + char linebuf[128]; + static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 1); + struct wcd9xxx_core_resource *wcd9xxx_res = data; + int num_irq_regs = wcd9xxx_res->num_irq_regs; + u8 status[4], status1[4] = {0}, unmask_status[4] = {0}; + + if (unlikely(wcd9xxx_lock_sleep(wcd9xxx_res) == false)) { + dev_err(wcd9xxx_res->dev, "Failed to hold suspend\n"); + return IRQ_NONE; + } + + if (!wcd9xxx_res->wcd_core_regmap) { + dev_err(wcd9xxx_res->dev, + "%s: Codec core regmap not supplied\n", + __func__); + goto err_disable_irq; + } + + memset(status, 0, sizeof(status)); + ret = regmap_bulk_read(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_STATUS_BASE], + status, num_irq_regs); + + if (ret < 0) { + dev_err(wcd9xxx_res->dev, + "Failed to read interrupt status: %d\n", ret); + goto err_disable_irq; + } + /* + * If status is 0 return without clearing. + * status contains: HW status - masked interrupts + * status1 contains: unhandled interrupts - masked interrupts + * unmasked_status contains: unhandled interrupts + */ + if (unlikely(!memcmp(status, status1, sizeof(status)))) { + pr_debug("%s: status is 0\n", __func__); + wcd9xxx_unlock_sleep(wcd9xxx_res); + return IRQ_HANDLED; + } + + /* + * Copy status to unmask_status before masking, otherwise SW may miss + * to clear masked interrupt in corner case. + */ + memcpy(unmask_status, status, sizeof(unmask_status)); + + /* Apply masking */ + for (i = 0; i < num_irq_regs; i++) + status[i] &= ~wcd9xxx_res->irq_masks_cur[i]; + + memcpy(status1, status, sizeof(status1)); + + /* Find out which interrupt was triggered and call that interrupt's + * handler function + * + * Since codec has only one hardware irq line which is shared by + * codec's different internal interrupts, so it's possible master irq + * handler dispatches multiple nested irq handlers after breaking + * order. Dispatch interrupts in the order that is maintained by + * the interrupt table. + */ + for (i = 0; i < wcd9xxx_res->intr_table_size; i++) { + irqdata = wcd9xxx_res->intr_table[i]; + if (status[BIT_BYTE(irqdata.intr_num)] & + BYTE_BIT_MASK(irqdata.intr_num)) { + wcd9xxx_irq_dispatch(wcd9xxx_res, &irqdata); + status1[BIT_BYTE(irqdata.intr_num)] &= + ~BYTE_BIT_MASK(irqdata.intr_num); + unmask_status[BIT_BYTE(irqdata.intr_num)] &= + ~BYTE_BIT_MASK(irqdata.intr_num); + } + } + + /* + * As a failsafe if unhandled irq is found, clear it to prevent + * interrupt storm. + * Note that we can say there was an unhandled irq only when no irq + * handled by nested irq handler since Taiko supports qdsp as irqs' + * destination for few irqs. Therefore driver shouldn't clear pending + * irqs when few handled while few others not. + */ + if (unlikely(!memcmp(status, status1, sizeof(status)))) { + if (__ratelimit(&ratelimit)) { + pr_warn("%s: Unhandled irq found\n", __func__); + hex_dump_to_buffer(status, sizeof(status), 16, 1, + linebuf, sizeof(linebuf), false); + pr_warn("%s: status0 : %s\n", __func__, linebuf); + hex_dump_to_buffer(status1, sizeof(status1), 16, 1, + linebuf, sizeof(linebuf), false); + pr_warn("%s: status1 : %s\n", __func__, linebuf); + } + /* + * unmask_status contains unhandled interrupts, hence clear all + * unhandled interrupts. + */ + ret = regmap_bulk_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE], + unmask_status, num_irq_regs); + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT], + 0x02); + } + wcd9xxx_unlock_sleep(wcd9xxx_res); + + return IRQ_HANDLED; + +err_disable_irq: + dev_err(wcd9xxx_res->dev, + "Disable irq %d\n", wcd9xxx_res->irq); + + disable_irq_wake(wcd9xxx_res->irq); + disable_irq_nosync(wcd9xxx_res->irq); + wcd9xxx_unlock_sleep(wcd9xxx_res); + return IRQ_NONE; +} + +/** + * wcd9xxx_free_irq + * + * @wcd9xxx_res: pointer to core resource + * irq: irq number + * @data: data pointer + * + */ +void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq, void *data) +{ + free_irq(phyirq_to_virq(wcd9xxx_res, irq), data); +} +EXPORT_SYMBOL(wcd9xxx_free_irq); + +/** + * wcd9xxx_enable_irq + * + * @wcd9xxx_res: pointer to core resource + * irq: irq number + * + */ +void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + if (wcd9xxx_res->irq) + enable_irq(phyirq_to_virq(wcd9xxx_res, irq)); +} +EXPORT_SYMBOL(wcd9xxx_enable_irq); + +/** + * wcd9xxx_disable_irq + * + * @wcd9xxx_res: pointer to core resource + * irq: irq number + * + */ +void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + if (wcd9xxx_res->irq) + disable_irq_nosync(phyirq_to_virq(wcd9xxx_res, irq)); +} +EXPORT_SYMBOL(wcd9xxx_disable_irq); + +/** + * wcd9xxx_disable_irq_sync + * + * @wcd9xxx_res: pointer to core resource + * irq: irq number + * + */ +void wcd9xxx_disable_irq_sync( + struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + if (wcd9xxx_res->irq) + disable_irq(phyirq_to_virq(wcd9xxx_res, irq)); +} +EXPORT_SYMBOL(wcd9xxx_disable_irq_sync); + +static int wcd9xxx_irq_setup_downstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + int irq, virq, ret; + + pr_debug("%s: enter\n", __func__); + + for (irq = 0; irq < wcd9xxx_res->num_irqs; irq++) { + /* Map OF irq */ + virq = wcd9xxx_map_irq(wcd9xxx_res, irq); + pr_debug("%s: irq %d -> %d\n", __func__, irq, virq); + if (virq == NO_IRQ) { + pr_err("%s, No interrupt specifier for irq %d\n", + __func__, irq); + return NO_IRQ; + } + + ret = irq_set_chip_data(virq, wcd9xxx_res); + if (ret) { + pr_err("%s: Failed to configure irq %d (%d)\n", + __func__, irq, ret); + return ret; + } + + if (wcd9xxx_res->irq_level_high[irq]) + irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip, + handle_level_irq); + else + irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip, + handle_edge_irq); + + irq_set_nested_thread(virq, 1); + } + + pr_debug("%s: leave\n", __func__); + + return 0; +} + +/** + * wcd9xxx_irq_init + * + * @wcd9xxx_res: pointer to core resource + * + * Returns 0 on success, appropriate error code otherwise + */ +int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + int i, ret; + u8 irq_level[wcd9xxx_res->num_irq_regs]; + struct irq_domain *domain; + struct device_node *pnode; + + mutex_init(&wcd9xxx_res->irq_lock); + mutex_init(&wcd9xxx_res->nested_irq_lock); + + pnode = of_irq_find_parent(wcd9xxx_res->dev->of_node); + if (unlikely(!pnode)) + return -EINVAL; + + domain = irq_find_host(pnode); + if (unlikely(!domain)) + return -EINVAL; + + wcd9xxx_res->domain = domain; + + wcd9xxx_res->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx_res); + if (!wcd9xxx_res->irq) { + pr_warn("%s: irq driver is not yet initialized\n", __func__); + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); + return -EPROBE_DEFER; + } + pr_debug("%s: probed irq %d\n", __func__, wcd9xxx_res->irq); + + /* Setup downstream IRQs */ + ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx_res); + if (ret) { + pr_err("%s: Failed to setup downstream IRQ\n", __func__); + wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); + return ret; + } + + /* All other wcd9xxx interrupts are edge triggered */ + wcd9xxx_res->irq_level_high[0] = true; + + /* mask all the interrupts */ + memset(irq_level, 0, wcd9xxx_res->num_irq_regs); + for (i = 0; i < wcd9xxx_res->num_irqs; i++) { + wcd9xxx_res->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i); + wcd9xxx_res->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i); + irq_level[BIT_BYTE(i)] |= + wcd9xxx_res->irq_level_high[i] << (i % BITS_PER_BYTE); + } + + if (!wcd9xxx_res->wcd_core_regmap) { + dev_err(wcd9xxx_res->dev, + "%s: Codec core regmap not defined\n", + __func__); + ret = -EINVAL; + goto fail_irq_init; + } + + for (i = 0; i < wcd9xxx_res->num_irq_regs; i++) { + /* Initialize interrupt mask and level registers */ + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_LEVEL_BASE] + i, + irq_level[i]); + regmap_write(wcd9xxx_res->wcd_core_regmap, + wcd9xxx_res->intr_reg[WCD9XXX_INTR_MASK_BASE] + i, + wcd9xxx_res->irq_masks_cur[i]); + } + + ret = request_threaded_irq(wcd9xxx_res->irq, NULL, wcd9xxx_irq_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "wcd9xxx", wcd9xxx_res); + if (ret != 0) + dev_err(wcd9xxx_res->dev, "Failed to request IRQ %d: %d\n", + wcd9xxx_res->irq, ret); + else { + ret = enable_irq_wake(wcd9xxx_res->irq); + if (ret) + dev_err(wcd9xxx_res->dev, + "Failed to set wake interrupt on IRQ %d: %d\n", + wcd9xxx_res->irq, ret); + if (ret) + free_irq(wcd9xxx_res->irq, wcd9xxx_res); + } + + if (ret) + goto fail_irq_init; + + return ret; + +fail_irq_init: + dev_err(wcd9xxx_res->dev, + "%s: Failed to init wcd9xxx irq\n", __func__); + wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); + return ret; +} +EXPORT_SYMBOL(wcd9xxx_irq_init); + +int wcd9xxx_request_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + int virq; + + virq = phyirq_to_virq(wcd9xxx_res, irq); + + return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING, + name, data); +} +EXPORT_SYMBOL(wcd9xxx_request_irq); + +void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res) +{ + dev_dbg(wcd9xxx_res->dev, "%s: Cleaning up irq %d\n", __func__, + wcd9xxx_res->irq); + + if (wcd9xxx_res->irq) { + disable_irq_wake(wcd9xxx_res->irq); + free_irq(wcd9xxx_res->irq, wcd9xxx_res); + wcd9xxx_res->irq = 0; + wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); + } + mutex_destroy(&wcd9xxx_res->irq_lock); + mutex_destroy(&wcd9xxx_res->nested_irq_lock); +} + +#ifndef CONFIG_OF +static int phyirq_to_virq( + struct wcd9xxx_core_resource *wcd9xxx_res, + int offset) +{ + return wcd9xxx_res->irq_base + offset; +} + +static int virq_to_phyirq( + struct wcd9xxx_core_resource *wcd9xxx_res, + int virq) +{ + return virq - wcd9xxx_res->irq_base; +} + +static unsigned int wcd9xxx_irq_get_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + return wcd9xxx_res->irq; +} + +static void wcd9xxx_irq_put_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + /* Do nothing */ +} + +static int wcd9xxx_map_irq( + struct wcd9xxx_core_resource *wcd9xxx_core_res, int irq) +{ + return phyirq_to_virq(wcd9xxx_core_res, irq); +} +#else +static struct wcd9xxx_irq_drv_data * +wcd9xxx_irq_add_domain(struct device_node *node, + struct device_node *parent) +{ + struct wcd9xxx_irq_drv_data *data = NULL; + + pr_debug("%s: node %s, node parent %s\n", __func__, + node->name, node->parent->name); + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + + /* + * wcd9xxx_intc interrupt controller supports N to N irq mapping with + * single cell binding with irq numbers(offsets) only. + * Use irq_domain_simple_ops that has irq_domain_simple_map and + * irq_domain_xlate_onetwocell. + */ + data->domain = irq_domain_add_linear(node, WCD9XXX_MAX_NUM_IRQS, + &irq_domain_simple_ops, data); + if (!data->domain) { + kfree(data); + return NULL; + } + + return data; +} + +static struct wcd9xxx_irq_drv_data * +wcd9xxx_get_irq_drv_d(const struct wcd9xxx_core_resource *wcd9xxx_res) +{ + struct irq_domain *domain; + + domain = wcd9xxx_res->domain; + + if (domain) + return domain->host_data; + else + return NULL; +} + +static int phyirq_to_virq(struct wcd9xxx_core_resource *wcd9xxx_res, int offset) +{ + struct wcd9xxx_irq_drv_data *data; + + data = wcd9xxx_get_irq_drv_d(wcd9xxx_res); + if (!data) { + pr_warn("%s: not registered to interrupt controller\n", + __func__); + return -EINVAL; + } + return irq_linear_revmap(data->domain, offset); +} + +static int virq_to_phyirq(struct wcd9xxx_core_resource *wcd9xxx_res, int virq) +{ + struct irq_data *irq_data = irq_get_irq_data(virq); + + if (unlikely(!irq_data)) { + pr_err("%s: irq_data is NULL", __func__); + return -EINVAL; + } + return irq_data->hwirq; +} + +static unsigned int wcd9xxx_irq_get_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + struct wcd9xxx_irq_drv_data *data; + + data = wcd9xxx_get_irq_drv_d(wcd9xxx_res); + if (!data) { + pr_err("%s: interrupt controller is not registered\n", + __func__); + return 0; + } + + /* Make sure data is updated before return. */ + rmb(); + return data->irq; +} + +static void wcd9xxx_irq_put_upstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + wcd9xxx_res->domain = NULL; +} + +static int wcd9xxx_map_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq) +{ + return of_irq_to_resource(wcd9xxx_res->dev->of_node, irq, NULL); +} + +static int wcd9xxx_irq_probe(struct platform_device *pdev) +{ + int irq, dir_apps_irq = -EINVAL; + struct wcd9xxx_irq_drv_data *data; + struct device_node *node = pdev->dev.of_node; + int ret = -EINVAL; + + irq = of_get_named_gpio(node, "qcom,gpio-connect", 0); + if (!gpio_is_valid(irq)) + dir_apps_irq = platform_get_irq_byname(pdev, "wcd_irq"); + + if (!gpio_is_valid(irq) && dir_apps_irq < 0) { + dev_err(&pdev->dev, "TLMM connect gpio not found\n"); + return -EPROBE_DEFER; + } + if (dir_apps_irq > 0) { + irq = dir_apps_irq; + } else { + irq = gpio_to_irq(irq); + if (irq < 0) { + dev_err(&pdev->dev, "Unable to configure irq\n"); + return irq; + } + } + dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq); + data = wcd9xxx_irq_add_domain(node, node->parent); + if (!data) { + pr_err("%s: irq_add_domain failed\n", __func__); + return -EINVAL; + } + data->irq = irq; + + /* Make sure irq is saved before return. */ + wmb(); + ret = 0; + + return ret; +} + +static int wcd9xxx_irq_remove(struct platform_device *pdev) +{ + struct irq_domain *domain; + struct wcd9xxx_irq_drv_data *data; + + domain = irq_find_host(pdev->dev.of_node); + if (unlikely(!domain)) { + pr_err("%s: domain is NULL", __func__); + return -EINVAL; + } + data = (struct wcd9xxx_irq_drv_data *)domain->host_data; + data->irq = 0; + + /* Make sure irq variable is updated in data, before irq removal. */ + wmb(); + irq_domain_remove(data->domain); + kfree(data); + domain->host_data = NULL; + + return 0; +} + +static const struct of_device_id of_match[] = { + { .compatible = "qcom,wcd9xxx-irq" }, + { } +}; + +static struct platform_driver wcd9xxx_irq_driver = { + .probe = wcd9xxx_irq_probe, + .remove = wcd9xxx_irq_remove, + .driver = { + .name = "wcd9xxx_intc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_match), + }, +}; + +int wcd9xxx_irq_drv_init(void) +{ + return platform_driver_register(&wcd9xxx_irq_driver); +} + +void wcd9xxx_irq_drv_exit(void) +{ + platform_driver_unregister(&wcd9xxx_irq_driver); +} +#endif /* CONFIG_OF */ diff --git a/drivers/mfd/wcd9xxx-regmap.h b/drivers/mfd/wcd9xxx-regmap.h new file mode 100644 index 000000000000..f44e8b1cf532 --- /dev/null +++ b/drivers/mfd/wcd9xxx-regmap.h @@ -0,0 +1,68 @@ +/* + * 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 _WCD9XXX_REGMAP_ +#define _WCD9XXX_REGMAP_ + +#include +#include + +typedef int (*regmap_patch_fptr)(struct regmap *, int); + +extern struct regmap_config wcd934x_regmap_config; +extern int wcd934x_regmap_register_patch(struct regmap *regmap, + int version); + +extern struct regmap_config wcd9335_regmap_config; +extern int wcd9335_regmap_register_patch(struct regmap *regmap, + int version); + +static inline struct regmap_config *wcd9xxx_get_regmap_config(int type) +{ + struct regmap_config *regmap_config; + + switch (type) { + case WCD934X: + regmap_config = &wcd934x_regmap_config; + break; + case WCD9335: + regmap_config = &wcd9335_regmap_config; + break; + default: + regmap_config = NULL; + break; + }; + + return regmap_config; +} + +static inline regmap_patch_fptr wcd9xxx_get_regmap_reg_patch(int type) +{ + regmap_patch_fptr apply_patch; + + switch (type) { + case WCD9335: + apply_patch = wcd9335_regmap_register_patch; + break; + case WCD934X: + apply_patch = wcd934x_regmap_register_patch; + break; + default: + apply_patch = NULL; + break; + } + + return apply_patch; +} + +#endif diff --git a/drivers/mfd/wcd9xxx-rst.c b/drivers/mfd/wcd9xxx-rst.c new file mode 100644 index 000000000000..c8e0b348254a --- /dev/null +++ b/drivers/mfd/wcd9xxx-rst.c @@ -0,0 +1,443 @@ +/* Copyright (c) 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 + +/* wcd9335 interrupt table */ +static const struct intr_data wcd9335_intr_table[] = { + {WCD9XXX_IRQ_SLIMBUS, false}, + {WCD9335_IRQ_MBHC_SW_DET, true}, + {WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, true}, + {WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, true}, + {WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, true}, + {WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true}, + {WCD9335_IRQ_FLL_LOCK_LOSS, false}, + {WCD9335_IRQ_HPH_PA_CNPL_COMPLETE, false}, + {WCD9335_IRQ_HPH_PA_CNPR_COMPLETE, false}, + {WCD9335_IRQ_EAR_PA_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA1_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA2_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA3_CNP_COMPLETE, false}, + {WCD9335_IRQ_LINE_PA4_CNP_COMPLETE, false}, + {WCD9335_IRQ_HPH_PA_OCPL_FAULT, false}, + {WCD9335_IRQ_HPH_PA_OCPR_FAULT, false}, + {WCD9335_IRQ_EAR_PA_OCP_FAULT, false}, + {WCD9335_IRQ_SOUNDWIRE, false}, + {WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE, false}, + {WCD9335_IRQ_RCO_ERROR, false}, + {WCD9335_IRQ_SVA_ERROR, false}, + {WCD9335_IRQ_MAD_AUDIO, false}, + {WCD9335_IRQ_MAD_BEACON, false}, + {WCD9335_IRQ_SVA_OUTBOX1, true}, + {WCD9335_IRQ_SVA_OUTBOX2, true}, + {WCD9335_IRQ_MAD_ULTRASOUND, false}, + {WCD9335_IRQ_VBAT_ATTACK, false}, + {WCD9335_IRQ_VBAT_RESTORE, false}, +}; + +static const struct intr_data wcd934x_intr_table[] = { + {WCD9XXX_IRQ_SLIMBUS, false}, + {WCD934X_IRQ_MBHC_SW_DET, true}, + {WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, true}, + {WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, true}, + {WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, true}, + {WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true}, + {WCD934X_IRQ_MISC, false}, + {WCD934X_IRQ_HPH_PA_CNPL_COMPLETE, false}, + {WCD934X_IRQ_HPH_PA_CNPR_COMPLETE, false}, + {WCD934X_IRQ_EAR_PA_CNP_COMPLETE, false}, + {WCD934X_IRQ_LINE_PA1_CNP_COMPLETE, false}, + {WCD934X_IRQ_LINE_PA2_CNP_COMPLETE, false}, + {WCD934X_IRQ_SLNQ_ANALOG_ERROR, false}, + {WCD934X_IRQ_RESERVED_3, false}, + {WCD934X_IRQ_HPH_PA_OCPL_FAULT, false}, + {WCD934X_IRQ_HPH_PA_OCPR_FAULT, false}, + {WCD934X_IRQ_EAR_PA_OCP_FAULT, false}, + {WCD934X_IRQ_SOUNDWIRE, false}, + {WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE, false}, + {WCD934X_IRQ_RCO_ERROR, false}, + {WCD934X_IRQ_CPE_ERROR, false}, + {WCD934X_IRQ_MAD_AUDIO, false}, + {WCD934X_IRQ_MAD_BEACON, false}, + {WCD934X_IRQ_CPE1_INTR, true}, + {WCD934X_IRQ_RESERVED_4, false}, + {WCD934X_IRQ_MAD_ULTRASOUND, false}, + {WCD934X_IRQ_VBAT_ATTACK, false}, + {WCD934X_IRQ_VBAT_RESTORE, false}, +}; + +/* + * wcd9335_bring_down: Bringdown WCD Codec + * + * @wcd9xxx: Pointer to wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd9335_bring_down(struct wcd9xxx *wcd9xxx) +{ + if (!wcd9xxx || !wcd9xxx->regmap) + return -EINVAL; + + regmap_write(wcd9xxx->regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x04); + + return 0; +} + +/* + * wcd9335_bring_up: Bringup WCD Codec + * + * @wcd9xxx: Pointer to the wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd9335_bring_up(struct wcd9xxx *wcd9xxx) +{ + int ret = 0; + int val, byte0; + struct regmap *wcd_regmap; + + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n", + __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, &val); + regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, &byte0); + + if ((val < 0) || (byte0 < 0)) { + dev_err(wcd9xxx->dev, "%s: tasha codec version detection fail!\n", + __func__); + return -EINVAL; + } + if ((val & 0x80) && (byte0 == 0x0)) { + dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.1\n", + __func__); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x5); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x7); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x3); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3); + } else if (byte0 == 0x1) { + dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v2.0\n", + __func__); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_TEST_2, 0x00); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_8, 0x6F); + regmap_write(wcd_regmap, WCD9335_BIAS_VBG_FINE_ADJ, 0x65); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x5); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x7); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x3); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3); + } else if ((byte0 == 0) && (!(val & 0x80))) { + dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.0\n", + __func__); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC); + regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x3); + regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3); + } else { + dev_err(wcd9xxx->dev, "%s: tasha codec version unknown\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +/* + * wcd9335_get_cdc_info: Get codec specific information + * + * @wcd9xxx: pointer to wcd9xxx structure + * @wcd_type: pointer to wcd9xxx_codec_type structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd9335_get_cdc_info(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_codec_type *wcd_type) +{ + u16 id_minor, id_major; + struct regmap *wcd_regmap; + int rc, val, version = 0; + + if (!wcd9xxx || !wcd_type) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n", + __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + if (rc) + return -EINVAL; + + rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2, + (u8 *)&id_major, sizeof(u16)); + if (rc) + return -EINVAL; + + dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n", + __func__, id_major, id_minor); + + /* Version detection */ + if (id_major == TASHA_MAJOR) { + regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, + &val); + version = ((u8)val & 0x80) >> 7; + } else if (id_major == TASHA2P0_MAJOR) + version = 2; + else + dev_err(wcd9xxx->dev, "%s: wcd9335 version unknown (major 0x%x, minor 0x%x)\n", + __func__, id_major, id_minor); + + /* Fill codec type info */ + wcd_type->id_major = id_major; + wcd_type->id_minor = id_minor; + wcd_type->num_irqs = WCD9335_NUM_IRQS; + wcd_type->version = version; + wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1; + wcd_type->i2c_chip_status = 0x01; + wcd_type->intr_tbl = wcd9335_intr_table; + wcd_type->intr_tbl_size = ARRAY_SIZE(wcd9335_intr_table); + + wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] = + WCD9335_INTR_PIN1_STATUS0; + wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] = + WCD9335_INTR_PIN1_CLEAR0; + wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] = + WCD9335_INTR_PIN1_MASK0; + wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] = + WCD9335_INTR_LEVEL0; + wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] = + WCD9335_INTR_CLR_COMMIT; + + return rc; +} + +/* + * wcd934x_bring_down: Bringdown WCD Codec + * + * @wcd9xxx: Pointer to wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd934x_bring_down(struct wcd9xxx *wcd9xxx) +{ + if (!wcd9xxx || !wcd9xxx->regmap) + return -EINVAL; + + regmap_write(wcd9xxx->regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x04); + + return 0; +} + +/* + * wcd934x_bring_up: Bringup WCD Codec + * + * @wcd9xxx: Pointer to the wcd9xxx structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd934x_bring_up(struct wcd9xxx *wcd9xxx) +{ + struct regmap *wcd_regmap; + + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n", + __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x01); + regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x19); + regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x15); + /* Add 1msec delay for VOUT to settle */ + usleep_range(1000, 1100); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x3); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x7); + regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + + return 0; +} + +/* + * wcd934x_get_cdc_info: Get codec specific information + * + * @wcd9xxx: pointer to wcd9xxx structure + * @wcd_type: pointer to wcd9xxx_codec_type structure + * + * Returns 0 for success or negative error code for failure + */ +static int wcd934x_get_cdc_info(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_codec_type *wcd_type) +{ + u16 id_minor, id_major; + struct regmap *wcd_regmap; + int rc, version = -1; + + if (!wcd9xxx || !wcd_type) + return -EINVAL; + + if (!wcd9xxx->regmap) { + dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null\n", __func__); + return -EINVAL; + } + wcd_regmap = wcd9xxx->regmap; + + rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0, + (u8 *)&id_minor, sizeof(u16)); + if (rc) + return -EINVAL; + + rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2, + (u8 *)&id_major, sizeof(u16)); + if (rc) + return -EINVAL; + + dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n", + __func__, id_major, id_minor); + + if (id_major != TAVIL_MAJOR) + goto version_unknown; + + /* + * As fine version info cannot be retrieved before tavil probe. + * Assign coarse versions for possible future use before tavil probe. + */ + if (id_minor == cpu_to_le16(0)) + version = TAVIL_VERSION_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_1_1; + +version_unknown: + if (version < 0) + dev_err(wcd9xxx->dev, "%s: wcd934x version unknown\n", + __func__); + + /* Fill codec type info */ + wcd_type->id_major = id_major; + wcd_type->id_minor = id_minor; + wcd_type->num_irqs = WCD934X_NUM_IRQS; + wcd_type->version = version; + wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1; + wcd_type->i2c_chip_status = 0x01; + wcd_type->intr_tbl = wcd934x_intr_table; + wcd_type->intr_tbl_size = ARRAY_SIZE(wcd934x_intr_table); + + wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] = + WCD934X_INTR_PIN1_STATUS0; + wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] = + WCD934X_INTR_PIN1_CLEAR0; + wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] = + WCD934X_INTR_PIN1_MASK0; + wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] = + WCD934X_INTR_LEVEL0; + wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] = + WCD934X_INTR_CLR_COMMIT; + + return rc; +} + +codec_bringdown_fn wcd9xxx_bringdown_fn(int type) +{ + codec_bringdown_fn cdc_bdown_fn; + + switch (type) { + case WCD934X: + cdc_bdown_fn = wcd934x_bring_down; + break; + case WCD9335: + cdc_bdown_fn = wcd9335_bring_down; + break; + default: + cdc_bdown_fn = NULL; + break; + } + + return cdc_bdown_fn; +} + +codec_bringup_fn wcd9xxx_bringup_fn(int type) +{ + codec_bringup_fn cdc_bup_fn; + + switch (type) { + case WCD934X: + cdc_bup_fn = wcd934x_bring_up; + break; + case WCD9335: + cdc_bup_fn = wcd9335_bring_up; + break; + default: + cdc_bup_fn = NULL; + break; + } + + return cdc_bup_fn; +} + +codec_type_fn wcd9xxx_get_codec_info_fn(int type) +{ + codec_type_fn cdc_type_fn; + + switch (type) { + case WCD934X: + cdc_type_fn = wcd934x_get_cdc_info; + break; + case WCD9335: + cdc_type_fn = wcd9335_get_cdc_info; + break; + default: + cdc_type_fn = NULL; + break; + } + + return cdc_type_fn; +} + diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c new file mode 100644 index 000000000000..a99ad5a2f9c8 --- /dev/null +++ b/drivers/mfd/wcd9xxx-slimslave.c @@ -0,0 +1,584 @@ +/* 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 + +struct wcd9xxx_slim_sch { + u16 rx_port_ch_reg_base; + u16 port_tx_cfg_reg_base; + u16 port_rx_cfg_reg_base; +}; + +static struct wcd9xxx_slim_sch sh_ch; + +static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx, + u8 wcd9xxx_pgd_la, u32 cnt, + struct wcd9xxx_ch *channels, u32 path); + +static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim, + u32 cnt, struct wcd9xxx_ch *channels); + +static int wcd9xxx_configure_ports(struct wcd9xxx *wcd9xxx) +{ + if (wcd9xxx->codec_type->slim_slave_type == + WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0) { + sh_ch.rx_port_ch_reg_base = 0x180; + sh_ch.port_rx_cfg_reg_base = 0x040; + sh_ch.port_tx_cfg_reg_base = 0x040; + } else { + sh_ch.rx_port_ch_reg_base = + 0x180 - (TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4); + sh_ch.port_rx_cfg_reg_base = + 0x040 - TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS; + sh_ch.port_tx_cfg_reg_base = 0x050; + } + + return 0; +} + +/** + * wcd9xxx_init_slimslave + * + * @wcd9xxx: pointer to wcd9xxx struct + * @wcd9xxx_pgd_la: pgd_la value + * @tx_num: tx number + * @rx_num: rx number + * @tx_slot: pointer to tx slot + * @rx_slot: pointer to rx slot + * + * Returns 0 on success, appropriate error code otherwise + */ +int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) +{ + int ret = 0; + int i; + + ret = wcd9xxx_configure_ports(wcd9xxx); + if (ret) { + pr_err("%s: Failed to configure register address offset\n", + __func__); + goto err; + } + + if (!rx_num || rx_num > wcd9xxx->num_rx_port) { + pr_err("%s: invalid rx num %d\n", __func__, rx_num); + return -EINVAL; + } + if (wcd9xxx->rx_chs) { + wcd9xxx->num_rx_port = rx_num; + for (i = 0; i < rx_num; i++) { + wcd9xxx->rx_chs[i].ch_num = rx_slot[i]; + INIT_LIST_HEAD(&wcd9xxx->rx_chs[i].list); + } + ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la, + wcd9xxx->num_rx_port, + wcd9xxx->rx_chs, + SLIM_SINK); + if (ret) { + pr_err("%s: Failed to alloc %d rx slimbus channels\n", + __func__, wcd9xxx->num_rx_port); + kfree(wcd9xxx->rx_chs); + wcd9xxx->rx_chs = NULL; + wcd9xxx->num_rx_port = 0; + } + } else { + pr_err("Not able to allocate memory for %d slimbus rx ports\n", + wcd9xxx->num_rx_port); + } + + if (!tx_num || tx_num > wcd9xxx->num_tx_port) { + pr_err("%s: invalid tx num %d\n", __func__, tx_num); + return -EINVAL; + } + if (wcd9xxx->tx_chs) { + wcd9xxx->num_tx_port = tx_num; + for (i = 0; i < tx_num; i++) { + wcd9xxx->tx_chs[i].ch_num = tx_slot[i]; + INIT_LIST_HEAD(&wcd9xxx->tx_chs[i].list); + } + ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la, + wcd9xxx->num_tx_port, + wcd9xxx->tx_chs, + SLIM_SRC); + if (ret) { + pr_err("%s: Failed to alloc %d tx slimbus channels\n", + __func__, wcd9xxx->num_tx_port); + kfree(wcd9xxx->tx_chs); + wcd9xxx->tx_chs = NULL; + wcd9xxx->num_tx_port = 0; + } + } else { + pr_err("Not able to allocate memory for %d slimbus tx ports\n", + wcd9xxx->num_tx_port); + } + return 0; +err: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_init_slimslave); + +int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx) +{ + if (wcd9xxx->num_rx_port) { + wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim, + wcd9xxx->num_rx_port, + wcd9xxx->rx_chs); + wcd9xxx->num_rx_port = 0; + } + if (wcd9xxx->num_tx_port) { + wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim, + wcd9xxx->num_tx_port, + wcd9xxx->tx_chs); + wcd9xxx->num_tx_port = 0; + } + return 0; +} + + +static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx, + u8 wcd9xxx_pgd_la, u32 cnt, + struct wcd9xxx_ch *channels, u32 path) +{ + int ret = 0; + u32 ch_idx; + + /* The slimbus channel allocation seem take longer time + * so do the allocation up front to avoid delay in start of + * playback + */ + pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la); + for (ch_idx = 0; ch_idx < cnt; ch_idx++) { + ret = slim_get_slaveport(wcd9xxx_pgd_la, + channels[ch_idx].port, + &channels[ch_idx].sph, path); + pr_debug("%s: pgd_la[%d] channels[%d].port[%d]\n" + "channels[%d].sph[%d] path[%d]\n", + __func__, wcd9xxx_pgd_la, ch_idx, + channels[ch_idx].port, + ch_idx, channels[ch_idx].sph, path); + if (ret < 0) { + pr_err("%s: slave port failure id[%d] ret[%d]\n", + __func__, channels[ch_idx].ch_num, ret); + goto err; + } + + ret = slim_query_ch(wcd9xxx->slim, + channels[ch_idx].ch_num, + &channels[ch_idx].ch_h); + if (ret < 0) { + pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n", + __func__, channels[ch_idx].ch_num, ret); + goto err; + } + } +err: + return ret; +} + +static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim, + u32 cnt, struct wcd9xxx_ch *channels) +{ + int idx = 0; + int ret = 0; + /* slim_dealloc_ch */ + for (idx = 0; idx < cnt; idx++) { + ret = slim_dealloc_ch(slim, channels[idx].ch_h); + if (ret < 0) { + pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n", + __func__, ret, channels[idx].ch_h); + } + } + return ret; +} + +/* Enable slimbus slave device for RX path */ +int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph) +{ + u8 ch_cnt = 0; + u16 ch_h[SLIM_MAX_RX_PORTS] = {0}; + u8 payload = 0; + u16 codec_port = 0; + int ret; + struct slim_ch prop; + struct wcd9xxx_ch *rx; + int size = ARRAY_SIZE(ch_h); + + /* Configure slave interface device */ + + list_for_each_entry(rx, wcd9xxx_ch_list, list) { + payload |= 1 << rx->shift; + if (ch_cnt < size) { + ch_h[ch_cnt] = rx->ch_h; + ch_cnt++; + pr_debug("list ch->ch_h %d ch->sph %d\n", + rx->ch_h, rx->sph); + } else { + pr_err("%s: allocated channel number %u is out of max rangae %d\n", + __func__, ch_cnt, + size); + ret = EINVAL; + goto err; + } + } + pr_debug("%s: ch_cnt[%d] rate=%d WATER_MARK_VAL %d\n", + __func__, ch_cnt, rate, WATER_MARK_VAL); + /* slim_define_ch api */ + prop.prot = SLIM_AUTO_ISO; + if ((rate == 44100) || (rate == 88200) || (rate == 176400) || + (rate == 352800)) { + prop.baser = SLIM_RATE_11025HZ; + prop.ratem = (rate/11025); + } else { + prop.baser = SLIM_RATE_4000HZ; + prop.ratem = (rate/4000); + } + prop.dataf = SLIM_CH_DATAF_NOT_DEFINED; + prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE; + prop.sampleszbits = bit_width; + + pr_debug("Before slim_define_ch:\n" + "ch_cnt %d,ch_h[0] %d ch_h[1] %d, grph %d\n", + ch_cnt, ch_h[0], ch_h[1], *grph); + ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, + true, grph); + if (ret < 0) { + pr_err("%s: slim_define_ch failed ret[%d]\n", + __func__, ret); + goto err; + } + + list_for_each_entry(rx, wcd9xxx_ch_list, list) { + codec_port = rx->port; + pr_debug("%s: codec_port %d rx 0x%p, payload %d\n" + "sh_ch.rx_port_ch_reg_base0 0x%x\n" + "sh_ch.port_rx_cfg_reg_base 0x%x\n", + __func__, codec_port, rx, payload, + sh_ch.rx_port_ch_reg_base, + sh_ch.port_rx_cfg_reg_base); + + /* look for the valid port range and chose the + * payload accordingly + */ + /* write to interface device */ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_RX_PORT_MULTI_CHANNEL_0( + sh_ch.rx_port_ch_reg_base, codec_port), + payload); + + if (ret < 0) { + pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n", + __func__, + SB_PGD_RX_PORT_MULTI_CHANNEL_0( + sh_ch.rx_port_ch_reg_base, codec_port), + payload, ret); + goto err; + } + /* configure the slave port for water mark and enable*/ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_PORT_CFG_BYTE_ADDR( + sh_ch.port_rx_cfg_reg_base, codec_port), + WATER_MARK_VAL); + if (ret < 0) { + pr_err("%s:watermark set failure for port[%d] ret[%d]", + __func__, codec_port, ret); + } + + ret = slim_connect_sink(wcd9xxx->slim, &rx->sph, 1, rx->ch_h); + if (ret < 0) { + pr_err("%s: slim_connect_sink failed ret[%d]\n", + __func__, ret); + goto err_close_slim_sch; + } + } + /* slim_control_ch */ + ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE, + true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", + __func__, ret); + goto err_close_slim_sch; + } + return 0; + +err_close_slim_sch: + /* release all acquired handles */ + wcd9xxx_close_slim_sch_rx(wcd9xxx, wcd9xxx_ch_list, *grph); +err: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_rx); + +/* Enable slimbus slave device for RX path */ +int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph) +{ + u16 ch_cnt = 0; + u16 payload = 0; + u16 ch_h[SLIM_MAX_TX_PORTS] = {0}; + u16 codec_port; + int ret = 0; + struct wcd9xxx_ch *tx; + int size = ARRAY_SIZE(ch_h); + + struct slim_ch prop; + + list_for_each_entry(tx, wcd9xxx_ch_list, list) { + payload |= 1 << tx->shift; + if (ch_cnt < size) { + ch_h[ch_cnt] = tx->ch_h; + ch_cnt++; + } else { + pr_err("%s: allocated channel number %u is out of max rangae %d\n", + __func__, ch_cnt, + size); + ret = EINVAL; + goto err; + } + } + + /* slim_define_ch api */ + 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 = (rate/4000); + prop.sampleszbits = bit_width; + ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt, + true, grph); + if (ret < 0) { + pr_err("%s: slim_define_ch failed ret[%d]\n", + __func__, ret); + goto err; + } + + pr_debug("%s: ch_cnt[%d] rate[%d] bitwidth[%u]\n", __func__, ch_cnt, + rate, bit_width); + list_for_each_entry(tx, wcd9xxx_ch_list, list) { + codec_port = tx->port; + pr_debug("%s: codec_port %d tx 0x%p, payload 0x%x\n", + __func__, codec_port, tx, payload); + /* write to interface device */ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port), + payload & 0x00FF); + if (ret < 0) { + pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n", + __func__, + SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port), + payload, ret); + goto err; + } + /* ports 8,9 */ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port), + (payload & 0xFF00)>>8); + if (ret < 0) { + pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n", + __func__, + SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port), + payload, ret); + goto err; + } + /* configure the slave port for water mark and enable*/ + ret = wcd9xxx_interface_reg_write(wcd9xxx, + SB_PGD_PORT_CFG_BYTE_ADDR( + sh_ch.port_tx_cfg_reg_base, codec_port), + WATER_MARK_VAL); + if (ret < 0) { + pr_err("%s:watermark set failure for port[%d] ret[%d]", + __func__, codec_port, ret); + } + + ret = slim_connect_src(wcd9xxx->slim, tx->sph, tx->ch_h); + + if (ret < 0) { + pr_err("%s: slim_connect_src failed ret[%d]\n", + __func__, ret); + goto err; + } + } + /* slim_control_ch */ + ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE, + true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", + __func__, ret); + goto err; + } + return 0; +err: + /* release all acquired handles */ + wcd9xxx_close_slim_sch_tx(wcd9xxx, wcd9xxx_ch_list, *grph); + return ret; +} +EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_tx); + +int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph) +{ + u32 sph[SLIM_MAX_RX_PORTS] = {0}; + int ch_cnt = 0; + int ret = 0; + struct wcd9xxx_ch *rx; + + list_for_each_entry(rx, wcd9xxx_ch_list, list) + sph[ch_cnt++] = rx->sph; + + pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", __func__, ch_cnt, + sph[0], sph[1]); + + /* slim_control_ch (REMOVE) */ + pr_debug("%s before slim_control_ch grph %d\n", __func__, grph); + ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret); + goto err; + } +err: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_close_slim_sch_rx); + +int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + u16 grph) +{ + u32 sph[SLIM_MAX_TX_PORTS] = {0}; + int ret = 0; + int ch_cnt = 0; + struct wcd9xxx_ch *tx; + + pr_debug("%s\n", __func__); + list_for_each_entry(tx, wcd9xxx_ch_list, list) + sph[ch_cnt++] = tx->sph; + + pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", + __func__, ch_cnt, sph[0], sph[1]); + /* slim_control_ch (REMOVE) */ + ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true); + if (ret < 0) { + pr_err("%s: slim_control_ch failed ret[%d]\n", + __func__, ret); + goto err; + } +err: + return ret; +} +EXPORT_SYMBOL(wcd9xxx_close_slim_sch_tx); + +int wcd9xxx_get_slave_port(unsigned int ch_num) +{ + int ret = 0; + + ret = (ch_num - BASE_CH_NUM); + pr_debug("%s: ch_num[%d] slave port[%d]\n", __func__, ch_num, ret); + if (ret < 0) { + pr_err("%s: Error:- Invalid slave port found = %d\n", + __func__, ret); + return -EINVAL; + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_get_slave_port); + +int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph) +{ + u32 sph[SLIM_MAX_TX_PORTS + SLIM_MAX_RX_PORTS] = {0}; + int ch_cnt = 0; + int ret = 0; + struct wcd9xxx_ch *slim_ch; + + list_for_each_entry(slim_ch, wcd9xxx_ch_list, list) + sph[ch_cnt++] = slim_ch->sph; + + /* slim_disconnect_port */ + ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt); + if (ret < 0) { + pr_err("%s: slim_disconnect_ports failed ret[%d]\n", + __func__, ret); + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_disconnect_port); + +/* This function is called with mutex acquired */ +int wcd9xxx_rx_vport_validation(u32 port_id, + struct list_head *codec_dai_list) +{ + struct wcd9xxx_ch *ch; + int ret = 0; + + pr_debug("%s: port_id %u\n", __func__, port_id); + + list_for_each_entry(ch, + codec_dai_list, list) { + pr_debug("%s: ch->port %u\n", __func__, ch->port); + if (ch->port == port_id) { + ret = -EINVAL; + break; + } + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_rx_vport_validation); + + +/* This function is called with mutex acquired */ +int wcd9xxx_tx_vport_validation(u32 table, u32 port_id, + struct wcd9xxx_codec_dai_data *codec_dai, + u32 num_codec_dais) +{ + struct wcd9xxx_ch *ch; + int ret = 0; + u32 index; + unsigned long vtable = table; + u32 size = sizeof(table) * BITS_PER_BYTE; + + pr_debug("%s: vtable 0x%lx port_id %u size %d\n", __func__, + vtable, port_id, size); + for_each_set_bit(index, &vtable, size) { + if (index < num_codec_dais) { + list_for_each_entry(ch, + &codec_dai[index].wcd9xxx_ch_list, + list) { + pr_debug("%s: index %u ch->port %u vtable 0x%lx\n", + __func__, index, ch->port, + vtable); + if (ch->port == port_id) { + pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n", + __func__, port_id + 1, + (index + 1)/2); + ret = -EINVAL; + break; + } + } + } else { + pr_err("%s: Invalid index %d of codec dai", + __func__, index); + ret = -EINVAL; + } + if (ret) + break; + } + return ret; +} +EXPORT_SYMBOL(wcd9xxx_tx_vport_validation); diff --git a/drivers/mfd/wcd9xxx-utils.c b/drivers/mfd/wcd9xxx-utils.c new file mode 100644 index 000000000000..8d3d4ad4bbb5 --- /dev/null +++ b/drivers/mfd/wcd9xxx-utils.c @@ -0,0 +1,1198 @@ +/* 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 + +#define REG_BYTES 2 +#define VAL_BYTES 1 +/* + * Page Register Address that APP Proc uses to + * access WCD9335 Codec registers is identified + * as 0x00 + */ +#define PAGE_REG_ADDR 0x00 + +static enum wcd9xxx_intf_status wcd9xxx_intf = -1; + +static struct mfd_cell tavil_devs[] = { + { + .name = "qcom-wcd-pinctrl", + .of_compatible = "qcom,wcd-pinctrl", + }, + { + .name = "tavil_codec", + }, +}; + +static struct mfd_cell tasha_devs[] = { + { + .name = "tasha_codec", + }, +}; + +static struct mfd_cell tomtom_devs[] = { + { + .name = "tomtom_codec", + }, +}; + +static int wcd9xxx_read_of_property_u32(struct device *dev, const char *name, + u32 *val) +{ + int rc = 0; + + rc = of_property_read_u32(dev->of_node, name, val); + if (rc) + dev_err(dev, "%s: Looking up %s property in node %s failed", + __func__, name, dev->of_node->full_name); + + return rc; +} + +static void wcd9xxx_dt_parse_micbias_info(struct device *dev, + struct wcd9xxx_micbias_setting *mb) +{ + u32 prop_val; + int rc; + + if (of_find_property(dev->of_node, "qcom,cdc-micbias-ldoh-v", NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-ldoh-v", + &prop_val); + if (!rc) + mb->ldoh_v = (u8)prop_val; + } + + /* MB1 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias-cfilt1-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-cfilt1-mv", + &prop_val); + if (!rc) + mb->cfilt1_mv = prop_val; + + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias1-cfilt-sel", + &prop_val); + if (!rc) + mb->bias1_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias1-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias1-mv", + &prop_val); + if (!rc) + mb->micb1_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias1 DT property not found\n", + __func__); + } + + /* MB2 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias-cfilt2-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-cfilt2-mv", + &prop_val); + if (!rc) + mb->cfilt2_mv = prop_val; + + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias2-cfilt-sel", + &prop_val); + if (!rc) + mb->bias2_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias2-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias2-mv", + &prop_val); + if (!rc) + mb->micb2_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias2 DT property not found\n", + __func__); + } + + /* MB3 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias-cfilt3-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias-cfilt3-mv", + &prop_val); + if (!rc) + mb->cfilt3_mv = prop_val; + + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias3-cfilt-sel", + &prop_val); + if (!rc) + mb->bias3_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias3-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias3-mv", + &prop_val); + if (!rc) + mb->micb3_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias3 DT property not found\n", + __func__); + } + + /* MB4 */ + if (of_find_property(dev->of_node, "qcom,cdc-micbias4-cfilt-sel", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias4-cfilt-sel", + &prop_val); + if (!rc) + mb->bias4_cfilt_sel = (u8)prop_val; + + } else if (of_find_property(dev->of_node, "qcom,cdc-micbias4-mv", + NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-micbias4-mv", + &prop_val); + if (!rc) + mb->micb4_mv = prop_val; + } else { + dev_info(dev, "%s: Micbias4 DT property not found\n", + __func__); + } + + mb->bias1_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias1-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + mb->bias2_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias2-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + mb->bias3_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias3-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + mb->bias4_cap_mode = + (of_property_read_bool(dev->of_node, "qcom,cdc-micbias4-ext-cap") ? + MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP); + + mb->bias2_is_headset_only = + of_property_read_bool(dev->of_node, + "qcom,cdc-micbias2-headset-only"); + + /* Print micbias info */ + dev_dbg(dev, "%s: ldoh_v %u cfilt1_mv %u cfilt2_mv %u cfilt3_mv %u", + __func__, (u32)mb->ldoh_v, (u32)mb->cfilt1_mv, + (u32)mb->cfilt2_mv, (u32)mb->cfilt3_mv); + + dev_dbg(dev, "%s: micb1_mv %u micb2_mv %u micb3_mv %u micb4_mv %u", + __func__, mb->micb1_mv, mb->micb2_mv, + mb->micb3_mv, mb->micb4_mv); + + dev_dbg(dev, "%s: bias1_cfilt_sel %u bias2_cfilt_sel %u\n", + __func__, (u32)mb->bias1_cfilt_sel, (u32)mb->bias2_cfilt_sel); + + dev_dbg(dev, "%s: bias3_cfilt_sel %u bias4_cfilt_sel %u\n", + __func__, (u32)mb->bias3_cfilt_sel, (u32)mb->bias4_cfilt_sel); + + dev_dbg(dev, "%s: bias1_ext_cap %d bias2_ext_cap %d\n", + __func__, mb->bias1_cap_mode, mb->bias2_cap_mode); + + dev_dbg(dev, "%s: bias3_ext_cap %d bias4_ext_cap %d\n", + __func__, mb->bias3_cap_mode, mb->bias4_cap_mode); + + dev_dbg(dev, "%s: bias2_is_headset_only %d\n", + __func__, mb->bias2_is_headset_only); +} + +/* + * wcd9xxx_validate_dmic_sample_rate: + * Given the dmic_sample_rate and mclk rate, validate the + * dmic_sample_rate. If dmic rate is found to be invalid, + * assign the dmic rate as undefined, so individual codec + * drivers can use their own defaults + * @dev: the device for which the dmic is to be configured + * @dmic_sample_rate: The input dmic_sample_rate + * @mclk_rate: The input codec mclk rate + * @dmic_rate_type: String to indicate the type of dmic sample + * rate, used for debug/error logging. + */ +static u32 wcd9xxx_validate_dmic_sample_rate(struct device *dev, + u32 dmic_sample_rate, u32 mclk_rate, + const char *dmic_rate_type) +{ + u32 div_factor; + + if (dmic_sample_rate == WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED || + mclk_rate % dmic_sample_rate != 0) + goto undefined_rate; + + div_factor = mclk_rate / dmic_sample_rate; + + switch (div_factor) { + case 2: + case 3: + case 4: + case 8: + case 16: + /* Valid dmic DIV factors */ + dev_dbg(dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n", + __func__, div_factor, mclk_rate); + break; + case 6: + /* + * DIV 6 is valid for both 9.6MHz and 12.288MHz + * MCLK on Tavil. Older codecs support DIV6 only + * for 12.288MHz MCLK. + */ + if ((mclk_rate == WCD9XXX_MCLK_CLK_9P6HZ) && + (of_device_is_compatible(dev->of_node, + "qcom,tavil-slim-pgd"))) + dev_dbg(dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n", + __func__, div_factor, mclk_rate); + else if (mclk_rate != WCD9XXX_MCLK_CLK_12P288MHZ) + goto undefined_rate; + break; + default: + /* Any other DIV factor is invalid */ + goto undefined_rate; + } + + return dmic_sample_rate; + +undefined_rate: + dev_dbg(dev, "%s: Invalid %s = %d, for mclk %d\n", + __func__, dmic_rate_type, dmic_sample_rate, mclk_rate); + dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + + return dmic_sample_rate; +} + +/* + * wcd9xxx_populate_dt_data: + * Parse device tree properties for the given codec device + * + * @dev: pointer to codec device + * + * Returns pointer to the platform data resulting from parsing + * device tree. + */ +struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev) +{ + struct wcd9xxx_pdata *pdata; + u32 dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + u32 mad_dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + u32 ecpp_dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED; + u32 dmic_clk_drive = WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED; + u32 prop_val; + int rc = 0; + + if (!dev || !dev->of_node) + return NULL; + + pdata = devm_kzalloc(dev, sizeof(struct wcd9xxx_pdata), + GFP_KERNEL); + if (!pdata) + return NULL; + + /* Parse power supplies */ + msm_cdc_get_power_supplies(dev, &pdata->regulator, + &pdata->num_supplies); + if (!pdata->regulator || (pdata->num_supplies <= 0)) { + dev_err(dev, "%s: no power supplies defined for codec\n", + __func__); + goto err_power_sup; + } + + /* Parse micbias info */ + wcd9xxx_dt_parse_micbias_info(dev, &pdata->micbias); + + pdata->wcd_rst_np = of_parse_phandle(dev->of_node, + "qcom,wcd-rst-gpio-node", 0); + if (!pdata->wcd_rst_np) { + dev_err(dev, "%s: Looking up %s property in node %s failed\n", + __func__, "qcom,wcd-rst-gpio-node", + dev->of_node->full_name); + goto err_parse_dt_prop; + } + + if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-mclk-clk-rate", + &prop_val))) + pdata->mclk_rate = prop_val; + + if (pdata->mclk_rate != WCD9XXX_MCLK_CLK_9P6HZ && + pdata->mclk_rate != WCD9XXX_MCLK_CLK_12P288MHZ) { + dev_err(dev, "%s: Invalid mclk_rate = %u\n", __func__, + pdata->mclk_rate); + goto err_parse_dt_prop; + } + + if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-dmic-sample-rate", + &prop_val))) + dmic_sample_rate = prop_val; + + pdata->dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev, + dmic_sample_rate, + pdata->mclk_rate, + "audio_dmic_rate"); + if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-mad-dmic-rate", + &prop_val))) + mad_dmic_sample_rate = prop_val; + + pdata->mad_dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev, + mad_dmic_sample_rate, + pdata->mclk_rate, + "mad_dmic_rate"); + + if (of_find_property(dev->of_node, "qcom,cdc-ecpp-dmic-rate", NULL)) { + rc = wcd9xxx_read_of_property_u32(dev, + "qcom,cdc-ecpp-dmic-rate", + &prop_val); + if (!rc) + ecpp_dmic_sample_rate = prop_val; + } + + pdata->ecpp_dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev, + ecpp_dmic_sample_rate, + pdata->mclk_rate, + "ecpp_dmic_rate"); + + if (!(of_property_read_u32(dev->of_node, + "qcom,cdc-dmic-clk-drv-strength", + &prop_val))) { + dmic_clk_drive = prop_val; + + if (dmic_clk_drive != 2 && dmic_clk_drive != 4 && + dmic_clk_drive != 8 && dmic_clk_drive != 16) + dev_err(dev, "Invalid cdc-dmic-clk-drv-strength %d\n", + dmic_clk_drive); + } + + pdata->dmic_clk_drv = dmic_clk_drive; + + return pdata; + +err_parse_dt_prop: + devm_kfree(dev, pdata->regulator); + pdata->regulator = NULL; + pdata->num_supplies = 0; +err_power_sup: + devm_kfree(dev, pdata); + return NULL; +} +EXPORT_SYMBOL(wcd9xxx_populate_dt_data); + +static bool is_wcd9xxx_reg_power_down(struct wcd9xxx *wcd9xxx, u16 rreg) +{ + bool ret = false; + int i; + struct wcd9xxx_power_region *wcd9xxx_pwr; + + if (!wcd9xxx) + return ret; + + for (i = 0; i < WCD9XXX_MAX_PWR_REGIONS; i++) { + wcd9xxx_pwr = wcd9xxx->wcd9xxx_pwr[i]; + if (!wcd9xxx_pwr) + continue; + if (((wcd9xxx_pwr->pwr_collapse_reg_min == 0) && + (wcd9xxx_pwr->pwr_collapse_reg_max == 0)) || + (wcd9xxx_pwr->power_state == + WCD_REGION_POWER_COLLAPSE_REMOVE)) + ret = false; + else if (((wcd9xxx_pwr->power_state == + WCD_REGION_POWER_DOWN) || + (wcd9xxx_pwr->power_state == + WCD_REGION_POWER_COLLAPSE_BEGIN)) && + (rreg >= wcd9xxx_pwr->pwr_collapse_reg_min) && + (rreg <= wcd9xxx_pwr->pwr_collapse_reg_max)) + ret = true; + } + return ret; +} + +/* + * wcd9xxx_page_write: + * Retrieve page number from register and + * write that page number to the page address. + * Called under io_lock acquisition. + * + * @wcd9xxx: pointer to wcd9xxx + * @reg: Register address from which page number is retrieved + * + * Returns 0 for success and negative error code for failure. + */ +int wcd9xxx_page_write(struct wcd9xxx *wcd9xxx, unsigned short *reg) +{ + int ret = 0; + unsigned short c_reg, reg_addr; + u8 pg_num, prev_pg_num; + + if (wcd9xxx->type != WCD9335 && wcd9xxx->type != WCD934X) + return ret; + + c_reg = *reg; + pg_num = c_reg >> 8; + reg_addr = c_reg & 0xff; + if (wcd9xxx->prev_pg_valid) { + prev_pg_num = wcd9xxx->prev_pg; + if (prev_pg_num != pg_num) { + ret = wcd9xxx->write_dev( + wcd9xxx, PAGE_REG_ADDR, 1, + (void *) &pg_num, false); + if (ret < 0) + pr_err("page write error, pg_num: 0x%x\n", + pg_num); + else { + wcd9xxx->prev_pg = pg_num; + dev_dbg(wcd9xxx->dev, "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + } else { + ret = wcd9xxx->write_dev( + wcd9xxx, PAGE_REG_ADDR, 1, (void *) &pg_num, + false); + if (ret < 0) + pr_err("page write error, pg_num: 0x%x\n", pg_num); + else { + wcd9xxx->prev_pg = pg_num; + wcd9xxx->prev_pg_valid = true; + dev_dbg(wcd9xxx->dev, "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + *reg = reg_addr; + return ret; +} +EXPORT_SYMBOL(wcd9xxx_page_write); + +static int regmap_bus_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + unsigned short c_reg, rreg; + int ret, i; + + if (!wcd9xxx) { + dev_err(dev, "%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + + mutex_lock(&wcd9xxx->io_lock); + c_reg = *(u16 *)reg; + rreg = c_reg; + + if (is_wcd9xxx_reg_power_down(wcd9xxx, rreg)) { + ret = 0; + for (i = 0; i < val_size; i++) + ((u8 *)val)[i] = 0; + goto err; + } + ret = wcd9xxx_page_write(wcd9xxx, &c_reg); + if (ret) + goto err; + ret = wcd9xxx->read_dev(wcd9xxx, c_reg, val_size, val, false); + if (ret < 0) + dev_err(dev, "%s: Codec read failed (%d), reg: 0x%x, size:%zd\n", + __func__, ret, rreg, val_size); + else { + for (i = 0; i < val_size; i++) + dev_dbg(dev, "%s: Read 0x%02x from 0x%x\n", + __func__, ((u8 *)val)[i], rreg + i); + } +err: + mutex_unlock(&wcd9xxx->io_lock); + + return ret; +} + +static int regmap_bus_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct device *dev = context; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + unsigned short c_reg, rreg; + int ret, i; + + if (!wcd9xxx) { + dev_err(dev, "%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + mutex_lock(&wcd9xxx->io_lock); + c_reg = *(u16 *)reg; + rreg = c_reg; + + if (is_wcd9xxx_reg_power_down(wcd9xxx, rreg)) { + ret = 0; + goto err; + } + ret = wcd9xxx_page_write(wcd9xxx, &c_reg); + if (ret) + goto err; + + for (i = 0; i < val_size; i++) + dev_dbg(dev, "Write %02x to 0x%x\n", ((u8 *)val)[i], + rreg + i); + + ret = wcd9xxx->write_dev(wcd9xxx, c_reg, val_size, (void *) val, + false); + if (ret < 0) + dev_err(dev, "%s: Codec write failed (%d), reg:0x%x, size:%zd\n", + __func__, ret, rreg, val_size); + +err: + mutex_unlock(&wcd9xxx->io_lock); + return ret; +} + +static int regmap_bus_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev); + + if (!wcd9xxx) + return -EINVAL; + + WARN_ON(count < REG_BYTES); + + if (count > (REG_BYTES + VAL_BYTES)) { + if (wcd9xxx->multi_reg_write) + return wcd9xxx->multi_reg_write(wcd9xxx, + data, count); + } else + return regmap_bus_gather_write(context, data, REG_BYTES, + data + REG_BYTES, + count - REG_BYTES); + + dev_err(dev, "%s: bus multi reg write failure\n", __func__); + + return -EINVAL; +} + +static struct regmap_bus regmap_bus_config = { + .write = regmap_bus_write, + .gather_write = regmap_bus_gather_write, + .read = regmap_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +/* + * wcd9xxx_regmap_init: + * Initialize wcd9xxx register map + * + * @dev: pointer to wcd device + * @config: pointer to register map config + * + * Returns pointer to regmap structure for success + * or NULL in case of failure. + */ +struct regmap *wcd9xxx_regmap_init(struct device *dev, + const struct regmap_config *config) +{ + return devm_regmap_init(dev, ®map_bus_config, dev, config); +} +EXPORT_SYMBOL(wcd9xxx_regmap_init); + +/* + * wcd9xxx_reset: + * Reset wcd9xxx codec + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_reset(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + int value; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->wcd_rst_np) { + dev_err(dev, "%s: reset gpio device node not specified\n", + __func__); + return -EINVAL; + } + + value = msm_cdc_pinctrl_get_state(wcd9xxx->wcd_rst_np); + if (value > 0) { + wcd9xxx->avoid_cdc_rstlow = 1; + return 0; + } + + rc = msm_cdc_pinctrl_select_sleep_state(wcd9xxx->wcd_rst_np); + if (rc) { + dev_err(dev, "%s: wcd sleep state request fail!\n", + __func__); + return rc; + } + + /* 20ms sleep required after pulling the reset gpio to LOW */ + msleep(20); + + rc = msm_cdc_pinctrl_select_active_state(wcd9xxx->wcd_rst_np); + if (rc) { + dev_err(dev, "%s: wcd active state request fail!\n", + __func__); + return rc; + } + msleep(20); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_reset); + +/* + * wcd9xxx_reset_low: + * Pull the wcd9xxx codec reset_n to low + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_reset_low(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + if (!wcd9xxx->wcd_rst_np) { + dev_err(dev, "%s: reset gpio device node not specified\n", + __func__); + return -EINVAL; + } + if (wcd9xxx->avoid_cdc_rstlow) { + wcd9xxx->avoid_cdc_rstlow = 0; + dev_dbg(dev, "%s: avoid pull down of reset GPIO\n", __func__); + return 0; + } + + rc = msm_cdc_pinctrl_select_sleep_state(wcd9xxx->wcd_rst_np); + if (rc) + dev_err(dev, "%s: wcd sleep state request fail!\n", + __func__); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_reset_low); + +/* + * wcd9xxx_bringup: + * Toggle reset analog and digital cores of wcd9xxx codec + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_bringup(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + codec_bringup_fn cdc_bup_fn; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + cdc_bup_fn = wcd9xxx_bringup_fn(wcd9xxx->type); + if (!cdc_bup_fn) { + dev_err(dev, "%s: Codec bringup fn NULL!\n", + __func__); + return -EINVAL; + } + rc = cdc_bup_fn(wcd9xxx); + if (rc) + dev_err(dev, "%s: Codec bringup error, rc: %d\n", + __func__, rc); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_bringup); + +/* + * wcd9xxx_bringup: + * Set analog and digital cores of wcd9xxx codec in reset state + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_bringdown(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + codec_bringdown_fn cdc_bdown_fn; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + cdc_bdown_fn = wcd9xxx_bringdown_fn(wcd9xxx->type); + if (!cdc_bdown_fn) { + dev_err(dev, "%s: Codec bring down fn NULL!\n", + __func__); + return -EINVAL; + } + rc = cdc_bdown_fn(wcd9xxx); + if (rc) + dev_err(dev, "%s: Codec bring down error, rc: %d\n", + __func__, rc); + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_bringdown); + +/* + * wcd9xxx_get_codec_info: + * Fill codec specific information like interrupts, version + * + * @dev: pointer to wcd device + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_get_codec_info(struct device *dev) +{ + struct wcd9xxx *wcd9xxx; + int rc; + codec_type_fn cdc_type_fn; + struct wcd9xxx_codec_type *cinfo; + + if (!dev) + return -ENODEV; + + wcd9xxx = dev_get_drvdata(dev); + if (!wcd9xxx) + return -EINVAL; + + cdc_type_fn = wcd9xxx_get_codec_info_fn(wcd9xxx->type); + if (!cdc_type_fn) { + dev_err(dev, "%s: Codec fill type fn NULL!\n", + __func__); + return -EINVAL; + } + + cinfo = wcd9xxx->codec_type; + if (!cinfo) + return -EINVAL; + + rc = cdc_type_fn(wcd9xxx, cinfo); + if (rc) { + dev_err(dev, "%s: Codec type fill failed, rc:%d\n", + __func__, rc); + return rc; + + } + + switch (wcd9xxx->type) { + case WCD934X: + cinfo->dev = tavil_devs; + cinfo->size = ARRAY_SIZE(tavil_devs); + break; + case WCD9335: + cinfo->dev = tasha_devs; + cinfo->size = ARRAY_SIZE(tasha_devs); + break; + case WCD9330: + cinfo->dev = tomtom_devs; + cinfo->size = ARRAY_SIZE(tomtom_devs); + break; + default: + cinfo->dev = NULL; + cinfo->size = 0; + break; + } + + return rc; +} +EXPORT_SYMBOL(wcd9xxx_get_codec_info); + +/* + * wcd9xxx_core_irq_init: + * Initialize wcd9xxx codec irq instance + * + * @wcd9xxx_core_res: pointer to wcd core resource + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_core_irq_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res) +{ + int ret = 0; + + if (!wcd9xxx_core_res) + return -EINVAL; + + if (wcd9xxx_core_res->irq != 1) { + ret = wcd9xxx_irq_init(wcd9xxx_core_res); + if (ret) + pr_err("IRQ initialization failed\n"); + } + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_core_irq_init); + +/* + * wcd9xxx_assign_irq: + * Assign irq and irq_base to wcd9xxx core resource + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @irq: irq number + * @irq_base: base irq number + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_assign_irq( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + unsigned int irq, + unsigned int irq_base) +{ + if (!wcd9xxx_core_res) + return -EINVAL; + + wcd9xxx_core_res->irq = irq; + wcd9xxx_core_res->irq_base = irq_base; + + return 0; +} +EXPORT_SYMBOL(wcd9xxx_assign_irq); + +/* + * wcd9xxx_core_res_init: + * Initialize wcd core resource instance + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @num_irqs: number of irqs for wcd9xxx core + * @num_irq_regs: number of irq registers + * @wcd_regmap: pointer to the wcd register map + * + * Returns 0 for success or negative error code in case of failure + */ +int wcd9xxx_core_res_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + int num_irqs, int num_irq_regs, struct regmap *wcd_regmap) +{ + if (!wcd9xxx_core_res || !wcd_regmap) + return -EINVAL; + + mutex_init(&wcd9xxx_core_res->pm_lock); + wcd9xxx_core_res->wlock_holders = 0; + wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE; + init_waitqueue_head(&wcd9xxx_core_res->pm_wq); + pm_qos_add_request(&wcd9xxx_core_res->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + wcd9xxx_core_res->num_irqs = num_irqs; + wcd9xxx_core_res->num_irq_regs = num_irq_regs; + wcd9xxx_core_res->wcd_core_regmap = wcd_regmap; + + pr_info("%s: num_irqs = %d, num_irq_regs = %d\n", + __func__, wcd9xxx_core_res->num_irqs, + wcd9xxx_core_res->num_irq_regs); + + return 0; +} +EXPORT_SYMBOL(wcd9xxx_core_res_init); + +/* + * wcd9xxx_core_res_deinit: + * Deinit wcd core resource instance + * + * @wcd9xxx_core_res: pointer to wcd core resource + */ +void wcd9xxx_core_res_deinit(struct wcd9xxx_core_resource *wcd9xxx_core_res) +{ + if (!wcd9xxx_core_res) + return; + + pm_qos_remove_request(&wcd9xxx_core_res->pm_qos_req); + mutex_destroy(&wcd9xxx_core_res->pm_lock); +} +EXPORT_SYMBOL(wcd9xxx_core_res_deinit); + +/* + * wcd9xxx_pm_cmpxchg: + * Check old state and exchange with pm new state + * if old state matches with current state + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @o: pm old state + * @n: pm new state + * + * Returns old state + */ +enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + enum wcd9xxx_pm_state o, + enum wcd9xxx_pm_state n) +{ + enum wcd9xxx_pm_state old; + + if (!wcd9xxx_core_res) + return o; + + mutex_lock(&wcd9xxx_core_res->pm_lock); + old = wcd9xxx_core_res->pm_state; + if (old == o) + wcd9xxx_core_res->pm_state = n; + mutex_unlock(&wcd9xxx_core_res->pm_lock); + + return old; +} +EXPORT_SYMBOL(wcd9xxx_pm_cmpxchg); + +/* + * wcd9xxx_core_res_suspend: + * Suspend callback function for wcd9xxx core + * + * @wcd9xxx_core_res: pointer to wcd core resource + * @pm_message_t: pm message + * + * Returns 0 for success or negative error code for failure/busy + */ +int wcd9xxx_core_res_suspend( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + pm_message_t pmesg) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + /* + * pm_qos_update_request() can be called after this suspend chain call + * started. thus suspend can be called while lock is being held + */ + mutex_lock(&wcd9xxx_core_res->pm_lock); + if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_SLEEPABLE) { + pr_debug("%s: suspending system, state %d, wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + wcd9xxx_core_res->pm_state = WCD9XXX_PM_ASLEEP; + } else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_AWAKE) { + /* + * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE + * then set to WCD9XXX_PM_ASLEEP + */ + pr_debug("%s: waiting to suspend system, state %d, wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + mutex_unlock(&wcd9xxx_core_res->pm_lock); + if (!(wait_event_timeout(wcd9xxx_core_res->pm_wq, + wcd9xxx_pm_cmpxchg(wcd9xxx_core_res, + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_ASLEEP) == + WCD9XXX_PM_SLEEPABLE, + HZ))) { + pr_debug("%s: suspend failed state %d, wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + ret = -EBUSY; + } else { + pr_debug("%s: done, state %d, wlock %d\n", __func__, + wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + } + mutex_lock(&wcd9xxx_core_res->pm_lock); + } else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) { + pr_warn("%s: system is already suspended, state %d, wlock %dn", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + } + mutex_unlock(&wcd9xxx_core_res->pm_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_core_res_suspend); + +/* + * wcd9xxx_core_res_resume: + * Resume callback function for wcd9xxx core + * + * @wcd9xxx_core_res: pointer to wcd core resource + * + * Returns 0 for success or negative error code for failure/busy + */ +int wcd9xxx_core_res_resume( + struct wcd9xxx_core_resource *wcd9xxx_core_res) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + mutex_lock(&wcd9xxx_core_res->pm_lock); + if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) { + pr_debug("%s: resuming system, state %d, wlock %d\n", __func__, + wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE; + } else { + pr_warn("%s: system is already awake, state %d wlock %d\n", + __func__, wcd9xxx_core_res->pm_state, + wcd9xxx_core_res->wlock_holders); + } + mutex_unlock(&wcd9xxx_core_res->pm_lock); + wake_up_all(&wcd9xxx_core_res->pm_wq); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_core_res_resume); + +/* + * wcd9xxx_get_intf_type: + * Get interface type of wcd9xxx core + * + * Returns interface type + */ +enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void) +{ + return wcd9xxx_intf; +} +EXPORT_SYMBOL(wcd9xxx_get_intf_type); + +/* + * wcd9xxx_set_intf_type: + * Set interface type of wcd9xxx core + * + */ +void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status intf_status) +{ + wcd9xxx_intf = intf_status; +} +EXPORT_SYMBOL(wcd9xxx_set_intf_type); + +/* + * wcd9xxx_set_power_state: set power state for the region + * @wcd9xxx: handle to wcd core + * @state: power state to be set + * @region: region index + * + * Returns error code in case of failure or 0 for success + */ +int wcd9xxx_set_power_state(struct wcd9xxx *wcd9xxx, + enum codec_power_states state, + enum wcd_power_regions region) +{ + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + if ((region < 0) || (region >= WCD9XXX_MAX_PWR_REGIONS)) { + dev_err(wcd9xxx->dev, "%s: region index %d out of bounds\n", + __func__, region); + return -EINVAL; + } + if (!wcd9xxx->wcd9xxx_pwr[region]) { + dev_err(wcd9xxx->dev, "%s: memory not created for region: %d\n", + __func__, region); + return -EINVAL; + } + mutex_lock(&wcd9xxx->io_lock); + wcd9xxx->wcd9xxx_pwr[region]->power_state = state; + mutex_unlock(&wcd9xxx->io_lock); + + return 0; +} +EXPORT_SYMBOL(wcd9xxx_set_power_state); + +/* + * wcd9xxx_get_current_power_state: Get power state of the region + * @wcd9xxx: handle to wcd core + * @region: region index + * + * Returns current power state of the region or error code for failure + */ +int wcd9xxx_get_current_power_state(struct wcd9xxx *wcd9xxx, + enum wcd_power_regions region) +{ + int state; + + if (!wcd9xxx) { + pr_err("%s: wcd9xxx is NULL\n", __func__); + return -EINVAL; + } + + if ((region < 0) || (region >= WCD9XXX_MAX_PWR_REGIONS)) { + dev_err(wcd9xxx->dev, "%s: region index %d out of bounds\n", + __func__, region); + return -EINVAL; + } + if (!wcd9xxx->wcd9xxx_pwr[region]) { + dev_err(wcd9xxx->dev, "%s: memory not created for region: %d\n", + __func__, region); + return -EINVAL; + } + + mutex_lock(&wcd9xxx->io_lock); + state = wcd9xxx->wcd9xxx_pwr[region]->power_state; + mutex_unlock(&wcd9xxx->io_lock); + + return state; +} +EXPORT_SYMBOL(wcd9xxx_get_current_power_state); diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile new file mode 100644 index 000000000000..f00d59de9db2 --- /dev/null +++ b/drivers/misc/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for misc devices that really don't fit anywhere else. +# + +obj-y += qcom/ diff --git a/drivers/misc/qcom/Kconfig b/drivers/misc/qcom/Kconfig new file mode 100644 index 000000000000..e8a79605f361 --- /dev/null +++ b/drivers/misc/qcom/Kconfig @@ -0,0 +1,19 @@ +config MSM_QDSP6V2_CODECS + bool "Audio QDSP6V2 APR support" + 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..c0828dc6d1bc --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/aac_in.c @@ -0,0 +1,709 @@ +/* + * 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 + +#define MAX_SAMPLE_RATE_384K 384000 + +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; + } + + if (cfg->sample_rate > MAX_SAMPLE_RATE_384K) { + pr_err("%s: ERROR: invalid sample rate = %u", + __func__, cfg->sample_rate); + 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..b24764cf9516 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/amrnb_in.c @@ -0,0 +1,402 @@ +/* + * 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..77b4be54f10b --- /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..458a80c4c5bf --- /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..b8972e3d8b6e --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c @@ -0,0 +1,778 @@ +/* + * 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 "q6audio_common.h" +#include "audio_utils_aio.h" +#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; + + struct mutex lock; + + 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; + } + 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); +} + +static void audio_effects_deinit_pp(struct audio_client *ac) +{ + if (!ac) { + pr_err("%s: audio client null to deinit pp\n", __func__); + return; + } +} + +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__); + + mutex_lock(&effects->lock); + + 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; + mutex_unlock(&effects->lock); + 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; + mutex_unlock(&effects->lock); + 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; + mutex_unlock(&effects->lock); + 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; + mutex_unlock(&effects->lock); + 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; + mutex_unlock(&effects->lock); + 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__); + } + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_WRITE: { + char *bufptr = NULL; + uint32_t idx = 0; + uint32_t size = 0; + + mutex_lock(&effects->lock); + + if (!effects->started) { + rc = -EFAULT; + mutex_unlock(&effects->lock); + 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; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + if (!atomic_read(&effects->out_count)) { + pr_err("%s: pcm stopped out_count 0\n", __func__); + rc = -EFAULT; + mutex_unlock(&effects->lock); + 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; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + rc = q6asm_write(effects->ac, + effects->config.buf_cfg.output_len, + 0, 0, NO_TIMESTAMP); + if (rc < 0) { + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + atomic_dec(&effects->out_count); + } else { + pr_err("%s: AUDIO_EFFECTS_WRITE: Buffer dropped\n", + __func__); + } + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_READ: { + char *bufptr = NULL; + uint32_t idx = 0; + uint32_t size = 0; + + mutex_lock(&effects->lock); + + if (!effects->started) { + rc = -EFAULT; + mutex_unlock(&effects->lock); + 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; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + if (!atomic_read(&effects->in_count)) { + pr_err("%s: pcm stopped in_count 0\n", __func__); + rc = -EFAULT; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + + bufptr = q6asm_is_cpu_buf_avail(OUT, effects->ac, &size, &idx); + if (bufptr) { + if (!((void *)arg)) { + rc = -EFAULT; + mutex_unlock(&effects->lock); + 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; + mutex_unlock(&effects->lock); + goto ioctl_fail; + } + } + mutex_unlock(&effects->lock); + 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; + 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__); + mutex_lock(&effects->lock); + 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); + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_SET_BUF_LEN: { + mutex_lock(&effects->lock); + 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); + mutex_unlock(&effects->lock); + 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); + mutex_lock(&effects->lock); + 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; + } + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_SET_PP_PARAMS: { + mutex_lock(&effects->lock); + 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__); + mutex_unlock(&effects->lock); + return -EFAULT; + } + rc = audio_effects_set_pp_param(effects, argvalues); + mutex_unlock(&effects->lock); + 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; + + mutex_lock(&effects->lock); + 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; + mutex_unlock(&effects->lock); + 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); + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_SET_BUF_LEN32: { + struct msm_hwacc_buf_cfg32 buf_cfg32; + struct msm_hwacc_effects_config *config = &effects->config; + + mutex_lock(&effects->lock); + 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; + mutex_unlock(&effects->lock); + 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); + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_GET_BUF_AVAIL32: { + struct msm_hwacc_buf_avail32 buf_avail; + + memset(&buf_avail, 0, sizeof(buf_avail)); + + mutex_lock(&effects->lock); + 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; + } + mutex_unlock(&effects->lock); + break; + } + case AUDIO_EFFECTS_SET_PP_PARAMS32: { + long argvalues[MAX_PP_PARAMS_SZ] = {0}; + int argvalues32[MAX_PP_PARAMS_SZ] = {0}; + + mutex_lock(&effects->lock); + 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__); + mutex_unlock(&effects->lock); + return -EFAULT; + } + for (i = 0; i < MAX_PP_PARAMS_SZ; i++) + argvalues[i] = argvalues32[i]; + + rc = audio_effects_set_pp_param(effects, argvalues); + mutex_unlock(&effects->lock); + 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); + + mutex_destroy(&effects->lock); + 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); + mutex_init(&effects->lock); + + 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..80f6e57e4699 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c @@ -0,0 +1,2142 @@ +/* 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 && + addr + len > addr) { + /* to avoid integer addition overflow */ + + /* 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 && + addr + len > addr) + 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; + memset(&usr_evt, 0, sizeof(struct msm_audio_event)); + + 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); + if (rc < 0) { + pr_err("%s: audio process event failed, rc = %ld", + __func__, rc); + return rc; + } + + 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..ea3113217281 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c @@ -0,0 +1,106 @@ +/* + * 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..3da46b647002 --- /dev/null +++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c @@ -0,0 +1,2465 @@ +/* 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 atomic_t 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 == atomic_cmpxchg(&s_opened_devs[ind], 0, minor)) { + pr_err("%s: device %d is already opened\n", + __func__, minor); + return USF_UNDEF_DEV_ID; + } else { + 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); + + atomic_set(&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/pinctrl/Makefile b/drivers/pinctrl/Makefile new file mode 100644 index 000000000000..0e0c511e18d9 --- /dev/null +++ b/drivers/pinctrl/Makefile @@ -0,0 +1,2 @@ + +obj-y += qcom/ diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h new file mode 120000 index 000000000000..2b68c81aa5d3 --- /dev/null +++ b/drivers/pinctrl/core.h @@ -0,0 +1 @@ +../../../../drivers/pinctrl/core.h \ No newline at end of file diff --git a/drivers/pinctrl/pinctrl-utils.h b/drivers/pinctrl/pinctrl-utils.h new file mode 120000 index 000000000000..3303afdf9f46 --- /dev/null +++ b/drivers/pinctrl/pinctrl-utils.h @@ -0,0 +1 @@ +../../../../drivers/pinctrl/pinctrl-utils.h \ No newline at end of file diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile new file mode 100644 index 000000000000..cd1b749ae5ee --- /dev/null +++ b/drivers/pinctrl/qcom/Makefile @@ -0,0 +1,3 @@ + +obj-$(CONFIG_PINCTRL_WCD) += pinctrl-wcd.o +obj-$(CONFIG_PINCTRL_LPI) += pinctrl-lpi.o diff --git a/drivers/pinctrl/qcom/pinctrl-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpi.c new file mode 100644 index 000000000000..fedd5f0aee22 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-lpi.c @@ -0,0 +1,647 @@ +/* + * 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 "../core.h" +#include "../pinctrl-utils.h" + +#define LPI_ADDRESS_SIZE 0xC000 + +#define LPI_GPIO_REG_VAL_CTL 0x00 +#define LPI_GPIO_REG_DIR_CTL 0x04 + +#define LPI_GPIO_REG_PULL_SHIFT 0x0 +#define LPI_GPIO_REG_PULL_MASK 0x3 + +#define LPI_GPIO_REG_FUNCTION_SHIFT 0x2 +#define LPI_GPIO_REG_FUNCTION_MASK 0x3C + +#define LPI_GPIO_REG_OUT_STRENGTH_SHIFT 0x6 +#define LPI_GPIO_REG_OUT_STRENGTH_MASK 0x1C0 + +#define LPI_GPIO_REG_OE_SHIFT 0x9 +#define LPI_GPIO_REG_OE_MASK 0x200 + +#define LPI_GPIO_REG_DIR_SHIFT 0x1 +#define LPI_GPIO_REG_DIR_MASK 0x2 + +#define LPI_GPIO_BIAS_DISABLE 0x0 +#define LPI_GPIO_PULL_DOWN 0x1 +#define LPI_GPIO_KEEPER 0x2 +#define LPI_GPIO_PULL_UP 0x3 + +#define LPI_GPIO_FUNC_GPIO "gpio" +#define LPI_GPIO_FUNC_FUNC1 "func1" +#define LPI_GPIO_FUNC_FUNC2 "func2" +#define LPI_GPIO_FUNC_FUNC3 "func3" +#define LPI_GPIO_FUNC_FUNC4 "func4" +#define LPI_GPIO_FUNC_FUNC5 "func5" + +static bool lpi_dev_up; + +/* The index of each function in lpi_gpio_functions[] array */ +enum lpi_gpio_func_index { + LPI_GPIO_FUNC_INDEX_GPIO = 0x00, + LPI_GPIO_FUNC_INDEX_FUNC1 = 0x01, + LPI_GPIO_FUNC_INDEX_FUNC2 = 0x02, + LPI_GPIO_FUNC_INDEX_FUNC3 = 0x03, + LPI_GPIO_FUNC_INDEX_FUNC4 = 0x04, + LPI_GPIO_FUNC_INDEX_FUNC5 = 0x05, +}; + +/** + * struct lpi_gpio_pad - keep current GPIO settings + * @offset: Nth GPIO in supported GPIOs. + * @output_enabled: Set to true if GPIO output logic is enabled. + * @value: value of a pin + * @base: Address base of LPI GPIO PAD. + * @pullup: Constant current which flow through GPIO output buffer. + * @strength: No, Low, Medium, High + * @function: See lpi_gpio_functions[] + */ +struct lpi_gpio_pad { + u16 offset; + bool output_enabled; + bool value; + char __iomem *base; + unsigned int pullup; + unsigned int strength; + unsigned int function; +}; + +struct lpi_gpio_state { + struct device *dev; + struct pinctrl_dev *ctrl; + struct gpio_chip chip; + char __iomem *base; +}; + +static const char *const lpi_gpio_groups[] = { + "gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7", + "gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14", + "gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21", + "gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28", + "gpio29", "gpio30", "gpio31", +}; + +static const u32 lpi_offset[] = { + 0x00000000, + 0x00001000, + 0x00002000, + 0x00002010, + 0x00003000, + 0x00003010, + 0x00004000, + 0x00004010, + 0x00005000, + 0x00005010, + 0x00005020, + 0x00005030, + 0x00006000, + 0x00006010, + 0x00007000, + 0x00007010, + 0x00005040, + 0x00005050, + 0x00008000, + 0x00008010, + 0x00008020, + 0x00008030, + 0x00008040, + 0x00008050, + 0x00008060, + 0x00008070, + 0x00009000, + 0x00009010, + 0x0000A000, + 0x0000A010, + 0x0000B000, + 0x0000B010, +}; + +static const char *const lpi_gpio_functions[] = { + [LPI_GPIO_FUNC_INDEX_GPIO] = LPI_GPIO_FUNC_GPIO, + [LPI_GPIO_FUNC_INDEX_FUNC1] = LPI_GPIO_FUNC_FUNC1, + [LPI_GPIO_FUNC_INDEX_FUNC2] = LPI_GPIO_FUNC_FUNC2, + [LPI_GPIO_FUNC_INDEX_FUNC3] = LPI_GPIO_FUNC_FUNC3, + [LPI_GPIO_FUNC_INDEX_FUNC4] = LPI_GPIO_FUNC_FUNC4, + [LPI_GPIO_FUNC_INDEX_FUNC5] = LPI_GPIO_FUNC_FUNC5, +}; + +static int lpi_gpio_read(struct lpi_gpio_pad *pad, unsigned int addr) +{ + int ret; + + if (!lpi_dev_up) { + pr_err_ratelimited("%s: ADSP is down due to SSR, return\n", + __func__); + return 0; + } + + ret = ioread32(pad->base + pad->offset + addr); + if (ret < 0) + pr_err("%s: read 0x%x failed\n", __func__, addr); + + return ret; +} + +static int lpi_gpio_write(struct lpi_gpio_pad *pad, unsigned int addr, + unsigned int val) +{ + if (!lpi_dev_up) { + pr_err_ratelimited("%s: ADSP is down due to SSR, return\n", + __func__); + return 0; + } + + iowrite32(val, pad->base + pad->offset + addr); + return 0; +} + +static int lpi_gpio_get_groups_count(struct pinctrl_dev *pctldev) +{ + /* Every PIN is a group */ + return pctldev->desc->npins; +} + +static const char *lpi_gpio_get_group_name(struct pinctrl_dev *pctldev, + unsigned int pin) +{ + return pctldev->desc->pins[pin].name; +} + +static int lpi_gpio_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int pin, + const unsigned int **pins, + unsigned int *num_pins) +{ + *pins = &pctldev->desc->pins[pin].number; + *num_pins = 1; + return 0; +} + +static const struct pinctrl_ops lpi_gpio_pinctrl_ops = { + .get_groups_count = lpi_gpio_get_groups_count, + .get_group_name = lpi_gpio_get_group_name, + .get_group_pins = lpi_gpio_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int lpi_gpio_get_functions_count(struct pinctrl_dev *pctldev) +{ + return ARRAY_SIZE(lpi_gpio_functions); +} + +static const char *lpi_gpio_get_function_name(struct pinctrl_dev *pctldev, + unsigned int function) +{ + return lpi_gpio_functions[function]; +} + +static int lpi_gpio_get_function_groups(struct pinctrl_dev *pctldev, + unsigned int function, + const char *const **groups, + unsigned *const num_qgroups) +{ + *groups = lpi_gpio_groups; + *num_qgroups = pctldev->desc->npins; + return 0; +} + +static int lpi_gpio_set_mux(struct pinctrl_dev *pctldev, unsigned int function, + unsigned int pin) +{ + struct lpi_gpio_pad *pad; + unsigned int val; + + pad = pctldev->desc->pins[pin].drv_data; + + pad->function = function; + + val = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL); + val &= ~(LPI_GPIO_REG_FUNCTION_MASK); + val |= pad->function << LPI_GPIO_REG_FUNCTION_SHIFT; + lpi_gpio_write(pad, LPI_GPIO_REG_VAL_CTL, val); + return 0; +} + +static const struct pinmux_ops lpi_gpio_pinmux_ops = { + .get_functions_count = lpi_gpio_get_functions_count, + .get_function_name = lpi_gpio_get_function_name, + .get_function_groups = lpi_gpio_get_function_groups, + .set_mux = lpi_gpio_set_mux, +}; + +static int lpi_config_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + unsigned int param = pinconf_to_config_param(*config); + struct lpi_gpio_pad *pad; + unsigned int arg; + + pad = pctldev->desc->pins[pin].drv_data; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + arg = pad->pullup = LPI_GPIO_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = pad->pullup == LPI_GPIO_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + arg = pad->pullup = LPI_GPIO_KEEPER; + break; + case PIN_CONFIG_BIAS_PULL_UP: + arg = pad->pullup == LPI_GPIO_PULL_UP; + break; + case PIN_CONFIG_INPUT_ENABLE: + case PIN_CONFIG_OUTPUT: + arg = pad->output_enabled; + break; + default: + return -EINVAL; + } + + *config = pinconf_to_config_packed(param, arg); + return 0; +} + +static unsigned int lpi_drive_to_regval(u32 arg) +{ + return (arg/2 - 1); +} + +static int lpi_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int nconfs) +{ + struct lpi_gpio_pad *pad; + unsigned int param, arg; + int i, ret = 0, val; + + pad = pctldev->desc->pins[pin].drv_data; + + for (i = 0; i < nconfs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + dev_dbg(pctldev->dev, "%s: param: %d arg: %d pin: %d\n", + __func__, param, arg, pin); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + pad->pullup = LPI_GPIO_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + pad->pullup = LPI_GPIO_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + pad->pullup = LPI_GPIO_KEEPER; + break; + case PIN_CONFIG_BIAS_PULL_UP: + pad->pullup = LPI_GPIO_PULL_UP; + break; + case PIN_CONFIG_INPUT_ENABLE: + pad->output_enabled = false; + break; + case PIN_CONFIG_OUTPUT: + pad->output_enabled = true; + pad->value = arg; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + pad->strength = arg; + break; + default: + ret = -EINVAL; + goto done; + } + } + + val = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL); + val &= ~(LPI_GPIO_REG_PULL_MASK | LPI_GPIO_REG_OUT_STRENGTH_MASK | + LPI_GPIO_REG_OE_MASK); + val |= pad->pullup << LPI_GPIO_REG_PULL_SHIFT; + val |= lpi_drive_to_regval(pad->strength) << + LPI_GPIO_REG_OUT_STRENGTH_SHIFT; + if (pad->output_enabled) + val |= pad->value << LPI_GPIO_REG_OE_SHIFT; + + lpi_gpio_write(pad, LPI_GPIO_REG_VAL_CTL, val); + lpi_gpio_write(pad, LPI_GPIO_REG_DIR_CTL, + pad->output_enabled << LPI_GPIO_REG_DIR_SHIFT); +done: + return ret; +} + +static const struct pinconf_ops lpi_gpio_pinconf_ops = { + .is_generic = true, + .pin_config_group_get = lpi_config_get, + .pin_config_group_set = lpi_config_set, +}; + +static int lpi_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) +{ + struct lpi_gpio_state *state = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); + + return lpi_config_set(state->ctrl, pin, &config, 1); +} + +static int lpi_gpio_direction_output(struct gpio_chip *chip, + unsigned int pin, int val) +{ + struct lpi_gpio_state *state = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); + + return lpi_config_set(state->ctrl, pin, &config, 1); +} + +static int lpi_gpio_get(struct gpio_chip *chip, unsigned int pin) +{ + struct lpi_gpio_state *state = gpiochip_get_data(chip); + struct lpi_gpio_pad *pad; + int value; + + pad = state->ctrl->desc->pins[pin].drv_data; + + value = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL); + return value; +} + +static void lpi_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) +{ + struct lpi_gpio_state *state = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); + + lpi_config_set(state->ctrl, pin, &config, 1); +} + +static int lpi_notifier_service_cb(struct notifier_block *this, + unsigned long opcode, void *ptr) +{ + static bool initial_boot = true; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + if (initial_boot) { + initial_boot = false; + break; + } + lpi_dev_up = false; + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (initial_boot) + initial_boot = false; + lpi_dev_up = true; + break; + default: + break; + } + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = lpi_notifier_service_cb, + .priority = -INT_MAX, +}; + +#ifdef CONFIG_DEBUG_FS +#include + +static unsigned int lpi_regval_to_drive(u32 val) +{ + return (val + 1) * 2; +} + +static void lpi_gpio_dbg_show_one(struct seq_file *s, + struct pinctrl_dev *pctldev, + struct gpio_chip *chip, + unsigned int offset, + unsigned int gpio) +{ + struct pinctrl_pin_desc pindesc; + struct lpi_gpio_pad *pad; + unsigned int func; + int is_out; + int drive; + int pull; + u32 ctl_reg; + + static const char * const pulls[] = { + "no pull", + "pull down", + "keeper", + "pull up" + }; + + pctldev = pctldev ? : to_gpio_state(chip)->ctrl; + pindesc = pctldev->desc->pins[offset]; + pad = pctldev->desc->pins[offset].drv_data; + ctl_reg = lpi_gpio_read(pad, LPI_GPIO_REG_DIR_CTL); + is_out = (ctl_reg & LPI_GPIO_REG_DIR_MASK) >> LPI_GPIO_REG_DIR_SHIFT; + ctl_reg = lpi_gpio_read(pad, LPI_GPIO_REG_VAL_CTL); + + func = (ctl_reg & LPI_GPIO_REG_FUNCTION_MASK) >> + LPI_GPIO_REG_FUNCTION_SHIFT; + drive = (ctl_reg & LPI_GPIO_REG_OUT_STRENGTH_MASK) >> + LPI_GPIO_REG_OUT_STRENGTH_SHIFT; + pull = (ctl_reg & LPI_GPIO_REG_PULL_MASK) >> LPI_GPIO_REG_PULL_SHIFT; + + seq_printf(s, " %-8s: %-3s %d", + pindesc.name, is_out ? "out" : "in", func); + seq_printf(s, " %dmA", lpi_regval_to_drive(drive)); + seq_printf(s, " %s", pulls[pull]); +} + +static void lpi_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + unsigned int gpio = chip->base; + unsigned int i; + + for (i = 0; i < chip->ngpio; i++, gpio++) { + lpi_gpio_dbg_show_one(s, NULL, chip, i, gpio); + seq_puts(s, "\n"); + } +} + +#else +#define lpi_gpio_dbg_show NULL +#endif + +static const struct gpio_chip lpi_gpio_template = { + .direction_input = lpi_gpio_direction_input, + .direction_output = lpi_gpio_direction_output, + .get = lpi_gpio_get, + .set = lpi_gpio_set, + .request = gpiochip_generic_request, + .free = gpiochip_generic_free, + .dbg_show = lpi_gpio_dbg_show, +}; + +static int lpi_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pinctrl_pin_desc *pindesc; + struct pinctrl_desc *pctrldesc; + struct lpi_gpio_pad *pad, *pads; + struct lpi_gpio_state *state; + int ret, npins, i; + char __iomem *lpi_base; + u32 reg; + + ret = of_property_read_u32(dev->of_node, "reg", ®); + if (ret < 0) { + dev_err(dev, "missing base address\n"); + return ret; + } + + ret = of_property_read_u32(dev->of_node, "qcom,num-gpios", &npins); + if (ret < 0) + return ret; + + WARN_ON(npins > ARRAY_SIZE(lpi_gpio_groups)); + + state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL); + if (!state) + return -ENOMEM; + + platform_set_drvdata(pdev, state); + + state->dev = &pdev->dev; + + pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); + if (!pindesc) + return -ENOMEM; + + pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); + if (!pads) + return -ENOMEM; + + pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); + if (!pctrldesc) + return -ENOMEM; + + pctrldesc->pctlops = &lpi_gpio_pinctrl_ops; + pctrldesc->pmxops = &lpi_gpio_pinmux_ops; + pctrldesc->confops = &lpi_gpio_pinconf_ops; + pctrldesc->owner = THIS_MODULE; + pctrldesc->name = dev_name(dev); + pctrldesc->pins = pindesc; + pctrldesc->npins = npins; + + lpi_base = devm_ioremap(dev, reg, LPI_ADDRESS_SIZE); + if (lpi_base == NULL) { + dev_err(dev, "%s devm_ioremap failed\n", __func__); + return -ENOMEM; + } + + state->base = lpi_base; + + for (i = 0; i < npins; i++, pindesc++) { + pad = &pads[i]; + pindesc->drv_data = pad; + pindesc->number = i; + pindesc->name = lpi_gpio_groups[i]; + + pad->base = lpi_base; + pad->offset = lpi_offset[i]; + } + + state->chip = lpi_gpio_template; + state->chip.parent = dev; + state->chip.base = -1; + state->chip.ngpio = npins; + state->chip.label = dev_name(dev); + state->chip.of_gpio_n_cells = 2; + state->chip.can_sleep = false; + + state->ctrl = devm_pinctrl_register(dev, pctrldesc, state); + if (IS_ERR(state->ctrl)) + return PTR_ERR(state->ctrl); + + ret = gpiochip_add_data(&state->chip, state); + if (ret) { + dev_err(state->dev, "can't add gpio chip\n"); + goto err_chip; + } + + ret = gpiochip_add_pin_range(&state->chip, dev_name(dev), 0, 0, npins); + if (ret) { + dev_err(dev, "failed to add pin range\n"); + goto err_range; + } + + lpi_dev_up = true; + ret = audio_notifier_register("lpi_tlmm", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + if (ret < 0) { + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + goto err_range; + } + + return 0; + +err_range: + gpiochip_remove(&state->chip); +err_chip: + return ret; +} + +static int lpi_pinctrl_remove(struct platform_device *pdev) +{ + struct lpi_gpio_state *state = platform_get_drvdata(pdev); + + gpiochip_remove(&state->chip); + return 0; +} + +static const struct of_device_id lpi_pinctrl_of_match[] = { + { .compatible = "qcom,lpi-pinctrl" }, /* Generic */ + { }, +}; + +MODULE_DEVICE_TABLE(of, lpi_pinctrl_of_match); + +static struct platform_driver lpi_pinctrl_driver = { + .driver = { + .name = "qcom-lpi-pinctrl", + .of_match_table = lpi_pinctrl_of_match, + }, + .probe = lpi_pinctrl_probe, + .remove = lpi_pinctrl_remove, +}; + +module_platform_driver(lpi_pinctrl_driver); + +MODULE_DESCRIPTION("QTI LPI GPIO pin control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/qcom/pinctrl-wcd.c b/drivers/pinctrl/qcom/pinctrl-wcd.c new file mode 100644 index 000000000000..2cc68a0c6743 --- /dev/null +++ b/drivers/pinctrl/qcom/pinctrl-wcd.c @@ -0,0 +1,435 @@ +/* + * 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 "../core.h" +#include "../pinctrl-utils.h" + +#define WCD_REG_DIR_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE +#define WCD_REG_VAL_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA +#define WCD_GPIO_PULL_UP 1 +#define WCD_GPIO_PULL_DOWN 2 +#define WCD_GPIO_BIAS_DISABLE 3 +#define WCD_GPIO_STRING_LEN 20 + +/** + * struct wcd_gpio_pad - keep current GPIO settings + * @offset: offset of gpio. + * @is_valid: Set to false, when GPIO in high Z state. + * @value: value of a pin + * @output_enabled: Set to true if GPIO is output and false if it is input + * @pullup: Constant current which flow through GPIO output buffer. + * @strength: Drive strength of a pin + */ +struct wcd_gpio_pad { + u16 offset; + bool is_valid; + bool value; + bool output_enabled; + unsigned int pullup; + unsigned int strength; +}; + +struct wcd_gpio_priv { + struct device *dev; + struct regmap *map; + struct pinctrl_dev *ctrl; + struct gpio_chip chip; +}; + +static int wcd_gpio_read(struct wcd_gpio_priv *priv_data, + struct wcd_gpio_pad *pad, unsigned int addr) +{ + unsigned int val; + int ret; + + ret = regmap_read(priv_data->map, addr, &val); + if (ret < 0) + dev_err(priv_data->dev, "%s: read 0x%x failed\n", + __func__, addr); + else + ret = (val >> pad->offset); + + return ret; +} + +static int wcd_gpio_write(struct wcd_gpio_priv *priv_data, + struct wcd_gpio_pad *pad, unsigned int addr, + unsigned int val) +{ + int ret; + + ret = regmap_update_bits(priv_data->map, addr, (1 << pad->offset), + val << pad->offset); + if (ret < 0) + dev_err(priv_data->dev, "write 0x%x failed\n", addr); + + return ret; +} + +static int wcd_get_groups_count(struct pinctrl_dev *pctldev) +{ + return pctldev->desc->npins; +} + +static const char *wcd_get_group_name(struct pinctrl_dev *pctldev, + unsigned int pin) +{ + return pctldev->desc->pins[pin].name; +} + +static int wcd_get_group_pins(struct pinctrl_dev *pctldev, unsigned int pin, + const unsigned int **pins, unsigned int *num_pins) +{ + *pins = &pctldev->desc->pins[pin].number; + *num_pins = 1; + return 0; +} + +static const struct pinctrl_ops wcd_pinctrl_ops = { + .get_groups_count = wcd_get_groups_count, + .get_group_name = wcd_get_group_name, + .get_group_pins = wcd_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_group, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int wcd_config_get(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *config) +{ + unsigned int param = pinconf_to_config_param(*config); + struct wcd_gpio_pad *pad; + unsigned int arg; + + pad = pctldev->desc->pins[pin].drv_data; + + switch (param) { + case PIN_CONFIG_BIAS_PULL_DOWN: + arg = pad->pullup == WCD_GPIO_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_DISABLE: + arg = pad->pullup = WCD_GPIO_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + arg = pad->pullup == WCD_GPIO_PULL_UP; + break; + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + arg = !pad->is_valid; + break; + case PIN_CONFIG_INPUT_ENABLE: + arg = pad->output_enabled; + break; + case PIN_CONFIG_OUTPUT: + arg = pad->value; + break; + default: + return -EINVAL; + } + + *config = pinconf_to_config_packed(param, arg); + return 0; +} + +static int wcd_config_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *configs, unsigned int nconfs) +{ + struct wcd_gpio_priv *priv_data = pinctrl_dev_get_drvdata(pctldev); + struct wcd_gpio_pad *pad; + unsigned int param, arg; + int i, ret; + + pad = pctldev->desc->pins[pin].drv_data; + + for (i = 0; i < nconfs; i++) { + param = pinconf_to_config_param(configs[i]); + arg = pinconf_to_config_argument(configs[i]); + + dev_dbg(priv_data->dev, "%s: param: %d arg: %d", + __func__, param, arg); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + pad->pullup = WCD_GPIO_BIAS_DISABLE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + pad->pullup = WCD_GPIO_PULL_UP; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + pad->pullup = WCD_GPIO_PULL_DOWN; + break; + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + pad->is_valid = false; + break; + case PIN_CONFIG_INPUT_ENABLE: + pad->output_enabled = false; + break; + case PIN_CONFIG_OUTPUT: + pad->output_enabled = true; + pad->value = arg; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + pad->strength = arg; + break; + default: + ret = -EINVAL; + goto done; + } + } + + if (pad->output_enabled) { + ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL, + pad->output_enabled); + if (ret < 0) + goto done; + ret = wcd_gpio_write(priv_data, pad, WCD_REG_VAL_CTL, + pad->value); + } else + ret = wcd_gpio_write(priv_data, pad, WCD_REG_DIR_CTL, + pad->output_enabled); +done: + return ret; +} + +static const struct pinconf_ops wcd_pinconf_ops = { + .is_generic = true, + .pin_config_group_get = wcd_config_get, + .pin_config_group_set = wcd_config_set, +}; + +static int wcd_gpio_direction_input(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_INPUT_ENABLE, 1); + + return wcd_config_set(priv_data->ctrl, pin, &config, 1); +} + +static int wcd_gpio_direction_output(struct gpio_chip *chip, + unsigned int pin, int val) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, val); + + return wcd_config_set(priv_data->ctrl, pin, &config, 1); +} + +static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + struct wcd_gpio_pad *pad; + int value; + + pad = priv_data->ctrl->desc->pins[pin].drv_data; + + if (!pad->is_valid) + return -EINVAL; + + value = wcd_gpio_read(priv_data, pad, WCD_REG_VAL_CTL); + return value; +} + +static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) +{ + struct wcd_gpio_priv *priv_data = gpiochip_get_data(chip); + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT, value); + + wcd_config_set(priv_data->ctrl, pin, &config, 1); +} + +static const struct gpio_chip wcd_gpio_chip = { + .direction_input = wcd_gpio_direction_input, + .direction_output = wcd_gpio_direction_output, + .get = wcd_gpio_get, + .set = wcd_gpio_set, +}; + +static int wcd_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pinctrl_pin_desc *pindesc; + struct pinctrl_desc *pctrldesc; + struct wcd_gpio_pad *pad, *pads; + struct wcd_gpio_priv *priv_data; + int ret, i, j; + u32 npins; + char **name; + + ret = of_property_read_u32(dev->of_node, "qcom,num-gpios", &npins); + if (ret) { + dev_err(dev, "%s: Looking up %s property in node %s failed\n", + __func__, "qcom,num-gpios", dev->of_node->full_name); + ret = -EINVAL; + goto err_priv_alloc; + } + if (!npins) { + dev_err(dev, "%s: no.of pins are 0\n", __func__); + ret = -EINVAL; + goto err_priv_alloc; + } + + priv_data = devm_kzalloc(dev, sizeof(*priv_data), GFP_KERNEL); + if (!priv_data) { + ret = -ENOMEM; + goto err_priv_alloc; + } + + priv_data->dev = dev; + priv_data->map = dev_get_regmap(dev->parent, NULL); + if (!priv_data->map) { + dev_err(dev, "%s: failed to get regmap\n", __func__); + ret = -EINVAL; + goto err_regmap; + } + + pindesc = devm_kcalloc(dev, npins, sizeof(*pindesc), GFP_KERNEL); + if (!pindesc) { + ret = -ENOMEM; + goto err_pinsec_alloc; + } + + pads = devm_kcalloc(dev, npins, sizeof(*pads), GFP_KERNEL); + if (!pads) { + ret = -ENOMEM; + goto err_pads_alloc; + } + + pctrldesc = devm_kzalloc(dev, sizeof(*pctrldesc), GFP_KERNEL); + if (!pctrldesc) { + ret = -ENOMEM; + goto err_pinctrl_alloc; + } + + pctrldesc->pctlops = &wcd_pinctrl_ops; + pctrldesc->confops = &wcd_pinconf_ops; + pctrldesc->owner = THIS_MODULE; + pctrldesc->name = dev_name(dev); + pctrldesc->pins = pindesc; + pctrldesc->npins = npins; + + name = devm_kcalloc(dev, npins, sizeof(char *), GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto err_name_alloc; + } + for (i = 0; i < npins; i++, pindesc++) { + name[i] = devm_kzalloc(dev, sizeof(char) * WCD_GPIO_STRING_LEN, + GFP_KERNEL); + if (!name[i]) { + ret = -ENOMEM; + goto err_pin; + } + pad = &pads[i]; + pindesc->drv_data = pad; + pindesc->number = i; + snprintf(name[i], (WCD_GPIO_STRING_LEN - 1), "gpio%d", (i+1)); + pindesc->name = name[i]; + pad->offset = i; + pad->is_valid = true; + } + + priv_data->chip = wcd_gpio_chip; + priv_data->chip.parent = dev; + priv_data->chip.base = -1; + priv_data->chip.ngpio = npins; + priv_data->chip.label = dev_name(dev); + priv_data->chip.of_gpio_n_cells = 2; + priv_data->chip.can_sleep = false; + + priv_data->ctrl = devm_pinctrl_register(dev, pctrldesc, priv_data); + if (IS_ERR(priv_data->ctrl)) { + dev_err(dev, "%s: failed to register to pinctrl\n", __func__); + ret = PTR_ERR(priv_data->ctrl); + goto err_pin; + } + + ret = gpiochip_add_data(&priv_data->chip, priv_data); + if (ret) { + dev_err(dev, "%s: can't add gpio chip\n", __func__); + goto err_pin; + } + + ret = gpiochip_add_pin_range(&priv_data->chip, dev_name(dev), 0, 0, + npins); + if (ret) { + dev_err(dev, "%s: failed to add pin range\n", __func__); + goto err_range; + } + platform_set_drvdata(pdev, priv_data); + + return 0; + +err_range: + gpiochip_remove(&priv_data->chip); +err_pin: + for (j = 0; j < i; j++) + devm_kfree(dev, name[j]); + devm_kfree(dev, name); +err_name_alloc: + devm_kfree(dev, pctrldesc); +err_pinctrl_alloc: + devm_kfree(dev, pads); +err_pads_alloc: + devm_kfree(dev, pindesc); +err_pinsec_alloc: +err_regmap: + devm_kfree(dev, priv_data); +err_priv_alloc: + return ret; +} + +static int wcd_pinctrl_remove(struct platform_device *pdev) +{ + struct wcd_gpio_priv *priv_data = platform_get_drvdata(pdev); + + gpiochip_remove(&priv_data->chip); + + return 0; +} + +static const struct of_device_id wcd_pinctrl_of_match[] = { + { .compatible = "qcom,wcd-pinctrl" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, wcd_pinctrl_of_match); + +static struct platform_driver wcd_pinctrl_driver = { + .driver = { + .name = "qcom-wcd-pinctrl", + .of_match_table = wcd_pinctrl_of_match, + }, + .probe = wcd_pinctrl_probe, + .remove = wcd_pinctrl_remove, +}; + +module_platform_driver(wcd_pinctrl_driver); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc WCD GPIO pin control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile new file mode 100644 index 000000000000..ae8ffae4cbc3 --- /dev/null +++ b/drivers/soc/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Linux Kernel SOC specific device drivers. +# + +obj-y += qcom/ diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile new file mode 100644 index 000000000000..33e8a12f7890 --- /dev/null +++ b/drivers/soc/qcom/Makefile @@ -0,0 +1 @@ +obj-y += qdsp6v2/ diff --git a/drivers/soc/qcom/qdsp6v2/Makefile b/drivers/soc/qcom/qdsp6v2/Makefile new file mode 100644 index 000000000000..9fdd63a45857 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/Makefile @@ -0,0 +1,8 @@ +obj-$(CONFIG_MSM_QDSP6_APRV2_GLINK) += apr.o apr_v2.o apr_tal_glink.o +obj-$(CONFIG_MSM_QDSP6_APRV3_GLINK) += apr.o apr_v3.o apr_tal_glink.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 +obj-$(CONFIG_MSM_CDSP_LOADER) += cdsp-loader.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..d90267e01505 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/adsp-loader.c @@ -0,0 +1,316 @@ +/* + * 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 + +#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 work_struct adsp_ldr_work; +static struct platform_device *adsp_private; +static void adsp_loader_unload(struct platform_device *pdev); + +static void adsp_load_fw(struct work_struct *adsp_ldr_work) +{ + struct platform_device *pdev = adsp_private; + 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 void adsp_loader_do(struct platform_device *pdev) +{ + schedule_work(&adsp_ldr_work); +} + +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; + } + + INIT_WORK(&adsp_ldr_work, adsp_load_fw); + + 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..e45f61eee58c --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr.c @@ -0,0 +1,976 @@ +/* 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 + +#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->svc_cnt) + clnt->svc_cnt++; + svc->port_cnt++; + svc->port_fn[temp_port] = svc_fn; + svc->port_priv[temp_port] = priv; + svc->svc_cnt++; + } else { + if (!svc->fn) { + if (!svc->svc_cnt) + clnt->svc_cnt++; + svc->fn = svc_fn; + svc->priv = priv; + svc->svc_cnt++; + } + } + + 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); + if (!svc->svc_cnt) { + pr_err("%s: svc already deregistered. svc = %pK\n", + __func__, svc); + mutex_unlock(&svc->m_lock); + return -EINVAL; + } + + dest_id = svc->dest_id; + client_id = svc->client_id; + clnt = &client[dest_id][client_id]; + + if (svc->svc_cnt > 0) { + if (svc->port_cnt) + svc->port_cnt--; + svc->svc_cnt--; + if (!svc->svc_cnt) { + client[dest_id][client_id].svc_cnt--; + pr_debug("%s: service is reset %pK\n", __func__, svc); + } + } + + if (!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) { + is_initial_boot = false; + 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 adsp_service_nb = { + .notifier_call = apr_notifier_service_cb, + .priority = 0, +}; + +static struct notifier_block modem_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, + &adsp_service_nb); + subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN, + &modem_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_glink.c b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c new file mode 100644 index 000000000000..fc7f63f1cae1 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c @@ -0,0 +1,451 @@ +/* + * 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 + +#define APR_MAXIMUM_NUM_OF_RETRIES 2 + +struct apr_tx_buf { + struct apr_pkt_priv pkt_priv; + char buf[APR_MAX_BUF]; +}; + +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 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_alloc_buf(int len) +{ + + if (len > APR_MAX_BUF) { + pr_err("%s: buf too large [%d]\n", __func__, len); + return ERR_PTR(-EINVAL); + } + + return kzalloc(sizeof(struct apr_tx_buf), GFP_ATOMIC); +} + +static void apr_free_buf(const void *ptr) +{ + + struct apr_pkt_priv *apr_pkt_priv = (struct apr_pkt_priv *)ptr; + struct apr_tx_buf *tx_buf; + + if (!apr_pkt_priv) { + pr_err("%s: Invalid apr_pkt_priv\n", __func__); + return; + } + + if (apr_pkt_priv->pkt_owner == APR_PKT_OWNER_DRIVER) { + tx_buf = container_of((void *)apr_pkt_priv, + struct apr_tx_buf, pkt_priv); + pr_debug("%s: Freeing buffer %pK", __func__, tx_buf); + kfree(tx_buf); + } +} + + +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_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 = NULL; + 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_alloc_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) + kfree(tx_buf); + } +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) +{ + pr_debug("%s: tx_abort received for pkt_priv:%pK\n", + __func__, pkt_priv); + apr_free_buf(pkt_priv); +} + +void apr_tal_notify_tx_done(void *handle, const void *priv, + const void *pkt_priv, const void *ptr) +{ + pr_debug("%s: tx_done received for pkt_priv:%pK\n", + __func__, pkt_priv); + apr_free_buf(pkt_priv); +} + +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; + wake_up(&apr_ch->wait); +} + +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; + + 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); + + 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; +} +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..2320fea6e891 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/audio_notifier.c @@ -0,0 +1,636 @@ +/* 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 (ret < 0) { + 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 (ret < 0) { + 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 (ret < 0) + 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 (ret < 0) { + 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 (ret < 0) { + 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 (ret < 0) + 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 (ret < 0) + 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_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 (ret < 0) { + 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 (ret < 0) { + 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 + */ + mutex_lock(¬ifier_mutex); + if (!audio_notifer_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE)) + audio_notifer_reg_all_clients(); + + mutex_unlock(¬ifier_mutex); + 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..449d19a80b3e --- /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 (ret < 0) + 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 (ret < 0) { + 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/cdsp-loader.c b/drivers/soc/qcom/qdsp6v2/cdsp-loader.c new file mode 100644 index 000000000000..70977d36f0c9 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/cdsp-loader.c @@ -0,0 +1,280 @@ +/* + * 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 BOOT_CMD 1 +#define IMAGE_UNLOAD_CMD 0 + +#define CDSP_SUBSYS_DOWN 0 +#define CDSP_SUBSYS_LOADED 1 + +static ssize_t cdsp_boot_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count); + +struct cdsp_loader_private { + void *pil_h; + struct kobject *boot_cdsp_obj; + struct attribute_group *attr_group; +}; + +static struct kobj_attribute cdsp_boot_attribute = + __ATTR(boot, 0220, NULL, cdsp_boot_store); + +static struct attribute *attrs[] = { + &cdsp_boot_attribute.attr, + NULL, +}; + +static u32 cdsp_state = CDSP_SUBSYS_DOWN; +static struct platform_device *cdsp_private; +static struct work_struct cdsp_ldr_work; +static void cdsp_loader_unload(struct platform_device *pdev); + +static void cdsp_load_fw(struct work_struct *cdsp_ldr_work) +{ + struct platform_device *pdev = cdsp_private; + struct cdsp_loader_private *priv = NULL; + + int rc = 0; + 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_string(pdev->dev.of_node, + "qcom,proc-img-to-load", + &img_name); + if (rc) + goto fail; + + if (!strcmp(img_name, "cdsp")) { + /* cdsp_state always returns "0".*/ + if (cdsp_state == CDSP_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("cdsp"); + if (IS_ERR(priv->pil_h)) { + dev_err(&pdev->dev, "%s: pil get failed,\n", + __func__); + goto fail; + } + + /* Set the state of the CDSP.*/ + cdsp_state = CDSP_SUBSYS_LOADED; + } else if (cdsp_state == CDSP_SUBSYS_LOADED) { + dev_dbg(&pdev->dev, + "%s: CDSP state = %x\n", __func__, cdsp_state); + } + + dev_dbg(&pdev->dev, "%s: CDSP image is loaded\n", __func__); + return; + } + +fail: + dev_err(&pdev->dev, "%s: CDSP image loading failed\n", __func__); +} + +static void cdsp_loader_do(struct platform_device *pdev) +{ + schedule_work(&cdsp_ldr_work); +} + +static ssize_t cdsp_boot_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, + size_t count) +{ + int boot = 0, ret = 0; + + ret = sscanf(buf, "%du", &boot); + + if (ret != 1) + pr_debug("%s: invalid arguments for cdsp_loader.\n", __func__); + + if (boot == BOOT_CMD) { + pr_debug("%s: going to call cdsp_loader_do\n", __func__); + cdsp_loader_do(cdsp_private); + } else if (boot == IMAGE_UNLOAD_CMD) { + pr_debug("%s: going to call cdsp_unloader\n", __func__); + cdsp_loader_unload(cdsp_private); + } + return count; +} + +static void cdsp_loader_unload(struct platform_device *pdev) +{ + struct cdsp_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 cdsp_loader_init_sysfs(struct platform_device *pdev) +{ + int ret = -EINVAL; + struct cdsp_loader_private *priv = NULL; + + cdsp_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_cdsp_obj = NULL; + priv->attr_group = devm_kzalloc(&pdev->dev, + sizeof(*(priv->attr_group)), + GFP_KERNEL); + if (!priv->attr_group) { + dev_err(&pdev->dev, "%s: malloc attr_group failed\n", + __func__); + ret = -ENOMEM; + goto error_return; + } + + priv->attr_group->attrs = attrs; + + priv->boot_cdsp_obj = kobject_create_and_add("boot_cdsp", kernel_kobj); + if (!priv->boot_cdsp_obj) { + dev_err(&pdev->dev, "%s: sysfs create and add failed\n", + __func__); + ret = -ENOMEM; + goto error_return; + } + + ret = sysfs_create_group(priv->boot_cdsp_obj, priv->attr_group); + if (ret) { + dev_err(&pdev->dev, "%s: sysfs create group failed %d\n", + __func__, ret); + goto error_return; + } + + cdsp_private = pdev; + + return 0; + +error_return: + + if (priv->boot_cdsp_obj) { + kobject_del(priv->boot_cdsp_obj); + priv->boot_cdsp_obj = NULL; + } + + return ret; +} + +static int cdsp_loader_remove(struct platform_device *pdev) +{ + struct cdsp_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_cdsp_obj) { + sysfs_remove_group(priv->boot_cdsp_obj, priv->attr_group); + kobject_del(priv->boot_cdsp_obj); + priv->boot_cdsp_obj = NULL; + } + + return 0; +} + +static int cdsp_loader_probe(struct platform_device *pdev) +{ + int ret = cdsp_loader_init_sysfs(pdev); + + if (ret != 0) { + dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__); + return ret; + } + + INIT_WORK(&cdsp_ldr_work, cdsp_load_fw); + + return 0; +} + +static const struct of_device_id cdsp_loader_dt_match[] = { + { .compatible = "qcom,cdsp-loader" }, + { } +}; +MODULE_DEVICE_TABLE(of, cdsp_loader_dt_match); + +static struct platform_driver cdsp_loader_driver = { + .driver = { + .name = "cdsp-loader", + .owner = THIS_MODULE, + .of_match_table = cdsp_loader_dt_match, + }, + .probe = cdsp_loader_probe, + .remove = cdsp_loader_remove, +}; + +static int __init cdsp_loader_init(void) +{ + return platform_driver_register(&cdsp_loader_driver); +} +module_init(cdsp_loader_init); + +static void __exit cdsp_loader_exit(void) +{ + platform_driver_unregister(&cdsp_loader_driver); +} +module_exit(cdsp_loader_exit); + +MODULE_DESCRIPTION("CDSP Loader module"); +MODULE_LICENSE("GPL v2"); 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..2f14c744cd89 --- /dev/null +++ b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c @@ -0,0 +1,853 @@ +/* + * 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 + +#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 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_fd(*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=%lu\n", + vma, (unsigned int)addr, len, + (unsigned int)vma->vm_start, + (unsigned int)vma->vm_end, + (unsigned long)vma->vm_page_prot.pgprot); + 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=%lu\n", + vma, (unsigned int)vma->vm_start, + (unsigned int)vma->vm_end, vma->vm_pgoff, + (unsigned long)vma->vm_page_prot.pgprot); + 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_fd(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(struct device *dev) +{ + struct dma_iommu_mapping *mapping; + int ret; + + mapping = arm_iommu_create_mapping(&platform_bus_type, + 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"; + const char *msm_audio_ion_smmu_sid_mask = "qcom,smmu-sid-mask"; + 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; + u64 smmu_sid_mask = 0; + struct of_phandle_args iommuspec; + + /* Get SMMU SID information from Devicetree */ + rc = of_property_read_u64(dev->of_node, + msm_audio_ion_smmu_sid_mask, + &smmu_sid_mask); + if (rc) { + dev_err(dev, + "%s: qcom,smmu-sid-mask missing in DT node, using default\n", + __func__); + smmu_sid_mask = 0xFFFFFFFFFFFFFFFF; + } + 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] & smmu_sid_mask); + + msm_audio_ion_data.smmu_sid_bits = + smmu_sid << MSM_AUDIO_SMMU_SID_OFFSET; + + 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/soundwire/Kconfig b/drivers/soundwire/Kconfig new file mode 100644 index 000000000000..e1ca532c22f5 --- /dev/null +++ b/drivers/soundwire/Kconfig @@ -0,0 +1,17 @@ +# +# SOUNDWIRE driver configuration +# +menuconfig SOUNDWIRE + bool "Soundwire support" + help + Soundwire is a two wire interface for audio to connect + simple peripheral components in mobile devices. + +if SOUNDWIRE +config SOUNDWIRE_WCD_CTRL + depends on WCD9XXX_CODEC_CORE + tristate "QTI WCD CODEC Soundwire controller" + default n + help + Select driver for QTI's Soundwire Master Component. +endif diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile new file mode 100644 index 000000000000..53acff15c4c4 --- /dev/null +++ b/drivers/soundwire/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for kernel soundwire framework. +# +obj-$(CONFIG_SOUNDWIRE) += soundwire.o +obj-$(CONFIG_SOUNDWIRE_WCD_CTRL) += swr-wcd-ctrl.o diff --git a/drivers/soundwire/soundwire.c b/drivers/soundwire/soundwire.c new file mode 100644 index 000000000000..f0c7aa9ac01f --- /dev/null +++ b/drivers/soundwire/soundwire.c @@ -0,0 +1,1032 @@ +/* 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 +#include + +struct boardinfo { + struct list_head list; + struct swr_boardinfo board_info; +}; + +static LIST_HEAD(board_list); +static LIST_HEAD(swr_master_list); +static DEFINE_MUTEX(board_lock); +static DEFINE_IDR(master_idr); +static DEFINE_MUTEX(swr_lock); + +static struct device_type swr_dev_type; + +#define SOUNDWIRE_NAME_SIZE 32 + +static void swr_master_put(struct swr_master *master) +{ + if (master) + put_device(&master->dev); +} + +static struct swr_master *swr_master_get(struct swr_master *master) +{ + if (!master || !get_device(&master->dev)) + return NULL; + return master; +} + +static void swr_dev_release(struct device *dev) +{ + struct swr_device *swr_dev = to_swr_device(dev); + struct swr_master *master; + + if (!swr_dev) + return; + master = swr_dev->master; + if (!master) + return; + mutex_lock(&master->mlock); + list_del_init(&swr_dev->dev_list); + mutex_unlock(&master->mlock); + swr_master_put(swr_dev->master); + kfree(swr_dev); +} + +/** + * swr_remove_device - remove a soundwire device + * @swr_dev: soundwire device to remove + * + * Remove a soundwire device. Go through the soundwire + * device list that master has and remove swr_dev from + * it. + */ +void swr_remove_device(struct swr_device *swr_dev) +{ + struct swr_device *swr_dev_loop, *safe; + + list_for_each_entry_safe(swr_dev_loop, safe, + &swr_dev->master->devices, + dev_list) { + if (swr_dev == swr_dev_loop) + list_del(&swr_dev_loop->dev_list); + } +} +EXPORT_SYMBOL(swr_remove_device); + +/** + * swr_new_device - instantiate a new soundwire device + * @master: Controller to which device is connected + * @info: Describes the soundwire device + * Context: can sleep + * + * Create a soundwire device. Binding is handled through driver model + * probe/remove methods. A driver may be bound to this device when + * the function gets returned. + * + * Returns a soundwire new device or NULL + */ +struct swr_device *swr_new_device(struct swr_master *master, + struct swr_boardinfo const *info) +{ + int result; + struct swr_device *swr; + + if (!master || !swr_master_get(master)) { + pr_err("%s: master is NULL\n", __func__); + return NULL; + } + + swr = kzalloc(sizeof(*swr), GFP_KERNEL); + if (!swr) { + put_device(&master->dev); + return NULL; + } + swr->master = master; + swr->addr = info->addr; + strlcpy(swr->name, info->name, sizeof(swr->name)); + swr->dev.type = &swr_dev_type; + swr->dev.parent = &master->dev; + swr->dev.bus = &soundwire_type; + swr->dev.release = swr_dev_release; + swr->dev.of_node = info->of_node; + mutex_lock(&master->mlock); + list_add_tail(&swr->dev_list, &master->devices); + mutex_unlock(&master->mlock); + + dev_set_name(&swr->dev, "%s.%lx", swr->name, swr->addr); + result = device_register(&swr->dev); + if (result) { + dev_err(&master->dev, "device [%s] register failed err %d\n", + swr->name, result); + goto err_out; + } + dev_dbg(&master->dev, "Device [%s] registered with bus id %s\n", + swr->name, dev_name(&swr->dev)); + return swr; + +err_out: + dev_dbg(&master->dev, "Failed to register swr device %s at 0x%lx %d\n", + swr->name, swr->addr, result); + swr_master_put(master); + kfree(swr); + return NULL; +} +EXPORT_SYMBOL(swr_new_device); + +/** + * of_register_swr_devices - register child devices on to the soundwire bus + * @master: pointer to soundwire master device + * + * Registers a soundwire device for each child node of master node which has + * a "swr-devid" property + * + */ +int of_register_swr_devices(struct swr_master *master) +{ + struct swr_device *swr; + struct device_node *node; + + if (!master->dev.of_node) + return -EINVAL; + + for_each_available_child_of_node(master->dev.of_node, node) { + struct swr_boardinfo info = {}; + u64 addr; + + dev_dbg(&master->dev, "of_swr:register %s\n", node->full_name); + + if (of_modalias_node(node, info.name, sizeof(info.name)) < 0) { + dev_err(&master->dev, "of_swr:modalias failure %s\n", + node->full_name); + continue; + } + if (of_property_read_u64(node, "reg", &addr)) { + dev_err(&master->dev, "of_swr:invalid reg %s\n", + node->full_name); + continue; + } + info.addr = addr; + info.of_node = of_node_get(node); + master->num_dev++; + swr = swr_new_device(master, &info); + if (!swr) { + dev_err(&master->dev, "of_swr: Register failed %s\n", + node->full_name); + of_node_put(node); + master->num_dev--; + continue; + } + } + return 0; +} +EXPORT_SYMBOL(of_register_swr_devices); + +/** + * swr_port_response - response from master to free the completed transaction + * @mstr: pointer to soundwire master device + * @tid: transaction id that indicates transaction to be freed. + * + * Master calls this function to free the compeleted transaction memory + */ +void swr_port_response(struct swr_master *mstr, u8 tid) +{ + struct swr_params *txn; + + txn = mstr->port_txn[tid]; + + if (txn == NULL) { + dev_err(&mstr->dev, "%s: transaction is already NULL\n", + __func__); + return; + } + mstr->port_txn[tid] = NULL; + kfree(txn); +} +EXPORT_SYMBOL(swr_port_response); + +/** + * swr_remove_from_group - remove soundwire slave devices from group + * @dev: pointer to the soundwire slave device + * dev_num: device number of the soundwire slave device + * + * Returns error code for failure and 0 for success + */ +int swr_remove_from_group(struct swr_device *dev, u8 dev_num) +{ + struct swr_master *master; + + if (!dev) + return -ENODEV; + + master = dev->master; + if (!master) + return -EINVAL; + + if (!dev->group_id) + return 0; + + if (master->gr_sid == dev_num) + return 0; + + if (master->remove_from_group && master->remove_from_group(master)) + dev_dbg(&master->dev, "%s: falling back to GROUP_NONE\n", + __func__); + + return 0; +} +EXPORT_SYMBOL(swr_remove_from_group); + +/** + * swr_slvdev_datapath_control - Enables/Disables soundwire slave device + * data path + * @dev: pointer to soundwire slave device + * @dev_num: device number of the soundwire slave device + * + * Returns error code for failure and 0 for success + */ +int swr_slvdev_datapath_control(struct swr_device *dev, u8 dev_num, + bool enable) +{ + struct swr_master *master; + + if (!dev) + return -ENODEV; + + master = dev->master; + if (!master) + return -EINVAL; + + if (dev->group_id) { + /* Broadcast */ + if (master->gr_sid != dev_num) { + if (!master->gr_sid) + master->gr_sid = dev_num; + else + return 0; + } + } + + if (master->slvdev_datapath_control) + master->slvdev_datapath_control(master, enable); + + return 0; +} +EXPORT_SYMBOL(swr_slvdev_datapath_control); + +/** + * swr_connect_port - enable soundwire slave port(s) + * @dev: pointer to soundwire slave device + * @port_id: logical port id(s) of soundwire slave device + * @num_port: number of slave device ports need to be enabled + * @ch_mask: channels for each port that needs to be enabled + * @ch_rate: rate at which each port/channels operate + * @num_ch: number of channels for each port + * + * soundwire slave device call swr_connect_port API to enable all/some of + * its ports and corresponding channels and channel rate. This API will + * call master connect_port callback function to calculate frame structure + * and enable master and slave ports + */ +int swr_connect_port(struct swr_device *dev, u8 *port_id, u8 num_port, + u8 *ch_mask, u32 *ch_rate, u8 *num_ch) +{ + u8 i = 0; + int ret = 0; + struct swr_params *txn = NULL; + struct swr_params **temp_txn = NULL; + struct swr_master *master = dev->master; + + if (!master) { + pr_err("%s: Master is NULL\n", __func__); + return -EINVAL; + } + if (num_port > SWR_MAX_DEV_PORT_NUM) { + dev_err(&master->dev, "%s: num_port %d exceeds max port %d\n", + __func__, num_port, SWR_MAX_DEV_PORT_NUM); + return -EINVAL; + } + + /* + * create "txn" to accommodate ports enablement of + * different slave devices calling swr_connect_port at the + * same time. Once master process the txn data, it calls + * swr_port_response() to free the transaction. Maximum + * of 256 transactions can be allocated. + */ + txn = kzalloc(sizeof(struct swr_params), GFP_KERNEL); + if (!txn) + return -ENOMEM; + + mutex_lock(&master->mlock); + for (i = 0; i < master->last_tid; i++) { + if (master->port_txn[i] == NULL) + break; + } + if (i >= master->last_tid) { + if (master->last_tid == 255) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Max tid reached\n", + __func__); + return -ENOMEM; + } + temp_txn = krealloc(master->port_txn, + (i + 1) * sizeof(struct swr_params *), + GFP_KERNEL); + if (!temp_txn) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Not able to allocate\n" + "master port transaction memory\n", + __func__); + return -ENOMEM; + } + master->port_txn = temp_txn; + master->last_tid++; + } + master->port_txn[i] = txn; + mutex_unlock(&master->mlock); + txn->tid = i; + + txn->dev_id = dev->dev_num; + txn->num_port = num_port; + for (i = 0; i < num_port; i++) { + txn->port_id[i] = port_id[i]; + txn->num_ch[i] = num_ch[i]; + txn->ch_rate[i] = ch_rate[i]; + txn->ch_en[i] = ch_mask[i]; + } + ret = master->connect_port(master, txn); + return ret; +} +EXPORT_SYMBOL(swr_connect_port); + +/** + * swr_disconnect_port - disable soundwire slave port(s) + * @dev: pointer to soundwire slave device + * @port_id: logical port id(s) of soundwire slave device + * @num_port: number of slave device ports need to be disabled + * + * soundwire slave device call swr_disconnect_port API to disable all/some of + * its ports. This API will call master disconnect_port callback function to + * disable master and slave port and (re)configure frame structure + */ +int swr_disconnect_port(struct swr_device *dev, u8 *port_id, u8 num_port) +{ + u8 i = 0; + int ret; + struct swr_params *txn = NULL; + struct swr_params **temp_txn = NULL; + struct swr_master *master = dev->master; + + if (!master) { + pr_err("%s: Master is NULL\n", __func__); + return -EINVAL; + } + + if (num_port > SWR_MAX_DEV_PORT_NUM) { + dev_err(&master->dev, "%s: num_port %d exceeds max port %d\n", + __func__, num_port, SWR_MAX_DEV_PORT_NUM); + return -EINVAL; + } + + txn = kzalloc(sizeof(struct swr_params), GFP_KERNEL); + if (!txn) + return -ENOMEM; + + mutex_lock(&master->mlock); + for (i = 0; i < master->last_tid; i++) { + if (master->port_txn[i] == NULL) + break; + } + if (i >= master->last_tid) { + if (master->last_tid == 255) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Max tid reached\n", + __func__); + return -ENOMEM; + } + temp_txn = krealloc(master->port_txn, + (i + 1) * sizeof(struct swr_params *), + GFP_KERNEL); + if (!temp_txn) { + mutex_unlock(&master->mlock); + kfree(txn); + dev_err(&master->dev, "%s Not able to allocate\n" + "master port transaction memory\n", + __func__); + return -ENOMEM; + } + master->port_txn = temp_txn; + master->last_tid++; + } + master->port_txn[i] = txn; + mutex_unlock(&master->mlock); + txn->tid = i; + + txn->dev_id = dev->dev_num; + txn->num_port = num_port; + for (i = 0; i < num_port; i++) + txn->port_id[i] = port_id[i]; + ret = master->disconnect_port(master, txn); + return ret; +} +EXPORT_SYMBOL(swr_disconnect_port); + +/** + * swr_get_logical_dev_num - Get soundwire slave logical device number + * @dev: pointer to soundwire slave device + * @dev_id: physical device id of soundwire slave device + * @dev_num: pointer to logical device num of soundwire slave device + * + * This API will get the logical device number of soundwire slave device + */ +int swr_get_logical_dev_num(struct swr_device *dev, u64 dev_id, + u8 *dev_num) +{ + int ret = 0; + struct swr_master *master = dev->master; + + if (!master) { + pr_err("%s: Master is NULL\n", __func__); + return -EINVAL; + } + mutex_lock(&master->mlock); + ret = master->get_logical_dev_num(master, dev_id, dev_num); + if (ret) { + pr_err("%s: Error %d to get logical addr for device %llx\n", + __func__, ret, dev_id); + } + mutex_unlock(&master->mlock); + return ret; +} +EXPORT_SYMBOL(swr_get_logical_dev_num); + +/** + * swr_read - read soundwire slave device registers + * @dev: pointer to soundwire slave device + * @dev_num: logical device num of soundwire slave device + * @reg_addr: base register address that needs to be read + * @buf: pointer to store the values of registers from base address + * @len: length of the buffer + * + * This API will read the value of the register address from + * soundwire slave device + */ +int swr_read(struct swr_device *dev, u8 dev_num, u16 reg_addr, + void *buf, u32 len) +{ + struct swr_master *master = dev->master; + + if (!master) + return -EINVAL; + return master->read(master, dev_num, reg_addr, buf, len); +} +EXPORT_SYMBOL(swr_read); + +/** + * swr_bulk_write - write soundwire slave device registers + * @dev: pointer to soundwire slave device + * @dev_num: logical device num of soundwire slave device + * @reg_addr: register address of soundwire slave device + * @buf: contains value of register address + * @len: indicates number of registers + * + * This API will write the value of the register address to + * soundwire slave device + */ +int swr_bulk_write(struct swr_device *dev, u8 dev_num, void *reg, + const void *buf, size_t len) +{ + struct swr_master *master; + + if (!dev || !dev->master) + return -EINVAL; + + master = dev->master; + if (dev->group_id) { + if (master->gr_sid != dev_num) { + if (!master->gr_sid) + master->gr_sid = dev_num; + else + return 0; + } + dev_num = dev->group_id; + } + if (master->bulk_write) + return master->bulk_write(master, dev_num, reg, buf, len); + + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(swr_bulk_write); + +/** + * swr_write - write soundwire slave device registers + * @dev: pointer to soundwire slave device + * @dev_num: logical device num of soundwire slave device + * @reg_addr: register address of soundwire slave device + * @buf: contains value of register address + * + * This API will write the value of the register address to + * soundwire slave device + */ +int swr_write(struct swr_device *dev, u8 dev_num, u16 reg_addr, + const void *buf) +{ + struct swr_master *master = dev->master; + + if (!master) + return -EINVAL; + + if (dev->group_id) { + if (master->gr_sid != dev_num) { + if (!master->gr_sid) + master->gr_sid = dev_num; + else + return 0; + } + dev_num = dev->group_id; + } + return master->write(master, dev_num, reg_addr, buf); +} +EXPORT_SYMBOL(swr_write); + +/** + * swr_device_up - Function to bringup the soundwire slave device + * @swr_dev: pointer to soundwire slave device + * Context: can sleep + * + * This API will be called by soundwire master to bringup the slave + * device. + */ +int swr_device_up(struct swr_device *swr_dev) +{ + struct device *dev; + const struct swr_driver *sdrv; + + if (!swr_dev) + return -EINVAL; + + dev = &swr_dev->dev; + sdrv = to_swr_driver(dev->driver); + if (!sdrv) + return 0; + + if (sdrv->device_up) + return sdrv->device_up(to_swr_device(dev)); + + return -ENODEV; +} +EXPORT_SYMBOL(swr_device_up); + +/** + * swr_device_down - Function to call soundwire slave device down + * @swr_dev: pointer to soundwire slave device + * Context: can sleep + * + * This API will be called by soundwire master to put slave device in + * shutdown state. + */ +int swr_device_down(struct swr_device *swr_dev) +{ + struct device *dev; + const struct swr_driver *sdrv; + + if (!swr_dev) + return -EINVAL; + + dev = &swr_dev->dev; + sdrv = to_swr_driver(dev->driver); + if (!sdrv) + return 0; + + if (sdrv->device_down) + return sdrv->device_down(to_swr_device(dev)); + + return -ENODEV; +} +EXPORT_SYMBOL(swr_device_down); + +/** + * swr_reset_device - reset soundwire slave device + * @swr_dev: pointer to soundwire slave device + * Context: can sleep + * + * This API will be called by soundwire master to reset the slave + * device when the slave device is not responding or in undefined + * state + */ +int swr_reset_device(struct swr_device *swr_dev) +{ + struct device *dev; + const struct swr_driver *sdrv; + + if (!swr_dev) + return -EINVAL; + + dev = &swr_dev->dev; + sdrv = to_swr_driver(dev->driver); + if (!sdrv) + return -EINVAL; + + if (sdrv->reset_device) + return sdrv->reset_device(to_swr_device(dev)); + + return -ENODEV; +} +EXPORT_SYMBOL(swr_reset_device); + +/** + * swr_set_device_group - Assign group id to the slave devices + * @swr_dev: pointer to soundwire slave device + * @id: group id to be assigned to slave device + * Context: can sleep + * + * This API will be called either from soundwire master or slave + * device to assign group id. + */ +int swr_set_device_group(struct swr_device *swr_dev, u8 id) +{ + struct swr_master *master; + + if (!swr_dev) + return -EINVAL; + + swr_dev->group_id = id; + master = swr_dev->master; + if (!id && master) + master->gr_sid = 0; + + return 0; +} +EXPORT_SYMBOL(swr_set_device_group); + +static int swr_drv_probe(struct device *dev) +{ + const struct swr_driver *sdrv = to_swr_driver(dev->driver); + + if (!sdrv) + return -EINVAL; + + if (sdrv->probe) + return sdrv->probe(to_swr_device(dev)); + return -ENODEV; +} + +static int swr_drv_remove(struct device *dev) +{ + const struct swr_driver *sdrv = to_swr_driver(dev->driver); + + if (!sdrv) + return -EINVAL; + + if (sdrv->remove) + return sdrv->remove(to_swr_device(dev)); + return -ENODEV; +} + +static void swr_drv_shutdown(struct device *dev) +{ + const struct swr_driver *sdrv = to_swr_driver(dev->driver); + + if (!sdrv) + return; + + if (sdrv->shutdown) + sdrv->shutdown(to_swr_device(dev)); +} + +/** + * swr_driver_register - register a soundwire driver + * @drv: the driver to register + * Context: can sleep + */ +int swr_driver_register(struct swr_driver *drv) +{ + drv->driver.bus = &soundwire_type; + if (drv->probe) + drv->driver.probe = swr_drv_probe; + if (drv->remove) + drv->driver.remove = swr_drv_remove; + + if (drv->shutdown) + drv->driver.shutdown = swr_drv_shutdown; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL(swr_driver_register); + +/** + * swr_driver_unregister - unregister a soundwire driver + * @drv: the driver to unregister + */ +void swr_driver_unregister(struct swr_driver *drv) +{ + if (drv) + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL(swr_driver_unregister); + +static void swr_match_ctrl_to_boardinfo(struct swr_master *master, + struct swr_boardinfo *bi) +{ + struct swr_device *swr; + + if (master->bus_num != bi->bus_num) { + dev_dbg(&master->dev, + "%s: master# %d and bi# %d does not match\n", + __func__, master->bus_num, bi->bus_num); + return; + } + + swr = swr_new_device(master, bi); + if (!swr) + dev_err(&master->dev, "can't create new device for %s\n", + bi->swr_slave->name); +} + +/** + * swr_master_add_boarddevices - Add devices registered by board info + * @master: master to which these devices are to be added to. + * + * This API is called by master when it is up and running. If devices + * on a master were registered before master, this will make sure that + * they get probed when master is up. + */ +void swr_master_add_boarddevices(struct swr_master *master) +{ + struct boardinfo *bi; + + mutex_lock(&board_lock); + list_add_tail(&master->list, &swr_master_list); + list_for_each_entry(bi, &board_list, list) + swr_match_ctrl_to_boardinfo(master, &bi->board_info); + mutex_unlock(&board_lock); +} +EXPORT_SYMBOL(swr_master_add_boarddevices); + +static void swr_unregister_device(struct swr_device *swr) +{ + if (swr) + device_unregister(&swr->dev); +} + +static void swr_master_release(struct device *dev) +{ + struct swr_master *master = to_swr_master(dev); + + kfree(master); +} + +#define swr_master_attr_gr NULL +static struct device_type swr_master_type = { + .groups = swr_master_attr_gr, + .release = swr_master_release, +}; + +static int __unregister(struct device *dev, void *null) +{ + swr_unregister_device(to_swr_device(dev)); + return 0; +} + +/** + * swr_unregister_master - unregister soundwire master controller + * @master: the master being unregistered + * + * This API is called by master controller driver to unregister + * master controller that was registered by swr_register_master API. + */ +void swr_unregister_master(struct swr_master *master) +{ + int dummy; + struct swr_master *m_ctrl; + + mutex_lock(&swr_lock); + m_ctrl = idr_find(&master_idr, master->bus_num); + mutex_unlock(&swr_lock); + if (m_ctrl != master) + return; + + mutex_lock(&board_lock); + list_del(&master->list); + mutex_unlock(&board_lock); + + /* free bus id */ + mutex_lock(&swr_lock); + idr_remove(&master_idr, master->bus_num); + mutex_unlock(&swr_lock); + + dummy = device_for_each_child(&master->dev, NULL, __unregister); + device_unregister(&master->dev); +} +EXPORT_SYMBOL(swr_unregister_master); + +/** + * swr_register_master - register soundwire master controller + * @master: master to be registered + * + * This API will register master with the framework. master->bus_num + * is the desired number with which soundwire framework registers the + * master. + */ +int swr_register_master(struct swr_master *master) +{ + int id; + int status = 0; + + mutex_lock(&swr_lock); + id = idr_alloc(&master_idr, master, master->bus_num, + master->bus_num+1, GFP_KERNEL); + mutex_unlock(&swr_lock); + if (id < 0) + return id; + master->bus_num = id; + + /* Can't register until driver model init */ + if (WARN_ON(!soundwire_type.p)) { + status = -EAGAIN; + goto done; + } + + dev_set_name(&master->dev, "swr%u", master->bus_num); + master->dev.bus = &soundwire_type; + master->dev.type = &swr_master_type; + mutex_init(&master->mlock); + status = device_register(&master->dev); + if (status < 0) + goto done; + + INIT_LIST_HEAD(&master->devices); + pr_debug("%s: SWR master registered successfully %s\n", + __func__, dev_name(&master->dev)); + return 0; + +done: + idr_remove(&master_idr, master->bus_num); + return status; +} +EXPORT_SYMBOL(swr_register_master); + +#define swr_device_attr_gr NULL +#define swr_device_uevent NULL +static struct device_type swr_dev_type = { + .groups = swr_device_attr_gr, + .uevent = swr_device_uevent, + .release = swr_dev_release, +}; + +static const struct swr_device_id *swr_match(const struct swr_device_id *id, + const struct swr_device *swr_dev) +{ + while (id->name[0]) { + if (strcmp(swr_dev->name, id->name) == 0) + return id; + id++; + } + return NULL; +} + +static int swr_device_match(struct device *dev, struct device_driver *driver) +{ + struct swr_device *swr_dev; + struct swr_driver *drv = to_swr_driver(driver); + + if (!drv) + return -EINVAL; + + if (dev->type == &swr_dev_type) + swr_dev = to_swr_device(dev); + else + return 0; + if (drv->id_table) + return swr_match(drv->id_table, swr_dev) != NULL; + + if (driver->name) + return strcmp(swr_dev->name, driver->name) == 0; + return 0; +} +#ifdef CONFIG_PM_SLEEP +static int swr_legacy_suspend(struct device *dev, pm_message_t mesg) +{ + struct swr_device *swr_dev = NULL; + struct swr_driver *driver; + + if (dev->type == &swr_dev_type) + swr_dev = to_swr_device(dev); + + if (!swr_dev || !dev->driver) + return 0; + + driver = to_swr_driver(dev->driver); + if (!driver->suspend) + return 0; + + return driver->suspend(swr_dev, mesg); +} + +static int swr_legacy_resume(struct device *dev) +{ + struct swr_device *swr_dev = NULL; + struct swr_driver *driver; + + if (dev->type == &swr_dev_type) + swr_dev = to_swr_device(dev); + + if (!swr_dev || !dev->driver) + return 0; + + driver = to_swr_driver(dev->driver); + if (!driver->resume) + return 0; + + return driver->resume(swr_dev); +} + +static int swr_pm_suspend(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_suspend(dev); + else + return swr_legacy_suspend(dev, PMSG_SUSPEND); +} + +static int swr_pm_resume(struct device *dev) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + + if (pm) + return pm_generic_resume(dev); + else + return swr_legacy_resume(dev); +} +#else +#define swr_pm_suspend NULL +#define swr_pm_resume NULL +#endif /*CONFIG_PM_SLEEP*/ + +static const struct dev_pm_ops soundwire_pm = { + .suspend = swr_pm_suspend, + .resume = swr_pm_resume, + SET_RUNTIME_PM_OPS( + pm_generic_suspend, + pm_generic_resume, + NULL + ) +}; + +struct device soundwire_dev = { + .init_name = "soundwire", +}; + +struct bus_type soundwire_type = { + .name = "soundwire", + .match = swr_device_match, + .pm = &soundwire_pm, +}; +EXPORT_SYMBOL(soundwire_type); + +static void __exit soundwire_exit(void) +{ + device_unregister(&soundwire_dev); + bus_unregister(&soundwire_type); +} + +static int __init soundwire_init(void) +{ + int retval; + + retval = bus_register(&soundwire_type); + if (!retval) + retval = device_register(&soundwire_dev); + + if (retval) + bus_unregister(&soundwire_type); + + return retval; +} +postcore_initcall(soundwire_init); +module_exit(soundwire_exit); + + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Soundwire module"); +MODULE_ALIAS("platform:soundwire"); diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c new file mode 100644 index 000000000000..e338d582fa95 --- /dev/null +++ b/drivers/soundwire/swr-wcd-ctrl.c @@ -0,0 +1,1880 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include "swrm_registers.h" +#include "swr-wcd-ctrl.h" + +#define SWR_BROADCAST_CMD_ID 0x0F +#define SWR_AUTO_SUSPEND_DELAY 3 /* delay in sec */ +#define SWR_DEV_ID_MASK 0xFFFFFFFF +#define SWR_REG_VAL_PACK(data, dev, id, reg) \ + ((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24)) + +/* pm runtime auto suspend timer in msecs */ +static int auto_suspend_timer = SWR_AUTO_SUSPEND_DELAY * 1000; +module_param(auto_suspend_timer, int, 0664); +MODULE_PARM_DESC(auto_suspend_timer, "timer for auto suspend"); + +static u8 mstr_ports[] = {100, 101, 102, 103, 104, 105, 106, 107}; +static u8 mstr_port_type[] = {SWR_DAC_PORT, SWR_COMP_PORT, SWR_BOOST_PORT, + SWR_DAC_PORT, SWR_COMP_PORT, SWR_BOOST_PORT, + SWR_VISENSE_PORT, SWR_VISENSE_PORT}; + +struct usecase uc[] = { + {0, 0, 0}, /* UC0: no ports */ + {1, 1, 2400}, /* UC1: Spkr */ + {1, 4, 600}, /* UC2: Compander */ + {1, 2, 300}, /* UC3: Smart Boost */ + {1, 2, 1200}, /* UC4: VI Sense */ + {4, 9, 4500}, /* UC5: Spkr + Comp + SB + VI */ + {8, 18, 9000}, /* UC6: 2*(Spkr + Comp + SB + VI) */ + {2, 2, 4800}, /* UC7: 2*Spkr */ + {2, 5, 3000}, /* UC8: Spkr + Comp */ + {4, 10, 6000}, /* UC9: 2*(Spkr + Comp) */ + {3, 7, 3300}, /* UC10: Spkr + Comp + SB */ + {6, 14, 6600}, /* UC11: 2*(Spkr + Comp + SB) */ + {2, 3, 2700}, /* UC12: Spkr + SB */ + {4, 6, 5400}, /* UC13: 2*(Spkr + SB) */ + {3, 5, 3900}, /* UC14: Spkr + SB + VI */ + {6, 10, 7800}, /* UC15: 2*(Spkr + SB + VI) */ + {2, 3, 3600}, /* UC16: Spkr + VI */ + {4, 6, 7200}, /* UC17: 2*(Spkr + VI) */ + {3, 7, 4200}, /* UC18: Spkr + Comp + VI */ + {6, 14, 8400}, /* UC19: 2*(Spkr + Comp + VI) */ +}; +#define MAX_USECASE ARRAY_SIZE(uc) + +struct port_params pp[MAX_USECASE][SWR_MSTR_PORT_LEN] = { + /* UC 0 */ + { + {0, 0, 0}, + }, + /* UC 1 */ + { + {7, 1, 0}, + }, + /* UC 2 */ + { + {31, 2, 0}, + }, + /* UC 3 */ + { + {63, 12, 31}, + }, + /* UC 4 */ + { + {15, 7, 0}, + }, + /* UC 5 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + {15, 7, 0}, + }, + /* UC 6 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + {15, 7, 0}, + {7, 6, 0}, + {31, 18, 0}, + {63, 13, 31}, + {15, 10, 0}, + }, + /* UC 7 */ + { + {7, 1, 0}, + {7, 6, 0}, + + }, + /* UC 8 */ + { + {7, 1, 0}, + {31, 2, 0}, + }, + /* UC 9 */ + { + {7, 1, 0}, + {31, 2, 0}, + {7, 6, 0}, + {31, 18, 0}, + }, + /* UC 10 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + }, + /* UC 11 */ + { + {7, 1, 0}, + {31, 2, 0}, + {63, 12, 31}, + {7, 6, 0}, + {31, 18, 0}, + {63, 13, 31}, + }, + /* UC 12 */ + { + {7, 1, 0}, + {63, 12, 31}, + }, + /* UC 13 */ + { + {7, 1, 0}, + {63, 12, 31}, + {7, 6, 0}, + {63, 13, 31}, + }, + /* UC 14 */ + { + {7, 1, 0}, + {63, 12, 31}, + {15, 7, 0}, + }, + /* UC 15 */ + { + {7, 1, 0}, + {63, 12, 31}, + {15, 7, 0}, + {7, 6, 0}, + {63, 13, 31}, + {15, 10, 0}, + }, + /* UC 16 */ + { + {7, 1, 0}, + {15, 7, 0}, + }, + /* UC 17 */ + { + {7, 1, 0}, + {15, 7, 0}, + {7, 6, 0}, + {15, 10, 0}, + }, + /* UC 18 */ + { + {7, 1, 0}, + {31, 2, 0}, + {15, 7, 0}, + }, + /* UC 19 */ + { + {7, 1, 0}, + {31, 2, 0}, + {15, 7, 0}, + {7, 6, 0}, + {31, 18, 0}, + {15, 10, 0}, + }, +}; + +enum { + SWR_NOT_PRESENT, /* Device is detached/not present on the bus */ + SWR_ATTACHED_OK, /* Device is attached */ + SWR_ALERT, /* Device alters master for any interrupts */ + SWR_RESERVED, /* Reserved */ +}; + +#define SWRM_MAX_PORT_REG 40 +#define SWRM_MAX_INIT_REG 8 + +#define SWR_MSTR_MAX_REG_ADDR 0x1740 +#define SWR_MSTR_START_REG_ADDR 0x00 +#define SWR_MSTR_MAX_BUF_LEN 32 +#define BYTES_PER_LINE 12 +#define SWR_MSTR_RD_BUF_LEN 8 +#define SWR_MSTR_WR_BUF_LEN 32 + +static void swrm_copy_data_port_config(struct swr_master *master, + u8 inactive_bank); +static struct swr_mstr_ctrl *dbgswrm; +static struct dentry *debugfs_swrm_dent; +static struct dentry *debugfs_peek; +static struct dentry *debugfs_poke; +static struct dentry *debugfs_reg_dump; +static unsigned int read_data; + + +static bool swrm_is_msm_variant(int val) +{ + return (val == SWRM_VERSION_1_3); +} + +static int swrm_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int get_parameters(char *buf, u32 *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtou32(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + return 0; +} + +static ssize_t swrm_reg_show(char __user *ubuf, size_t count, + loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[SWR_MSTR_MAX_BUF_LEN]; + + if (!ubuf || !ppos) + return 0; + + for (i = (((int) *ppos / BYTES_PER_LINE) + SWR_MSTR_START_REG_ADDR); + i <= SWR_MSTR_MAX_REG_ADDR; i += 4) { + reg_val = dbgswrm->read(dbgswrm->handle, i); + len = snprintf(tmp_buf, 25, "0x%.3x: 0x%.2x\n", i, reg_val); + if ((total + len) >= count - 1) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + pr_err("%s: fail to copy reg dump\n", __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t swrm_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char lbuf[SWR_MSTR_RD_BUF_LEN]; + char *access_str; + ssize_t ret_cnt; + + if (!count || !file || !ppos || !ubuf) + return -EINVAL; + + access_str = file->private_data; + if (*ppos < 0) + return -EINVAL; + + if (!strcmp(access_str, "swrm_peek")) { + snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data); + ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf, + strnlen(lbuf, 7)); + } else if (!strcmp(access_str, "swrm_reg_dump")) { + ret_cnt = swrm_reg_show(ubuf, count, ppos); + } else { + pr_err("%s: %s not permitted to read\n", __func__, access_str); + ret_cnt = -EPERM; + } + return ret_cnt; +} + +static ssize_t swrm_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char lbuf[SWR_MSTR_WR_BUF_LEN]; + int rc; + u32 param[5]; + char *access_str; + + if (!filp || !ppos || !ubuf) + return -EINVAL; + + access_str = filp->private_data; + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + if (!strcmp(access_str, "swrm_poke")) { + /* write */ + rc = get_parameters(lbuf, param, 2); + if ((param[0] <= SWR_MSTR_MAX_REG_ADDR) && + (param[1] <= 0xFFFFFFFF) && + (rc == 0)) + rc = dbgswrm->write(dbgswrm->handle, param[0], + param[1]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "swrm_peek")) { + /* read */ + rc = get_parameters(lbuf, param, 1); + if ((param[0] <= SWR_MSTR_MAX_REG_ADDR) && (rc == 0)) + read_data = dbgswrm->read(dbgswrm->handle, param[0]); + else + rc = -EINVAL; + } + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations swrm_debug_ops = { + .open = swrm_debug_open, + .write = swrm_debug_write, + .read = swrm_debug_read, +}; + +static int swrm_set_ch_map(struct swr_mstr_ctrl *swrm, void *data) +{ + struct swr_mstr_port *pinfo = (struct swr_mstr_port *)data; + + swrm->mstr_port = kzalloc(sizeof(struct swr_mstr_port), GFP_KERNEL); + if (swrm->mstr_port == NULL) + return -ENOMEM; + swrm->mstr_port->num_port = pinfo->num_port; + swrm->mstr_port->port = kzalloc((pinfo->num_port * sizeof(u8)), + GFP_KERNEL); + if (!swrm->mstr_port->port) { + kfree(swrm->mstr_port); + swrm->mstr_port = NULL; + return -ENOMEM; + } + memcpy(swrm->mstr_port->port, pinfo->port, pinfo->num_port); + return 0; +} + +static bool swrm_is_port_en(struct swr_master *mstr) +{ + return !!(mstr->num_port); +} + +static int swrm_clk_request(struct swr_mstr_ctrl *swrm, bool enable) +{ + if (!swrm->clk || !swrm->handle) + return -EINVAL; + + if (enable) { + swrm->clk(swrm->handle, true); + swrm->state = SWR_MSTR_UP; + } else { + swrm->clk(swrm->handle, false); + swrm->state = SWR_MSTR_DOWN; + } + return 0; +} + +static int swrm_get_port_config(struct swr_master *master) +{ + u32 ch_rate = 0; + u32 num_ch = 0; + int i, uc_idx; + u32 portcount = 0; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + if (master->port[i].port_en) { + ch_rate += master->port[i].ch_rate; + num_ch += master->port[i].num_ch; + portcount++; + } + } + for (i = 0; i < ARRAY_SIZE(uc); i++) { + if ((uc[i].num_port == portcount) && + (uc[i].num_ch == num_ch) && + (uc[i].chrate == ch_rate)) { + uc_idx = i; + break; + } + } + + if (i >= ARRAY_SIZE(uc)) { + dev_err(&master->dev, + "%s: usecase port:%d, num_ch:%d, chrate:%d not found\n", + __func__, master->num_port, num_ch, ch_rate); + return -EINVAL; + } + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + if (master->port[i].port_en) { + master->port[i].sinterval = pp[uc_idx][i].si; + master->port[i].offset1 = pp[uc_idx][i].off1; + master->port[i].offset2 = pp[uc_idx][i].off2; + } + } + return 0; +} + +static int swrm_get_master_port(u8 *mstr_port_id, u8 slv_port_id) +{ + int i; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + if (mstr_ports[i] == slv_port_id) { + *mstr_port_id = i; + return 0; + } + } + return -EINVAL; +} + +static u32 swrm_get_packed_reg_val(u8 *cmd_id, u8 cmd_data, + u8 dev_addr, u16 reg_addr) +{ + u32 val; + u8 id = *cmd_id; + + if (id != SWR_BROADCAST_CMD_ID) { + if (id < 14) + id += 1; + else + id = 0; + *cmd_id = id; + } + val = SWR_REG_VAL_PACK(cmd_data, dev_addr, id, reg_addr); + + return val; +} + +static int swrm_cmd_fifo_rd_cmd(struct swr_mstr_ctrl *swrm, int *cmd_data, + u8 dev_addr, u8 cmd_id, u16 reg_addr, + u32 len) +{ + u32 val; + int ret = 0; + + val = swrm_get_packed_reg_val(&swrm->rcmd_id, len, dev_addr, reg_addr); + ret = swrm->write(swrm->handle, SWRM_CMD_FIFO_RD_CMD, val); + if (ret < 0) { + dev_err(swrm->dev, "%s: reg 0x%x write failed, err:%d\n", + __func__, val, ret); + goto err; + } + *cmd_data = swrm->read(swrm->handle, SWRM_CMD_FIFO_RD_FIFO_ADDR); + dev_dbg(swrm->dev, + "%s: reg: 0x%x, cmd_id: 0x%x, dev_id: 0x%x, cmd_data: 0x%x\n", + __func__, reg_addr, cmd_id, dev_addr, *cmd_data); +err: + return ret; +} + +static int swrm_cmd_fifo_wr_cmd(struct swr_mstr_ctrl *swrm, u8 cmd_data, + u8 dev_addr, u8 cmd_id, u16 reg_addr) +{ + u32 val; + int ret = 0; + + if (!cmd_id) + val = swrm_get_packed_reg_val(&swrm->wcmd_id, cmd_data, + dev_addr, reg_addr); + else + val = swrm_get_packed_reg_val(&cmd_id, cmd_data, + dev_addr, reg_addr); + + dev_dbg(swrm->dev, + "%s: reg: 0x%x, cmd_id: 0x%x, dev_id: 0x%x, cmd_data: 0x%x\n", + __func__, reg_addr, cmd_id, dev_addr, cmd_data); + ret = swrm->write(swrm->handle, SWRM_CMD_FIFO_WR_CMD, val); + if (ret < 0) { + dev_err(swrm->dev, "%s: reg 0x%x write failed, err:%d\n", + __func__, val, ret); + goto err; + } + if (cmd_id == 0xF) { + /* + * sleep for 10ms for MSM soundwire variant to allow broadcast + * command to complete. + */ + if (swrm_is_msm_variant(swrm->version)) + usleep_range(10000, 10100); + else + wait_for_completion_timeout(&swrm->broadcast, + (2 * HZ/10)); + } +err: + return ret; +} + +static int swrm_read(struct swr_master *master, u8 dev_num, u16 reg_addr, + void *buf, u32 len) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + int val; + u8 *reg_val = (u8 *)buf; + + if (!swrm) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + + if (dev_num) + ret = swrm_cmd_fifo_rd_cmd(swrm, &val, dev_num, 0, reg_addr, + len); + else + val = swrm->read(swrm->handle, reg_addr); + + if (!ret) + *reg_val = (u8)val; + + pm_runtime_mark_last_busy(&swrm->pdev->dev); + + return ret; +} + +static int swrm_write(struct swr_master *master, u8 dev_num, u16 reg_addr, + const void *buf) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + u8 reg_val = *(u8 *)buf; + + if (!swrm) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + + if (dev_num) + ret = swrm_cmd_fifo_wr_cmd(swrm, reg_val, dev_num, 0, reg_addr); + else + ret = swrm->write(swrm->handle, reg_addr, reg_val); + + pm_runtime_mark_last_busy(&swrm->pdev->dev); + + return ret; +} + +static int swrm_bulk_write(struct swr_master *master, u8 dev_num, void *reg, + const void *buf, size_t len) +{ + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int ret = 0; + int i; + u32 *val; + u32 *swr_fifo_reg; + + if (!swrm || !swrm->handle) { + dev_err(&master->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + if (len <= 0) + return -EINVAL; + + if (dev_num) { + swr_fifo_reg = kcalloc(len, sizeof(u32), GFP_KERNEL); + if (!swr_fifo_reg) { + ret = -ENOMEM; + goto err; + } + val = kcalloc(len, sizeof(u32), GFP_KERNEL); + if (!val) { + ret = -ENOMEM; + goto mem_fail; + } + + for (i = 0; i < len; i++) { + val[i] = swrm_get_packed_reg_val(&swrm->wcmd_id, + ((u8 *)buf)[i], + dev_num, + ((u16 *)reg)[i]); + swr_fifo_reg[i] = SWRM_CMD_FIFO_WR_CMD; + } + ret = swrm->bulk_write(swrm->handle, swr_fifo_reg, val, len); + if (ret) { + dev_err(&master->dev, "%s: bulk write failed\n", + __func__); + ret = -EINVAL; + } + } else { + dev_err(&master->dev, + "%s: No support of Bulk write for master regs\n", + __func__); + ret = -EINVAL; + goto err; + } + kfree(val); +mem_fail: + kfree(swr_fifo_reg); +err: + pm_runtime_mark_last_busy(&swrm->pdev->dev); + return ret; +} + +static u8 get_inactive_bank_num(struct swr_mstr_ctrl *swrm) +{ + return (swrm->read(swrm->handle, SWRM_MCP_STATUS) & + SWRM_MCP_STATUS_BANK_NUM_MASK) ? 0 : 1; +} + +static void enable_bank_switch(struct swr_mstr_ctrl *swrm, u8 bank, + u8 row, u8 col) +{ + swrm_cmd_fifo_wr_cmd(swrm, ((row << 3) | col), 0xF, 0xF, + SWRS_SCP_FRAME_CTRL_BANK(bank)); +} + +static struct swr_port_info *swrm_get_port(struct swr_master *master, + u8 port_id) +{ + int i; + struct swr_port_info *port = NULL; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + port = &master->port[i]; + if (port->port_id == port_id) { + dev_dbg(&master->dev, "%s: port_id: %d, index: %d\n", + __func__, port_id, i); + return port; + } + } + + return NULL; +} + +static struct swr_port_info *swrm_get_avail_port(struct swr_master *master) +{ + int i; + struct swr_port_info *port = NULL; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + port = &master->port[i]; + if (port->port_en) + continue; + + dev_dbg(&master->dev, "%s: port_id: %d, index: %d\n", + __func__, port->port_id, i); + return port; + } + + return NULL; +} + +static struct swr_port_info *swrm_get_enabled_port(struct swr_master *master, + u8 port_id) +{ + int i; + struct swr_port_info *port = NULL; + + for (i = 0; i < SWR_MSTR_PORT_LEN; i++) { + port = &master->port[i]; + if ((port->port_id == port_id) && (port->port_en == true)) + break; + } + if (i == SWR_MSTR_PORT_LEN) + port = NULL; + return port; +} + +static bool swrm_remove_from_group(struct swr_master *master) +{ + struct swr_device *swr_dev; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + bool is_removed = false; + + if (!swrm) + goto end; + + mutex_lock(&swrm->mlock); + if ((swrm->num_rx_chs > 1) && + (swrm->num_rx_chs == swrm->num_cfg_devs)) { + list_for_each_entry(swr_dev, &master->devices, + dev_list) { + swr_dev->group_id = SWR_GROUP_NONE; + master->gr_sid = 0; + } + is_removed = true; + } + mutex_unlock(&swrm->mlock); + +end: + return is_removed; +} + +static void swrm_cleanup_disabled_data_ports(struct swr_master *master, + u8 bank) +{ + u32 value; + struct swr_port_info *port; + int i; + int port_type; + struct swrm_mports *mport, *mport_next = NULL; + int port_disable_cnt = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return; + } + + dev_dbg(swrm->dev, "%s: master num_port: %d\n", __func__, + master->num_port); + + mport = list_first_entry_or_null(&swrm->mport_list, + struct swrm_mports, + list); + if (!mport) { + dev_err(swrm->dev, "%s: list is empty\n", __func__); + return; + } + + for (i = 0; i < master->num_port; i++) { + port = swrm_get_port(master, mstr_ports[mport->id]); + if (!port || port->ch_en) + goto inc_loop; + + port_disable_cnt++; + port_type = mstr_port_type[mport->id]; + value = ((port->ch_en) + << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); + value |= ((port->offset2) + << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + value |= ((port->offset1) + << SWRM_DP_PORT_CTRL_OFFSET1_SHFT); + value |= port->sinterval; + + swrm->write(swrm->handle, + SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank), + value); + swrm_cmd_fifo_wr_cmd(swrm, 0x00, port->dev_id, 0x00, + SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank)); + + dev_dbg(swrm->dev, "%s: mport :%d, reg: 0x%x, val: 0x%x\n", + __func__, mport->id, + (SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank)), value); + +inc_loop: + mport_next = list_next_entry(mport, list); + if (port && !port->ch_en) { + list_del(&mport->list); + kfree(mport); + } + if (!mport_next) { + dev_err(swrm->dev, "%s: end of list\n", __func__); + break; + } + mport = mport_next; + } + master->num_port -= port_disable_cnt; + + dev_dbg(swrm->dev, "%s:disable ports: %d, active ports (rem): %d\n", + __func__, port_disable_cnt, master->num_port); +} + +static void swrm_slvdev_datapath_control(struct swr_master *master, + bool enable) +{ + u8 bank; + u32 value, n_col; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + int mask = (SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK | + SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK | + SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK); + u8 inactive_bank; + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return; + } + + bank = get_inactive_bank_num(swrm); + + dev_dbg(swrm->dev, "%s: enable: %d, cfg_devs: %d\n", + __func__, enable, swrm->num_cfg_devs); + + if (enable) { + /* set Row = 48 and col = 16 */ + n_col = SWR_MAX_COL; + } else { + /* + * Do not change to 48x2 if number of channels configured + * as stereo and if disable datapath is called for the + * first slave device + */ + if (swrm->num_cfg_devs > 0) + n_col = SWR_MAX_COL; + else + n_col = SWR_MIN_COL; + + /* + * All ports are already disabled, no need to perform + * bank-switch and copy operation. This case can arise + * when speaker channels are enabled in stereo mode with + * BROADCAST and disabled in GROUP_NONE + */ + if (master->num_port == 0) + return; + } + + value = swrm->read(swrm->handle, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank)); + value &= (~mask); + value |= ((0 << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) | + (n_col << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT) | + (0 << SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT)); + swrm->write(swrm->handle, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank), value); + + dev_dbg(swrm->dev, "%s: regaddr: 0x%x, value: 0x%x\n", __func__, + SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank), value); + + enable_bank_switch(swrm, bank, SWR_MAX_ROW, n_col); + + inactive_bank = bank ? 0 : 1; + if (enable) + swrm_copy_data_port_config(master, inactive_bank); + else + swrm_cleanup_disabled_data_ports(master, inactive_bank); + + if (!swrm_is_port_en(master)) { + dev_dbg(&master->dev, "%s: pm_runtime auto suspend triggered\n", + __func__); + pm_runtime_mark_last_busy(&swrm->pdev->dev); + pm_runtime_put_autosuspend(&swrm->pdev->dev); + } +} + +static void swrm_apply_port_config(struct swr_master *master) +{ + u8 bank; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: Invalid handle to swr controller\n", + __func__); + return; + } + + bank = get_inactive_bank_num(swrm); + dev_dbg(swrm->dev, "%s: enter bank: %d master_ports: %d\n", + __func__, bank, master->num_port); + + + swrm_cmd_fifo_wr_cmd(swrm, 0x01, 0xF, 0x00, + SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(bank)); + + swrm_copy_data_port_config(master, bank); +} + +static void swrm_copy_data_port_config(struct swr_master *master, u8 bank) +{ + u32 value; + struct swr_port_info *port; + int i; + int port_type; + struct swrm_mports *mport; + u32 reg[SWRM_MAX_PORT_REG]; + u32 val[SWRM_MAX_PORT_REG]; + int len = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + pr_err("%s: swrm is null\n", __func__); + return; + } + + dev_dbg(swrm->dev, "%s: master num_port: %d\n", __func__, + master->num_port); + + mport = list_first_entry_or_null(&swrm->mport_list, + struct swrm_mports, + list); + if (!mport) { + dev_err(swrm->dev, "%s: list is empty\n", __func__); + return; + } + for (i = 0; i < master->num_port; i++) { + + port = swrm_get_enabled_port(master, mstr_ports[mport->id]); + if (!port) + continue; + port_type = mstr_port_type[mport->id]; + if (!port->dev_id || (port->dev_id > master->num_dev)) { + dev_dbg(swrm->dev, "%s: invalid device id = %d\n", + __func__, port->dev_id); + continue; + } + value = ((port->ch_en) + << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT); + value |= ((port->offset2) + << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + value |= ((port->offset1) + << SWRM_DP_PORT_CTRL_OFFSET1_SHFT); + value |= port->sinterval; + + reg[len] = SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank); + val[len++] = value; + + dev_dbg(swrm->dev, "%s: mport :%d, reg: 0x%x, val: 0x%x\n", + __func__, mport->id, + (SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank)), value); + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->ch_en, port->dev_id, 0x00, + SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank)); + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->sinterval, + port->dev_id, 0x00, + SWRS_DP_SAMPLE_CONTROL_1_BANK(port_type, bank)); + + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->offset1, + port->dev_id, 0x00, + SWRS_DP_OFFSET_CONTROL_1_BANK(port_type, bank)); + + if (port_type != 0) { + reg[len] = SWRM_CMD_FIFO_WR_CMD; + val[len++] = SWR_REG_VAL_PACK(port->offset2, + port->dev_id, 0x00, + SWRS_DP_OFFSET_CONTROL_2_BANK(port_type, + bank)); + } + mport = list_next_entry(mport, list); + if (!mport) { + dev_err(swrm->dev, "%s: end of list\n", __func__); + break; + } + } + swrm->bulk_write(swrm->handle, reg, val, len); +} + +static int swrm_connect_port(struct swr_master *master, + struct swr_params *portinfo) +{ + int i; + struct swr_port_info *port; + int ret = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + struct swrm_mports *mport; + struct list_head *ptr, *next; + + dev_dbg(&master->dev, "%s: enter\n", __func__); + if (!portinfo) + return -EINVAL; + + if (!swrm) { + dev_err(&master->dev, + "%s: Invalid handle to swr controller\n", + __func__); + return -EINVAL; + } + + mutex_lock(&swrm->mlock); + if (!swrm_is_port_en(master)) + pm_runtime_get_sync(&swrm->pdev->dev); + + for (i = 0; i < portinfo->num_port; i++) { + mport = kzalloc(sizeof(struct swrm_mports), GFP_KERNEL); + if (!mport) { + ret = -ENOMEM; + goto mem_fail; + } + ret = swrm_get_master_port(&mport->id, + portinfo->port_id[i]); + if (ret < 0) { + dev_err(&master->dev, + "%s: mstr portid for slv port %d not found\n", + __func__, portinfo->port_id[i]); + goto port_fail; + } + port = swrm_get_avail_port(master); + if (!port) { + dev_err(&master->dev, + "%s: avail ports not found!\n", __func__); + goto port_fail; + } + list_add(&mport->list, &swrm->mport_list); + port->dev_id = portinfo->dev_id; + port->port_id = portinfo->port_id[i]; + port->num_ch = portinfo->num_ch[i]; + port->ch_rate = portinfo->ch_rate[i]; + port->ch_en = portinfo->ch_en[i]; + port->port_en = true; + dev_dbg(&master->dev, + "%s: mstr port %d, slv port %d ch_rate %d num_ch %d\n", + __func__, mport->id, port->port_id, port->ch_rate, + port->num_ch); + } + master->num_port += portinfo->num_port; + if (master->num_port >= SWR_MSTR_PORT_LEN) + master->num_port = SWR_MSTR_PORT_LEN; + + swrm_get_port_config(master); + swr_port_response(master, portinfo->tid); + swrm->num_cfg_devs += 1; + dev_dbg(&master->dev, "%s: cfg_devs: %d, rx_chs: %d\n", + __func__, swrm->num_cfg_devs, swrm->num_rx_chs); + if (swrm->num_rx_chs > 1) { + if (swrm->num_rx_chs == swrm->num_cfg_devs) + swrm_apply_port_config(master); + } else { + swrm_apply_port_config(master); + } + mutex_unlock(&swrm->mlock); + return 0; + +port_fail: + kfree(mport); +mem_fail: + list_for_each_safe(ptr, next, &swrm->mport_list) { + mport = list_entry(ptr, struct swrm_mports, list); + for (i = 0; i < portinfo->num_port; i++) { + if (portinfo->port_id[i] == mstr_ports[mport->id]) { + port = swrm_get_port(master, + portinfo->port_id[i]); + if (port) + port->ch_en = false; + list_del(&mport->list); + kfree(mport); + break; + } + } + } + mutex_unlock(&swrm->mlock); + return ret; +} + +static int swrm_disconnect_port(struct swr_master *master, + struct swr_params *portinfo) +{ + int i; + struct swr_port_info *port; + u8 bank; + u32 value; + int ret = 0; + u8 mport_id = 0; + int port_type = 0; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master); + + if (!swrm) { + dev_err(&master->dev, + "%s: Invalid handle to swr controller\n", + __func__); + return -EINVAL; + } + + if (!portinfo) { + dev_err(&master->dev, "%s: portinfo is NULL\n", __func__); + return -EINVAL; + } + mutex_lock(&swrm->mlock); + bank = get_inactive_bank_num(swrm); + for (i = 0; i < portinfo->num_port; i++) { + ret = swrm_get_master_port(&mport_id, + portinfo->port_id[i]); + if (ret < 0) { + dev_err(&master->dev, + "%s: mstr portid for slv port %d not found\n", + __func__, portinfo->port_id[i]); + mutex_unlock(&swrm->mlock); + return -EINVAL; + } + port = swrm_get_enabled_port(master, portinfo->port_id[i]); + if (!port) { + dev_dbg(&master->dev, "%s: port %d already disabled\n", + __func__, portinfo->port_id[i]); + continue; + } + port_type = mstr_port_type[mport_id]; + port->dev_id = portinfo->dev_id; + port->port_en = false; + port->ch_en = 0; + value = port->ch_en << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT; + value |= (port->offset2 << SWRM_DP_PORT_CTRL_OFFSET2_SHFT); + value |= (port->offset1 << SWRM_DP_PORT_CTRL_OFFSET1_SHFT); + value |= port->sinterval; + + + swrm->write(swrm->handle, + SWRM_DP_PORT_CTRL_BANK((mport_id+1), bank), + value); + swrm_cmd_fifo_wr_cmd(swrm, 0x00, port->dev_id, 0x00, + SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank)); + } + + swr_port_response(master, portinfo->tid); + swrm->num_cfg_devs -= 1; + dev_dbg(&master->dev, "%s: cfg_devs: %d, rx_chs: %d, active ports: %d\n", + __func__, swrm->num_cfg_devs, swrm->num_rx_chs, + master->num_port); + mutex_unlock(&swrm->mlock); + + return 0; +} + +static int swrm_check_slave_change_status(struct swr_mstr_ctrl *swrm, + int status, u8 *devnum) +{ + int i; + int new_sts = status; + int ret = SWR_NOT_PRESENT; + + if (status != swrm->slave_status) { + for (i = 0; i < (swrm->master.num_dev + 1); i++) { + if ((status & SWRM_MCP_SLV_STATUS_MASK) != + (swrm->slave_status & SWRM_MCP_SLV_STATUS_MASK)) { + ret = (status & SWRM_MCP_SLV_STATUS_MASK); + *devnum = i; + break; + } + status >>= 2; + swrm->slave_status >>= 2; + } + swrm->slave_status = new_sts; + } + return ret; +} + +static irqreturn_t swr_mstr_interrupt(int irq, void *dev) +{ + struct swr_mstr_ctrl *swrm = dev; + u32 value, intr_sts; + int status, chg_sts, i; + u8 devnum = 0; + int ret = IRQ_HANDLED; + + pm_runtime_get_sync(&swrm->pdev->dev); + intr_sts = swrm->read(swrm->handle, SWRM_INTERRUPT_STATUS); + intr_sts &= SWRM_INTERRUPT_STATUS_RMSK; + for (i = 0; i < SWRM_INTERRUPT_MAX; i++) { + value = intr_sts & (1 << i); + if (!value) + continue; + + swrm->write(swrm->handle, SWRM_INTERRUPT_CLEAR, value); + switch (value) { + case SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ: + dev_dbg(swrm->dev, "SWR slave pend irq\n"); + break; + case SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED: + dev_dbg(swrm->dev, "SWR new slave attached\n"); + break; + case SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS: + status = swrm->read(swrm->handle, SWRM_MCP_SLV_STATUS); + if (status == swrm->slave_status) { + dev_dbg(swrm->dev, + "%s: No change in slave status: %d\n", + __func__, status); + break; + } + chg_sts = swrm_check_slave_change_status(swrm, status, + &devnum); + switch (chg_sts) { + case SWR_NOT_PRESENT: + dev_dbg(swrm->dev, "device %d got detached\n", + devnum); + break; + case SWR_ATTACHED_OK: + dev_dbg(swrm->dev, "device %d got attached\n", + devnum); + break; + case SWR_ALERT: + dev_dbg(swrm->dev, + "device %d has pending interrupt\n", + devnum); + break; + } + break; + case SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET: + dev_err_ratelimited(swrm->dev, "SWR bus clash detected\n"); + break; + case SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW: + dev_dbg(swrm->dev, "SWR read FIFO overflow\n"); + break; + case SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW: + dev_dbg(swrm->dev, "SWR read FIFO underflow\n"); + break; + case SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW: + dev_dbg(swrm->dev, "SWR write FIFO overflow\n"); + break; + case SWRM_INTERRUPT_STATUS_CMD_ERROR: + value = swrm->read(swrm->handle, SWRM_CMD_FIFO_STATUS); + dev_err_ratelimited(swrm->dev, + "SWR CMD error, fifo status 0x%x, flushing fifo\n", + value); + swrm->write(swrm->handle, SWRM_CMD_FIFO_CMD, 0x1); + break; + case SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION: + dev_dbg(swrm->dev, "SWR Port collision detected\n"); + break; + case SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH: + dev_dbg(swrm->dev, "SWR read enable valid mismatch\n"); + break; + case SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED: + complete(&swrm->broadcast); + dev_dbg(swrm->dev, "SWR cmd id finished\n"); + break; + case SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED: + break; + case SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED: + break; + case SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL: + break; + case SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED: + complete(&swrm->reset); + break; + case SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED: + break; + default: + dev_err_ratelimited(swrm->dev, "SWR unknown interrupt\n"); + ret = IRQ_NONE; + break; + } + } + pm_runtime_mark_last_busy(&swrm->pdev->dev); + pm_runtime_put_autosuspend(&swrm->pdev->dev); + return ret; +} + +static int swrm_get_device_status(struct swr_mstr_ctrl *swrm, u8 devnum) +{ + u32 val; + + swrm->slave_status = swrm->read(swrm->handle, SWRM_MCP_SLV_STATUS); + val = (swrm->slave_status >> (devnum * 2)); + val &= SWRM_MCP_SLV_STATUS_MASK; + return val; +} + +static int swrm_get_logical_dev_num(struct swr_master *mstr, u64 dev_id, + u8 *dev_num) +{ + int i; + u64 id = 0; + int ret = -EINVAL; + struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(mstr); + + if (!swrm) { + pr_err("%s: Invalid handle to swr controller\n", + __func__); + return ret; + } + + pm_runtime_get_sync(&swrm->pdev->dev); + for (i = 1; i < (mstr->num_dev + 1); i++) { + id = ((u64)(swrm->read(swrm->handle, + SWRM_ENUMERATOR_SLAVE_DEV_ID_2(i))) << 32); + id |= swrm->read(swrm->handle, + SWRM_ENUMERATOR_SLAVE_DEV_ID_1(i)); + if ((id & SWR_DEV_ID_MASK) == dev_id) { + if (swrm_get_device_status(swrm, i) == 0x01) { + *dev_num = i; + ret = 0; + } else { + dev_err(swrm->dev, "%s: device is not ready\n", + __func__); + } + goto found; + } + } + dev_err(swrm->dev, "%s: device id 0x%llx does not match with 0x%llx\n", + __func__, id, dev_id); +found: + pm_runtime_mark_last_busy(&swrm->pdev->dev); + pm_runtime_put_autosuspend(&swrm->pdev->dev); + return ret; +} +static int swrm_master_init(struct swr_mstr_ctrl *swrm) +{ + int ret = 0; + u32 val; + u8 row_ctrl = SWR_MAX_ROW; + u8 col_ctrl = SWR_MIN_COL; + u8 ssp_period = 1; + u8 retry_cmd_num = 3; + u32 reg[SWRM_MAX_INIT_REG]; + u32 value[SWRM_MAX_INIT_REG]; + int len = 0; + + /* Clear Rows and Cols */ + val = ((row_ctrl << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) | + (col_ctrl << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT) | + (ssp_period << SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT)); + + reg[len] = SWRM_MCP_FRAME_CTRL_BANK_ADDR(0); + value[len++] = val; + + /* Set Auto enumeration flag */ + reg[len] = SWRM_ENUMERATOR_CFG_ADDR; + value[len++] = 1; + + /* Mask soundwire interrupts */ + reg[len] = SWRM_INTERRUPT_MASK_ADDR; + value[len++] = 0x1FFFD; + + /* Configure No pings */ + val = swrm->read(swrm->handle, SWRM_MCP_CFG_ADDR); + val &= ~SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK; + val |= (0x1f << SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT); + reg[len] = SWRM_MCP_CFG_ADDR; + value[len++] = val; + + /* Configure number of retries of a read/write cmd */ + val = (retry_cmd_num << SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT); + reg[len] = SWRM_CMD_FIFO_CFG_ADDR; + value[len++] = val; + + /* Set IRQ to PULSE */ + reg[len] = SWRM_COMP_CFG_ADDR; + value[len++] = 0x02; + + reg[len] = SWRM_COMP_CFG_ADDR; + value[len++] = 0x03; + + reg[len] = SWRM_INTERRUPT_CLEAR; + value[len++] = 0x08; + + swrm->bulk_write(swrm->handle, reg, value, len); + + return ret; +} + +static int swrm_probe(struct platform_device *pdev) +{ + struct swr_mstr_ctrl *swrm; + struct swr_ctrl_platform_data *pdata; + int ret; + + /* Allocate soundwire master driver structure */ + swrm = kzalloc(sizeof(struct swr_mstr_ctrl), GFP_KERNEL); + if (!swrm) { + ret = -ENOMEM; + goto err_memory_fail; + } + swrm->dev = &pdev->dev; + swrm->pdev = pdev; + platform_set_drvdata(pdev, swrm); + swr_set_ctrl_data(&swrm->master, swrm); + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "%s: pdata from parent is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->handle = (void *)pdata->handle; + if (!swrm->handle) { + dev_err(&pdev->dev, "%s: swrm->handle is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->read = pdata->read; + if (!swrm->read) { + dev_err(&pdev->dev, "%s: swrm->read is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->write = pdata->write; + if (!swrm->write) { + dev_err(&pdev->dev, "%s: swrm->write is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->bulk_write = pdata->bulk_write; + if (!swrm->bulk_write) { + dev_err(&pdev->dev, "%s: swrm->bulk_write is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->clk = pdata->clk; + if (!swrm->clk) { + dev_err(&pdev->dev, "%s: swrm->clk is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->reg_irq = pdata->reg_irq; + if (!swrm->reg_irq) { + dev_err(&pdev->dev, "%s: swrm->reg_irq is NULL\n", + __func__); + ret = -EINVAL; + goto err_pdata_fail; + } + swrm->master.read = swrm_read; + swrm->master.write = swrm_write; + swrm->master.bulk_write = swrm_bulk_write; + swrm->master.get_logical_dev_num = swrm_get_logical_dev_num; + swrm->master.connect_port = swrm_connect_port; + swrm->master.disconnect_port = swrm_disconnect_port; + swrm->master.slvdev_datapath_control = swrm_slvdev_datapath_control; + swrm->master.remove_from_group = swrm_remove_from_group; + swrm->master.dev.parent = &pdev->dev; + swrm->master.dev.of_node = pdev->dev.of_node; + swrm->master.num_port = 0; + swrm->num_enum_slaves = 0; + swrm->rcmd_id = 0; + swrm->wcmd_id = 0; + swrm->slave_status = 0; + swrm->num_rx_chs = 0; + swrm->state = SWR_MSTR_RESUME; + init_completion(&swrm->reset); + init_completion(&swrm->broadcast); + mutex_init(&swrm->mlock); + INIT_LIST_HEAD(&swrm->mport_list); + mutex_init(&swrm->reslock); + + ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm, + SWR_IRQ_REGISTER); + if (ret) { + dev_err(&pdev->dev, "%s: IRQ register failed ret %d\n", + __func__, ret); + goto err_irq_fail; + } + + ret = swr_register_master(&swrm->master); + if (ret) { + dev_err(&pdev->dev, "%s: error adding swr master\n", __func__); + goto err_mstr_fail; + } + + /* Add devices registered with board-info as the + * controller will be up now + */ + swr_master_add_boarddevices(&swrm->master); + mutex_lock(&swrm->mlock); + swrm_clk_request(swrm, true); + ret = swrm_master_init(swrm); + if (ret < 0) { + dev_err(&pdev->dev, + "%s: Error in master Initializaiton, err %d\n", + __func__, ret); + mutex_unlock(&swrm->mlock); + goto err_mstr_fail; + } + swrm->version = swrm->read(swrm->handle, SWRM_COMP_HW_VERSION); + + mutex_unlock(&swrm->mlock); + + if (pdev->dev.of_node) + of_register_swr_devices(&swrm->master); + + dbgswrm = swrm; + debugfs_swrm_dent = debugfs_create_dir(dev_name(&pdev->dev), 0); + if (!IS_ERR(debugfs_swrm_dent)) { + debugfs_peek = debugfs_create_file("swrm_peek", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_peek", &swrm_debug_ops); + + debugfs_poke = debugfs_create_file("swrm_poke", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_poke", &swrm_debug_ops); + + debugfs_reg_dump = debugfs_create_file("swrm_reg_dump", + S_IFREG | 0444, debugfs_swrm_dent, + (void *) "swrm_reg_dump", + &swrm_debug_ops); + } + pm_runtime_set_autosuspend_delay(&pdev->dev, auto_suspend_timer); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_set_active(&pdev->dev); + pm_runtime_enable(&pdev->dev); + pm_runtime_mark_last_busy(&pdev->dev); + + return 0; +err_mstr_fail: + swrm->reg_irq(swrm->handle, swr_mstr_interrupt, + swrm, SWR_IRQ_FREE); +err_irq_fail: +err_pdata_fail: + kfree(swrm); +err_memory_fail: + return ret; +} + +static int swrm_remove(struct platform_device *pdev) +{ + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + swrm->reg_irq(swrm->handle, swr_mstr_interrupt, + swrm, SWR_IRQ_FREE); + if (swrm->mstr_port) { + kfree(swrm->mstr_port->port); + swrm->mstr_port->port = NULL; + kfree(swrm->mstr_port); + swrm->mstr_port = NULL; + } + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); + swr_unregister_master(&swrm->master); + mutex_destroy(&swrm->mlock); + mutex_destroy(&swrm->reslock); + kfree(swrm); + return 0; +} + +static int swrm_clk_pause(struct swr_mstr_ctrl *swrm) +{ + u32 val; + + dev_dbg(swrm->dev, "%s: state: %d\n", __func__, swrm->state); + swrm->write(swrm->handle, SWRM_INTERRUPT_MASK_ADDR, 0x1FDFD); + val = swrm->read(swrm->handle, SWRM_MCP_CFG_ADDR); + val |= SWRM_MCP_CFG_BUS_CLK_PAUSE_BMSK; + swrm->write(swrm->handle, SWRM_MCP_CFG_ADDR, val); + swrm->state = SWR_MSTR_PAUSE; + + return 0; +} + +#ifdef CONFIG_PM +static int swrm_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + struct swr_master *mstr = &swrm->master; + struct swr_device *swr_dev; + + dev_dbg(dev, "%s: pm_runtime: resume, state:%d\n", + __func__, swrm->state); + mutex_lock(&swrm->reslock); + if ((swrm->state == SWR_MSTR_PAUSE) || + (swrm->state == SWR_MSTR_DOWN)) { + if (swrm->state == SWR_MSTR_DOWN) { + if (swrm_clk_request(swrm, true)) + goto exit; + } + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_device_up(swr_dev); + if (ret) { + dev_err(dev, + "%s: failed to wakeup swr dev %d\n", + __func__, swr_dev->dev_num); + swrm_clk_request(swrm, false); + goto exit; + } + } + swrm->write(swrm->handle, SWRM_COMP_SW_RESET, 0x01); + swrm->write(swrm->handle, SWRM_COMP_SW_RESET, 0x01); + swrm_master_init(swrm); + } +exit: + pm_runtime_set_autosuspend_delay(&pdev->dev, auto_suspend_timer); + mutex_unlock(&swrm->reslock); + return ret; +} + +static int swrm_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + struct swr_master *mstr = &swrm->master; + struct swr_device *swr_dev; + + dev_dbg(dev, "%s: pm_runtime: suspend state: %d\n", + __func__, swrm->state); + mutex_lock(&swrm->reslock); + if ((swrm->state == SWR_MSTR_RESUME) || + (swrm->state == SWR_MSTR_UP)) { + if (swrm_is_port_en(&swrm->master)) { + dev_dbg(dev, "%s ports are enabled\n", __func__); + ret = -EBUSY; + goto exit; + } + swrm_clk_pause(swrm); + swrm->write(swrm->handle, SWRM_COMP_CFG_ADDR, 0x00); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_device_down(swr_dev); + if (ret) { + dev_err(dev, + "%s: failed to shutdown swr dev %d\n", + __func__, swr_dev->dev_num); + goto exit; + } + } + swrm_clk_request(swrm, false); + } +exit: + mutex_unlock(&swrm->reslock); + return ret; +} +#endif /* CONFIG_PM */ + +static int swrm_device_down(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + int ret = 0; + struct swr_master *mstr = &swrm->master; + struct swr_device *swr_dev; + + dev_dbg(dev, "%s: swrm state: %d\n", __func__, swrm->state); + mutex_lock(&swrm->reslock); + if ((swrm->state == SWR_MSTR_RESUME) || + (swrm->state == SWR_MSTR_UP)) { + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_device_down(swr_dev); + if (ret) + dev_err(dev, + "%s: failed to shutdown swr dev %d\n", + __func__, swr_dev->dev_num); + } + dev_dbg(dev, "%s: Shutting down SWRM\n", __func__); + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + swrm_clk_request(swrm, false); + } + mutex_unlock(&swrm->reslock); + return ret; +} + +/** + * swrm_wcd_notify - parent device can notify to soundwire master through + * this function + * @pdev: pointer to platform device structure + * @id: command id from parent to the soundwire master + * @data: data from parent device to soundwire master + */ +int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data) +{ + struct swr_mstr_ctrl *swrm; + int ret = 0; + struct swr_master *mstr; + struct swr_device *swr_dev; + + if (!pdev) { + pr_err("%s: pdev is NULL\n", __func__); + return -EINVAL; + } + swrm = platform_get_drvdata(pdev); + if (!swrm) { + dev_err(&pdev->dev, "%s: swrm is NULL\n", __func__); + return -EINVAL; + } + mstr = &swrm->master; + + switch (id) { + case SWR_CH_MAP: + if (!data) { + dev_err(swrm->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + } else { + ret = swrm_set_ch_map(swrm, data); + } + break; + case SWR_DEVICE_DOWN: + dev_dbg(swrm->dev, "%s: swr master down called\n", __func__); + mutex_lock(&swrm->mlock); + if ((swrm->state == SWR_MSTR_PAUSE) || + (swrm->state == SWR_MSTR_DOWN)) + dev_dbg(swrm->dev, "%s: SWR master is already Down: %d\n", + __func__, swrm->state); + else + swrm_device_down(&pdev->dev); + mutex_unlock(&swrm->mlock); + break; + case SWR_DEVICE_UP: + dev_dbg(swrm->dev, "%s: swr master up called\n", __func__); + mutex_lock(&swrm->mlock); + mutex_lock(&swrm->reslock); + if ((swrm->state == SWR_MSTR_RESUME) || + (swrm->state == SWR_MSTR_UP)) { + dev_dbg(swrm->dev, "%s: SWR master is already UP: %d\n", + __func__, swrm->state); + } else { + pm_runtime_mark_last_busy(&pdev->dev); + mutex_unlock(&swrm->reslock); + pm_runtime_get_sync(&pdev->dev); + mutex_lock(&swrm->reslock); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + ret = swr_reset_device(swr_dev); + if (ret) { + dev_err(swrm->dev, + "%s: failed to reset swr device %d\n", + __func__, swr_dev->dev_num); + swrm_clk_request(swrm, false); + } + } + pm_runtime_mark_last_busy(&pdev->dev); + pm_runtime_put_autosuspend(&pdev->dev); + } + mutex_unlock(&swrm->reslock); + mutex_unlock(&swrm->mlock); + break; + case SWR_SET_NUM_RX_CH: + if (!data) { + dev_err(swrm->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + } else { + mutex_lock(&swrm->mlock); + swrm->num_rx_chs = *(int *)data; + if ((swrm->num_rx_chs > 1) && !swrm->num_cfg_devs) { + list_for_each_entry(swr_dev, &mstr->devices, + dev_list) { + ret = swr_set_device_group(swr_dev, + SWR_BROADCAST); + if (ret) + dev_err(swrm->dev, + "%s: set num ch failed\n", + __func__); + } + } else { + list_for_each_entry(swr_dev, &mstr->devices, + dev_list) { + ret = swr_set_device_group(swr_dev, + SWR_GROUP_NONE); + if (ret) + dev_err(swrm->dev, + "%s: set num ch failed\n", + __func__); + } + } + mutex_unlock(&swrm->mlock); + } + break; + default: + dev_err(swrm->dev, "%s: swr master unknown id %d\n", + __func__, id); + break; + } + return ret; +} +EXPORT_SYMBOL(swrm_wcd_notify); + +#ifdef CONFIG_PM_SLEEP +static int swrm_suspend(struct device *dev) +{ + int ret = -EBUSY; + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: system suspend, state: %d\n", __func__, swrm->state); + if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) { + ret = swrm_runtime_suspend(dev); + if (!ret) { + /* + * Synchronize runtime-pm and system-pm states: + * At this point, we are already suspended. If + * runtime-pm still thinks its active, then + * make sure its status is in sync with HW + * status. The three below calls let the + * runtime-pm know that we are suspended + * already without re-invoking the suspend + * callback + */ + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + } + } + if (ret == -EBUSY) { + /* + * There is a possibility that some audio stream is active + * during suspend. We dont want to return suspend failure in + * that case so that display and relevant components can still + * go to suspend. + * If there is some other error, then it should be passed-on + * to system level suspend + */ + ret = 0; + } + return ret; +} + +static int swrm_resume(struct device *dev) +{ + int ret = 0; + struct platform_device *pdev = to_platform_device(dev); + struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: system resume, state: %d\n", __func__, swrm->state); + if (!pm_runtime_enabled(dev) || !pm_runtime_suspend(dev)) { + ret = swrm_runtime_resume(dev); + if (!ret) { + pm_runtime_mark_last_busy(dev); + pm_request_autosuspend(dev); + } + } + return ret; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops swrm_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS( + swrm_suspend, + swrm_resume + ) + SET_RUNTIME_PM_OPS( + swrm_runtime_suspend, + swrm_runtime_resume, + NULL + ) +}; + +static const struct of_device_id swrm_dt_match[] = { + { + .compatible = "qcom,swr-wcd", + }, + {} +}; + +static struct platform_driver swr_mstr_driver = { + .probe = swrm_probe, + .remove = swrm_remove, + .driver = { + .name = SWR_WCD_NAME, + .owner = THIS_MODULE, + .pm = &swrm_dev_pm_ops, + .of_match_table = swrm_dt_match, + }, +}; + +static int __init swrm_init(void) +{ + return platform_driver_register(&swr_mstr_driver); +} +subsys_initcall(swrm_init); + +static void __exit swrm_exit(void) +{ + platform_driver_unregister(&swr_mstr_driver); +} +module_exit(swrm_exit); + + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("WCD SoundWire Controller"); +MODULE_ALIAS("platform:swr-wcd"); diff --git a/drivers/soundwire/swr-wcd-ctrl.h b/drivers/soundwire/swr-wcd-ctrl.h new file mode 100644 index 000000000000..b7a3edac3e00 --- /dev/null +++ b/drivers/soundwire/swr-wcd-ctrl.h @@ -0,0 +1,106 @@ +/* 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 _SWR_WCD_CTRL_H +#define _SWR_WCD_CTRL_H +#include +#include + +#define SWR_MAX_ROW 0 /* Rows = 48 */ +#define SWR_MAX_COL 7 /* Cols = 16 */ +#define SWR_MIN_COL 0 /* Cols = 2 */ + +#define SWR_WCD_NAME "swr-wcd" + +#define SWR_MSTR_PORT_LEN 8 /* Number of master ports */ + +#define SWRM_VERSION_1_0 0x01010000 +#define SWRM_VERSION_1_2 0x01030000 +#define SWRM_VERSION_1_3 0x01040000 + +enum { + SWR_MSTR_PAUSE, + SWR_MSTR_RESUME, + SWR_MSTR_UP, + SWR_MSTR_DOWN, +}; + +enum { + SWR_IRQ_FREE, + SWR_IRQ_REGISTER, +}; + +enum { + SWR_DAC_PORT, + SWR_COMP_PORT, + SWR_BOOST_PORT, + SWR_VISENSE_PORT, +}; + +struct usecase { + u8 num_port; + u8 num_ch; + u32 chrate; +}; + +struct port_params { + u8 si; + u8 off1; + u8 off2; +}; + +struct swrm_mports { + struct list_head list; + u8 id; +}; + +struct swr_ctrl_platform_data { + void *handle; /* holds priv data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq, + void *data), void *swr_handle, int type); +}; + +struct swr_mstr_ctrl { + struct swr_master master; + struct device *dev; + struct resource *supplies; + struct clk *mclk; + struct completion reset; + struct completion broadcast; + struct mutex mlock; + struct mutex reslock; + u8 rcmd_id; + u8 wcmd_id; + void *handle; /* SWR Master handle from client for read and writes */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq, + void *data), void *swr_handle, int type); + int irq; + int version; + int num_enum_slaves; + int slave_status; + struct swr_mstr_port *mstr_port; + struct list_head mport_list; + int state; + struct platform_device *pdev; + int num_rx_chs; + u8 num_cfg_devs; +}; + +#endif /* _SWR_WCD_CTRL_H */ diff --git a/drivers/soundwire/swrm_registers.h b/drivers/soundwire/swrm_registers.h new file mode 100644 index 000000000000..50c3ecfdd47d --- /dev/null +++ b/drivers/soundwire/swrm_registers.h @@ -0,0 +1,204 @@ +/* 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 _SWRM_REGISTERS_H +#define _SWRM_REGISTERS_H + +#define SWRM_BASE_ADDRESS 0x00 + +#define SWRM_COMP_HW_VERSION SWRM_BASE_ADDRESS +#define SWRM_COMP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000004) +#define SWRM_COMP_CFG_RMSK 0x3 +#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_BMSK 0x2 +#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_SHFT 0x1 +#define SWRM_COMP_CFG_ENABLE_BMSK 0x1 +#define SWRM_COMP_CFG_ENABLE_SHFT 0x0 + +#define SWRM_COMP_SW_RESET (SWRM_BASE_ADDRESS+0x00000008) + +#define SWRM_COMP_PARAMS (SWRM_BASE_ADDRESS+0x100) +#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK 0x0000001F +#define SWRM_COMP_PARAMS_DIN_PORTS_MASK 0x000003E0 +#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH 0x00007C00 +#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH 0x000F8000 +#define SWRM_COMP_PARAMS_AUTO_ENUM_SLAVES 0x00F00000 +#define SWRM_COMP_PARAMS_DATA_LANES 0x07000000 + + +#define SWRM_INTERRUPT_STATUS (SWRM_BASE_ADDRESS+0x00000200) +#define SWRM_INTERRUPT_STATUS_RMSK 0x1FFFD + +#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ 0x1 +#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED 0x2 +#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS 0x4 +#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET 0x8 +#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW 0x10 +#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW 0x20 +#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW 0x40 +#define SWRM_INTERRUPT_STATUS_CMD_ERROR 0x80 +#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION 0x100 +#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH 0x200 +#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED 0x400 +#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED 0x800 +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED 0x1000 +#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL 0x2000 +#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED 0x4000 +#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED 0x8000 +#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST 0x10000 + +#define SWRM_INTERRUPT_MASK_ADDR (SWRM_BASE_ADDRESS+0x00000204) +#define SWRM_INTERRUPT_MASK_RMSK 0x1FFFF + +#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_BMSK 0x1 +#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_SHFT 0x0 + +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_BMSK 0x2 +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_SHFT 0x1 + +#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_BMSK 0x4 +#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_SHFT 0x2 + +#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_BMSK 0x8 +#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_SHFT 0x3 + +#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_BMSK 0x10 +#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_SHFT 0x4 + +#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_BMSK 0x20 +#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_SHFT 0x5 + +#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_BMSK 0x40 +#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_SHFT 0x6 + +#define SWRM_INTERRUPT_MASK_CMD_ERROR_BMSK 0x80 +#define SWRM_INTERRUPT_MASK_CMD_ERROR_SHFT 0x7 + +#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_BMSK 0x100 +#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_SHFT 0x8 + +#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_BMSK 0x200 +#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_SHFT 0x9 + +#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_BMSK 0x400 +#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_SHFT 0xA + +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_BMSK 0x800 +#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_SHFT 0xB + +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_BMSK 0x1000 +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_SHFT 0xC + +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_BMSK 0x2000 +#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_SHFT 0xD + +#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_BMSK 0x4000 +#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_SHFT 0xE + +#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_BMSK 0x8000 +#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_SHFT 0xF + +#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_BMSK 0x10000 +#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_SHFT 0x10 + +#define SWRM_INTERRUPT_MAX 0x11 + +#define SWRM_INTERRUPT_CLEAR (SWRM_BASE_ADDRESS+0x00000208) + +#define SWRM_CMD_FIFO_WR_CMD (SWRM_BASE_ADDRESS + 0x00000300) +#define SWRM_CMD_FIFO_WR_CMD_MASK 0xFFFFFFFF +#define SWRM_CMD_FIFO_RD_CMD (SWRM_BASE_ADDRESS + 0x00000304) +#define SWRM_CMD_FIFO_RD_CMD_MASK 0xFFFFFFF +#define SWRM_CMD_FIFO_CMD (SWRM_BASE_ADDRESS + 0x00000308) +#define SWRM_CMD_FIFO_STATUS (SWRM_BASE_ADDRESS + 0x0000030C) + +#define SWRM_CMD_FIFO_STATUS_WR_CMD_FIFO_CNT_MASK 0x1F00 +#define SWRM_CMD_FIFO_STATUS_RD_CMD_FIFO_CNT_MASK 0x7C00000 + +#define SWRM_CMD_FIFO_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000314) +#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_BMSK 0x7 +#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT 0x0 + +#define SWRM_CMD_FIFO_RD_FIFO_ADDR (SWRM_BASE_ADDRESS + 0x00000318) + +#define SWRM_ENUMERATOR_CFG_ADDR (SWRM_BASE_ADDRESS+0x00000500) +#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_BMSK 0x1 +#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_SHFT 0x0 + +#define SWRM_ENUMERATOR_SLAVE_DEV_ID_1(m) (SWRM_BASE_ADDRESS+0x530+0x8*m) +#define SWRM_ENUMERATOR_SLAVE_DEV_ID_2(m) (SWRM_BASE_ADDRESS+0x534+0x8*m) + +#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m) (SWRM_BASE_ADDRESS+0x101C+0x40*m) +#define SWRM_MCP_FRAME_CTRL_BANK_RMSK 0x00ff07ff +#define SWRM_MCP_FRAME_CTRL_BANK_SHFT 0 +#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK 0xff0000 +#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT 16 +#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_BMSK 0xf800 +#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_SHFT 11 +#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_BMSK 0x700 +#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_SHFT 8 +#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK 0xF8 +#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT 3 +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK 0x7 +#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT 0 + +#define SWRM_MCP_BUS_CTRL_ADDR (SWRM_BASE_ADDRESS+0x00001044) +#define SWRM_MCP_BUS_CTRL_BUS_RESET_BMSK 0x1 +#define SWRM_MCP_BUS_CTRL_BUS_RESET_SHFT 0x0 +#define SWRM_MCP_BUS_CTRL_CLK_START_BMSK 0x2 +#define SWRM_MCP_BUS_CTRL_CLK_START_SHFT 0x1 + +#define SWRM_MCP_CFG_ADDR (SWRM_BASE_ADDRESS+0x00001048) +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK 0x3E0000 +#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT 0x11 +#define SWRM_MCP_CFG_BUS_CLK_PAUSE_BMSK 0x02 + +#define SWRM_MCP_STATUS (SWRM_BASE_ADDRESS+0x104C) +#define SWRM_MCP_STATUS_BANK_NUM_MASK 0x01 + +#define SWRM_MCP_SLV_STATUS (SWRM_BASE_ADDRESS+0x1090) +#define SWRM_MCP_SLV_STATUS_MASK 0x03 + +#define SWRM_DP_PORT_CTRL_BANK(n, m) (SWRM_BASE_ADDRESS + \ + 0x00001124 + \ + 0x100*(n-1) + \ + 0x40*m) +#define SWRM_DP_PORT_CTRL_BANK_MASK 0xFFFFFFFF +#define SWRM_DP_PORT_CTRL_EN_CHAN_MASK 0xFF000000 +#define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT 0x18 +#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT 0x10 +#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT 0x08 +#define SWRM_DP_PORT_CTRL_SAMPLE_INTERVAL 0x00 + +/* Soundwire Slave Register definition */ + +#define SWRS_BASE_ADDRESS 0x00 + +#define SWRS_DP_REG_OFFSET(port, bank) ((0x100*port)+(0x10*bank)) + +#define SWRS_DP_CHANNEL_ENABLE_BANK(n, m) (SWRS_BASE_ADDRESS + 0x120 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_SAMPLE_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x122 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_OFFSET_CONTROL_1_BANK(n, m) (SWRS_BASE_ADDRESS + 0x124 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_OFFSET_CONTROL_2_BANK(n, m) (SWRS_BASE_ADDRESS + 0x125 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_HCONTROL_BANK(n, m) (SWRS_BASE_ADDRESS + 0x126 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_DP_BLOCK_CONTROL_3_BANK(n, m) (SWRS_BASE_ADDRESS + 0x127 + \ + SWRS_DP_REG_OFFSET(n, m)) +#define SWRS_SCP_FRAME_CTRL_BANK(m) (SWRS_BASE_ADDRESS + 0x60 + \ + 0x10*m) +#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m) (SWRS_BASE_ADDRESS + 0xE0 + \ + 0x10*m) + +#endif /* _SWRM_REGISTERS_H */ diff --git a/include/Kbuild b/include/Kbuild new file mode 100644 index 000000000000..bab1145bc7a7 --- /dev/null +++ b/include/Kbuild @@ -0,0 +1,2 @@ +# Top-level Makefile calls into asm-$(ARCH) +# List only non-arch directories below diff --git a/include/linux/avtimer_kernel.h b/include/linux/avtimer_kernel.h new file mode 100644 index 000000000000..e5a12559f1ba --- /dev/null +++ b/include/linux/avtimer_kernel.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 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 _AVTIMER_H +#define _AVTIMER_H + +#include + +int avcs_core_open(void); +int avcs_core_disable_power_collapse(int disable);/* true or flase */ +int avcs_core_query_timer(uint64_t *avtimer_tick); + +#endif diff --git a/include/linux/mfd/msm-cdc-pinctrl.h b/include/linux/mfd/msm-cdc-pinctrl.h new file mode 100644 index 000000000000..7eabefb80e19 --- /dev/null +++ b/include/linux/mfd/msm-cdc-pinctrl.h @@ -0,0 +1,49 @@ +/* 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 __MFD_CDC_PINCTRL_H_ +#define __MFD_CDC_PINCTRL_H_ + +#include +#include + +#if IS_ENABLED(CONFIG_MSM_CDC_PINCTRL) +extern int msm_cdc_pinctrl_select_sleep_state(struct device_node *np); +extern int msm_cdc_pinctrl_select_active_state(struct device_node *np); +extern bool msm_cdc_pinctrl_get_state(struct device_node *np); +extern int msm_cdc_get_gpio_state(struct device_node *np); +int msm_cdc_pinctrl_drv_init(void); +void msm_cdc_pinctrl_drv_exit(void); + +#else +int msm_cdc_pinctrl_select_sleep_state(struct device_node *np) +{ + return 0; +} +int msm_cdc_pinctrl_select_active_state(struct device_node *np) +{ + return 0; +} +int msm_cdc_get_gpio_state(struct device_node *np) +{ + return 0; +} +int msm_cdc_pinctrl_drv_init(void) +{ + return 0; +} +void msm_cdc_pinctrl_drv_exit(void) +{ +} +#endif + +#endif diff --git a/include/linux/mfd/msm-cdc-supply.h b/include/linux/mfd/msm-cdc-supply.h new file mode 100644 index 000000000000..b40f44b1f12f --- /dev/null +++ b/include/linux/mfd/msm-cdc-supply.h @@ -0,0 +1,48 @@ +/* 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 __CODEC_POWER_SUPPLY_H__ +#define __CODEC_POWER_SUPPLY_H__ + +#include +#include +#include + +struct cdc_regulator { + const char *name; + int min_uV; + int max_uV; + int optimum_uA; + bool ondemand; + struct regulator *regulator; +}; + +extern int msm_cdc_get_power_supplies(struct device *dev, + struct cdc_regulator **cdc_vreg, + int *total_num_supplies); +extern int msm_cdc_disable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +extern int msm_cdc_release_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +extern int msm_cdc_enable_static_supplies(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +extern int msm_cdc_init_supplies(struct device *dev, + struct regulator_bulk_data **supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies); +#endif diff --git a/include/linux/mfd/wcd9335/irq.h b/include/linux/mfd/wcd9335/irq.h new file mode 100644 index 000000000000..c666d3144359 --- /dev/null +++ b/include/linux/mfd/wcd9335/irq.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 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 __WCD9335_IRQ_H_ +#define __WCD9335_IRQ_H_ + +enum { + /* INTR_REG 0 */ + WCD9335_IRQ_FLL_LOCK_LOSS = 1, + WCD9335_IRQ_HPH_PA_OCPL_FAULT, + WCD9335_IRQ_HPH_PA_OCPR_FAULT, + WCD9335_IRQ_EAR_PA_OCP_FAULT, + WCD9335_IRQ_HPH_PA_CNPL_COMPLETE, + WCD9335_IRQ_HPH_PA_CNPR_COMPLETE, + WCD9335_IRQ_EAR_PA_CNP_COMPLETE, + /* INTR_REG 1 */ + WCD9335_IRQ_MBHC_SW_DET, + WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, + WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, + WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD9335_IRQ_RESERVED_0, + WCD9335_IRQ_RESERVED_1, + WCD9335_IRQ_RESERVED_2, + /* INTR_REG 2 */ + WCD9335_IRQ_LINE_PA1_CNP_COMPLETE, + WCD9335_IRQ_LINE_PA2_CNP_COMPLETE, + WCD9335_IRQ_LINE_PA3_CNP_COMPLETE, + WCD9335_IRQ_LINE_PA4_CNP_COMPLETE, + WCD9335_IRQ_SOUNDWIRE, + WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE, + WCD9335_IRQ_RCO_ERROR, + WCD9335_IRQ_SVA_ERROR, + /* INTR_REG 3 */ + WCD9335_IRQ_MAD_AUDIO, + WCD9335_IRQ_MAD_BEACON, + WCD9335_IRQ_MAD_ULTRASOUND, + WCD9335_IRQ_VBAT_ATTACK, + WCD9335_IRQ_VBAT_RESTORE, + WCD9335_IRQ_SVA_OUTBOX1, + WCD9335_IRQ_SVA_OUTBOX2, + WCD9335_NUM_IRQS, +}; + +#endif diff --git a/include/linux/mfd/wcd9335/registers.h b/include/linux/mfd/wcd9335/registers.h new file mode 100644 index 000000000000..c50430d4278f --- /dev/null +++ b/include/linux/mfd/wcd9335/registers.h @@ -0,0 +1,1348 @@ +/* + * Copyright (c) 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 _WCD9335_REGISTERS_H +#define _WCD9335_REGISTERS_H + +#define WCD9335_PAGE_SIZE 256 +#define WCD9335_NUM_PAGES 256 + +extern const u8 *wcd9335_reg[WCD9335_NUM_PAGES]; + +enum { + PAGE_0 = 0, + PAGE_1, + PAGE_2, + PAGE_6 = 6, + PAGE_10 = 0xA, + PAGE_11, + PAGE_12, + PAGE_13, + PAGE_0X80, +}; + +/* Page-0 Registers */ +#define WCD9335_PAGE0_PAGE_REGISTER 0x0000 +#define WCD9335_CODEC_RPM_CLK_BYPASS 0x0001 +#define WCD9335_CODEC_RPM_CLK_GATE 0x0002 +#define WCD9335_CODEC_RPM_CLK_MCLK_CFG 0x0003 +#define WCD9335_CODEC_RPM_RST_CTL 0x0009 +#define WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL 0x0011 +#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1 0x0012 +#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2 0x0013 +#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3 0x0014 +#define WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN 0x0015 +#define WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN 0x0016 +#define WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1 0x0017 +#define WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2 0x0018 +#define WCD9335_CODEC_RPM_INT_MASK 0x001d +#define WCD9335_CODEC_RPM_INT_STATUS 0x001e +#define WCD9335_CODEC_RPM_INT_CLEAR 0x001f +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0 0x0021 +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1 0x0022 +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2 0x0023 +#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3 0x0024 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_CTL 0x0025 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0 0x0026 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1 0x0027 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0 0x0029 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 0x002a +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 0x002b +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3 0x002c +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4 0x002d +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5 0x002e +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6 0x002f +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7 0x0030 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8 0x0031 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9 0x0032 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10 0x0033 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11 0x0034 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12 0x0035 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13 0x0036 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14 0x0037 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15 0x0038 +#define WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS 0x0039 +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO 0x003a +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1 0x003b +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2 0x003c +#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3 0x003d +#define WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL 0x003e +#define WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE 0x003f +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL 0x0041 +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS 0x0042 +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB 0x0043 +#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB 0x0044 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL 0x0045 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS 0x0046 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB 0x0047 +#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB 0x0048 +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL 0x0049 +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS 0x004a +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB 0x004b +#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB 0x004c +#define WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL 0x0051 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL 0x0052 +#define WCD9335_DATA_HUB_DATA_HUB_I2S_CLK 0x0053 +#define WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG 0x0054 +#define WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG 0x0055 +#define WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG 0x0056 +#define WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG 0x0057 +#define WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG 0x0058 +#define WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG 0x0059 +#define WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG 0x005a +#define WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG 0x005b +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG 0x0061 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG 0x0062 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG 0x0063 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG 0x0064 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG 0x0065 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG 0x0066 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG 0x0067 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG 0x0068 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG 0x0069 +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG 0x006a +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG 0x006b +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG 0x006c +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG 0x006e +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG 0x006f +#define WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG 0x0070 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG 0x0071 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG 0x0072 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG 0x0073 +#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG 0x0074 +#define WCD9335_DATA_HUB_NATIVE_FIFO_SYNC 0x0075 +#define WCD9335_DATA_HUB_NATIVE_FIFO_STATUS 0x007D +#define WCD9335_INTR_CFG 0x0081 +#define WCD9335_INTR_CLR_COMMIT 0x0082 +#define WCD9335_INTR_PIN1_MASK0 0x0089 +#define WCD9335_INTR_PIN1_MASK1 0x008a +#define WCD9335_INTR_PIN1_MASK2 0x008b +#define WCD9335_INTR_PIN1_MASK3 0x008c +#define WCD9335_INTR_PIN1_STATUS0 0x0091 +#define WCD9335_INTR_PIN1_STATUS1 0x0092 +#define WCD9335_INTR_PIN1_STATUS2 0x0093 +#define WCD9335_INTR_PIN1_STATUS3 0x0094 +#define WCD9335_INTR_PIN1_CLEAR0 0x0099 +#define WCD9335_INTR_PIN1_CLEAR1 0x009a +#define WCD9335_INTR_PIN1_CLEAR2 0x009b +#define WCD9335_INTR_PIN1_CLEAR3 0x009c +#define WCD9335_INTR_PIN2_MASK0 0x00a1 +#define WCD9335_INTR_PIN2_MASK1 0x00a2 +#define WCD9335_INTR_PIN2_MASK2 0x00a3 +#define WCD9335_INTR_PIN2_MASK3 0x00a4 +#define WCD9335_INTR_PIN2_STATUS0 0x00a9 +#define WCD9335_INTR_PIN2_STATUS1 0x00aa +#define WCD9335_INTR_PIN2_STATUS2 0x00ab +#define WCD9335_INTR_PIN2_STATUS3 0x00ac +#define WCD9335_INTR_PIN2_CLEAR0 0x00b1 +#define WCD9335_INTR_PIN2_CLEAR1 0x00b2 +#define WCD9335_INTR_PIN2_CLEAR2 0x00b3 +#define WCD9335_INTR_PIN2_CLEAR3 0x00b4 +#define WCD9335_INTR_LEVEL0 0x00e1 +#define WCD9335_INTR_LEVEL1 0x00e2 +#define WCD9335_INTR_LEVEL2 0x00e3 +#define WCD9335_INTR_LEVEL3 0x00e4 +#define WCD9335_INTR_BYPASS0 0x00e9 +#define WCD9335_INTR_BYPASS1 0x00ea +#define WCD9335_INTR_BYPASS2 0x00eb +#define WCD9335_INTR_BYPASS3 0x00ec +#define WCD9335_INTR_SET0 0x00f1 +#define WCD9335_INTR_SET1 0x00f2 +#define WCD9335_INTR_SET2 0x00f3 +#define WCD9335_INTR_SET3 0x00f4 + +/* Page-1 Registers */ +#define WCD9335_PAGE1_PAGE_REGISTER 0x0100 +#define WCD9335_CPE_FLL_USER_CTL_0 0x0101 +#define WCD9335_CPE_FLL_USER_CTL_1 0x0102 +#define WCD9335_CPE_FLL_USER_CTL_2 0x0103 +#define WCD9335_CPE_FLL_USER_CTL_3 0x0104 +#define WCD9335_CPE_FLL_USER_CTL_4 0x0105 +#define WCD9335_CPE_FLL_USER_CTL_5 0x0106 +#define WCD9335_CPE_FLL_USER_CTL_6 0x0107 +#define WCD9335_CPE_FLL_USER_CTL_7 0x0108 +#define WCD9335_CPE_FLL_USER_CTL_8 0x0109 +#define WCD9335_CPE_FLL_USER_CTL_9 0x010a +#define WCD9335_CPE_FLL_L_VAL_CTL_0 0x010b +#define WCD9335_CPE_FLL_L_VAL_CTL_1 0x010c +#define WCD9335_CPE_FLL_DSM_FRAC_CTL_0 0x010d +#define WCD9335_CPE_FLL_DSM_FRAC_CTL_1 0x010e +#define WCD9335_CPE_FLL_CONFIG_CTL_0 0x010f +#define WCD9335_CPE_FLL_CONFIG_CTL_1 0x0110 +#define WCD9335_CPE_FLL_CONFIG_CTL_2 0x0111 +#define WCD9335_CPE_FLL_CONFIG_CTL_3 0x0112 +#define WCD9335_CPE_FLL_CONFIG_CTL_4 0x0113 +#define WCD9335_CPE_FLL_TEST_CTL_0 0x0114 +#define WCD9335_CPE_FLL_TEST_CTL_1 0x0115 +#define WCD9335_CPE_FLL_TEST_CTL_2 0x0116 +#define WCD9335_CPE_FLL_TEST_CTL_3 0x0117 +#define WCD9335_CPE_FLL_TEST_CTL_4 0x0118 +#define WCD9335_CPE_FLL_TEST_CTL_5 0x0119 +#define WCD9335_CPE_FLL_TEST_CTL_6 0x011a +#define WCD9335_CPE_FLL_TEST_CTL_7 0x011b +#define WCD9335_CPE_FLL_FREQ_CTL_0 0x011c +#define WCD9335_CPE_FLL_FREQ_CTL_1 0x011d +#define WCD9335_CPE_FLL_FREQ_CTL_2 0x011e +#define WCD9335_CPE_FLL_FREQ_CTL_3 0x011f +#define WCD9335_CPE_FLL_SSC_CTL_0 0x0120 +#define WCD9335_CPE_FLL_SSC_CTL_1 0x0121 +#define WCD9335_CPE_FLL_SSC_CTL_2 0x0122 +#define WCD9335_CPE_FLL_SSC_CTL_3 0x0123 +#define WCD9335_CPE_FLL_FLL_MODE 0x0124 +#define WCD9335_CPE_FLL_STATUS_0 0x0125 +#define WCD9335_CPE_FLL_STATUS_1 0x0126 +#define WCD9335_CPE_FLL_STATUS_2 0x0127 +#define WCD9335_CPE_FLL_STATUS_3 0x0128 +#define WCD9335_I2S_FLL_USER_CTL_0 0x0141 +#define WCD9335_I2S_FLL_USER_CTL_1 0x0142 +#define WCD9335_I2S_FLL_USER_CTL_2 0x0143 +#define WCD9335_I2S_FLL_USER_CTL_3 0x0144 +#define WCD9335_I2S_FLL_USER_CTL_4 0x0145 +#define WCD9335_I2S_FLL_USER_CTL_5 0x0146 +#define WCD9335_I2S_FLL_USER_CTL_6 0x0147 +#define WCD9335_I2S_FLL_USER_CTL_7 0x0148 +#define WCD9335_I2S_FLL_USER_CTL_8 0x0149 +#define WCD9335_I2S_FLL_USER_CTL_9 0x014a +#define WCD9335_I2S_FLL_L_VAL_CTL_0 0x014b +#define WCD9335_I2S_FLL_L_VAL_CTL_1 0x014c +#define WCD9335_I2S_FLL_DSM_FRAC_CTL_0 0x014d +#define WCD9335_I2S_FLL_DSM_FRAC_CTL_1 0x014e +#define WCD9335_I2S_FLL_CONFIG_CTL_0 0x014f +#define WCD9335_I2S_FLL_CONFIG_CTL_1 0x0150 +#define WCD9335_I2S_FLL_CONFIG_CTL_2 0x0151 +#define WCD9335_I2S_FLL_CONFIG_CTL_3 0x0152 +#define WCD9335_I2S_FLL_CONFIG_CTL_4 0x0153 +#define WCD9335_I2S_FLL_TEST_CTL_0 0x0154 +#define WCD9335_I2S_FLL_TEST_CTL_1 0x0155 +#define WCD9335_I2S_FLL_TEST_CTL_2 0x0156 +#define WCD9335_I2S_FLL_TEST_CTL_3 0x0157 +#define WCD9335_I2S_FLL_TEST_CTL_4 0x0158 +#define WCD9335_I2S_FLL_TEST_CTL_5 0x0159 +#define WCD9335_I2S_FLL_TEST_CTL_6 0x015a +#define WCD9335_I2S_FLL_TEST_CTL_7 0x015b +#define WCD9335_I2S_FLL_FREQ_CTL_0 0x015c +#define WCD9335_I2S_FLL_FREQ_CTL_1 0x015d +#define WCD9335_I2S_FLL_FREQ_CTL_2 0x015e +#define WCD9335_I2S_FLL_FREQ_CTL_3 0x015f +#define WCD9335_I2S_FLL_SSC_CTL_0 0x0160 +#define WCD9335_I2S_FLL_SSC_CTL_1 0x0161 +#define WCD9335_I2S_FLL_SSC_CTL_2 0x0162 +#define WCD9335_I2S_FLL_SSC_CTL_3 0x0163 +#define WCD9335_I2S_FLL_FLL_MODE 0x0164 +#define WCD9335_I2S_FLL_STATUS_0 0x0165 +#define WCD9335_I2S_FLL_STATUS_1 0x0166 +#define WCD9335_I2S_FLL_STATUS_2 0x0167 +#define WCD9335_I2S_FLL_STATUS_3 0x0168 +#define WCD9335_SB_FLL_USER_CTL_0 0x0181 +#define WCD9335_SB_FLL_USER_CTL_1 0x0182 +#define WCD9335_SB_FLL_USER_CTL_2 0x0183 +#define WCD9335_SB_FLL_USER_CTL_3 0x0184 +#define WCD9335_SB_FLL_USER_CTL_4 0x0185 +#define WCD9335_SB_FLL_USER_CTL_5 0x0186 +#define WCD9335_SB_FLL_USER_CTL_6 0x0187 +#define WCD9335_SB_FLL_USER_CTL_7 0x0188 +#define WCD9335_SB_FLL_USER_CTL_8 0x0189 +#define WCD9335_SB_FLL_USER_CTL_9 0x018a +#define WCD9335_SB_FLL_L_VAL_CTL_0 0x018b +#define WCD9335_SB_FLL_L_VAL_CTL_1 0x018c +#define WCD9335_SB_FLL_DSM_FRAC_CTL_0 0x018d +#define WCD9335_SB_FLL_DSM_FRAC_CTL_1 0x018e +#define WCD9335_SB_FLL_CONFIG_CTL_0 0x018f +#define WCD9335_SB_FLL_CONFIG_CTL_1 0x0190 +#define WCD9335_SB_FLL_CONFIG_CTL_2 0x0191 +#define WCD9335_SB_FLL_CONFIG_CTL_3 0x0192 +#define WCD9335_SB_FLL_CONFIG_CTL_4 0x0193 +#define WCD9335_SB_FLL_TEST_CTL_0 0x0194 +#define WCD9335_SB_FLL_TEST_CTL_1 0x0195 +#define WCD9335_SB_FLL_TEST_CTL_2 0x0196 +#define WCD9335_SB_FLL_TEST_CTL_3 0x0197 +#define WCD9335_SB_FLL_TEST_CTL_4 0x0198 +#define WCD9335_SB_FLL_TEST_CTL_5 0x0199 +#define WCD9335_SB_FLL_TEST_CTL_6 0x019a +#define WCD9335_SB_FLL_TEST_CTL_7 0x019b +#define WCD9335_SB_FLL_FREQ_CTL_0 0x019c +#define WCD9335_SB_FLL_FREQ_CTL_1 0x019d +#define WCD9335_SB_FLL_FREQ_CTL_2 0x019e +#define WCD9335_SB_FLL_FREQ_CTL_3 0x019f +#define WCD9335_SB_FLL_SSC_CTL_0 0x01a0 +#define WCD9335_SB_FLL_SSC_CTL_1 0x01a1 +#define WCD9335_SB_FLL_SSC_CTL_2 0x01a2 +#define WCD9335_SB_FLL_SSC_CTL_3 0x01a3 +#define WCD9335_SB_FLL_FLL_MODE 0x01a4 +#define WCD9335_SB_FLL_STATUS_0 0x01a5 +#define WCD9335_SB_FLL_STATUS_1 0x01a6 +#define WCD9335_SB_FLL_STATUS_2 0x01a7 +#define WCD9335_SB_FLL_STATUS_3 0x01a8 + +/* Page-2 Registers */ +#define WCD9335_PAGE2_PAGE_REGISTER 0x0200 +#define WCD9335_CPE_SS_MEM_PTR_0 0x0201 +#define WCD9335_CPE_SS_MEM_PTR_1 0x0202 +#define WCD9335_CPE_SS_MEM_PTR_2 0x0203 +#define WCD9335_CPE_SS_MEM_CTRL 0x0205 +#define WCD9335_CPE_SS_MEM_BANK_0 0x0206 +#define WCD9335_CPE_SS_MEM_BANK_1 0x0207 +#define WCD9335_CPE_SS_MEM_BANK_2 0x0208 +#define WCD9335_CPE_SS_MEM_BANK_3 0x0209 +#define WCD9335_CPE_SS_MEM_BANK_4 0x020a +#define WCD9335_CPE_SS_MEM_BANK_5 0x020b +#define WCD9335_CPE_SS_MEM_BANK_6 0x020c +#define WCD9335_CPE_SS_MEM_BANK_7 0x020d +#define WCD9335_CPE_SS_MEM_BANK_8 0x020e +#define WCD9335_CPE_SS_MEM_BANK_9 0x020f +#define WCD9335_CPE_SS_MEM_BANK_10 0x0210 +#define WCD9335_CPE_SS_MEM_BANK_11 0x0211 +#define WCD9335_CPE_SS_MEM_BANK_12 0x0212 +#define WCD9335_CPE_SS_MEM_BANK_13 0x0213 +#define WCD9335_CPE_SS_MEM_BANK_14 0x0214 +#define WCD9335_CPE_SS_MEM_BANK_15 0x0215 +#define WCD9335_CPE_SS_INBOX1_TRG 0x0216 +#define WCD9335_CPE_SS_INBOX2_TRG 0x0217 +#define WCD9335_CPE_SS_INBOX1_0 0x0218 +#define WCD9335_CPE_SS_INBOX1_1 0x0219 +#define WCD9335_CPE_SS_INBOX1_2 0x021a +#define WCD9335_CPE_SS_INBOX1_3 0x021b +#define WCD9335_CPE_SS_INBOX1_4 0x021c +#define WCD9335_CPE_SS_INBOX1_5 0x021d +#define WCD9335_CPE_SS_INBOX1_6 0x021e +#define WCD9335_CPE_SS_INBOX1_7 0x021f +#define WCD9335_CPE_SS_INBOX1_8 0x0220 +#define WCD9335_CPE_SS_INBOX1_9 0x0221 +#define WCD9335_CPE_SS_INBOX1_10 0x0222 +#define WCD9335_CPE_SS_INBOX1_11 0x0223 +#define WCD9335_CPE_SS_INBOX1_12 0x0224 +#define WCD9335_CPE_SS_INBOX1_13 0x0225 +#define WCD9335_CPE_SS_INBOX1_14 0x0226 +#define WCD9335_CPE_SS_INBOX1_15 0x0227 +#define WCD9335_CPE_SS_OUTBOX1_0 0x0228 +#define WCD9335_CPE_SS_OUTBOX1_1 0x0229 +#define WCD9335_CPE_SS_OUTBOX1_2 0x022a +#define WCD9335_CPE_SS_OUTBOX1_3 0x022b +#define WCD9335_CPE_SS_OUTBOX1_4 0x022c +#define WCD9335_CPE_SS_OUTBOX1_5 0x022d +#define WCD9335_CPE_SS_OUTBOX1_6 0x022e +#define WCD9335_CPE_SS_OUTBOX1_7 0x022f +#define WCD9335_CPE_SS_OUTBOX1_8 0x0230 +#define WCD9335_CPE_SS_OUTBOX1_9 0x0231 +#define WCD9335_CPE_SS_OUTBOX1_10 0x0232 +#define WCD9335_CPE_SS_OUTBOX1_11 0x0233 +#define WCD9335_CPE_SS_OUTBOX1_12 0x0234 +#define WCD9335_CPE_SS_OUTBOX1_13 0x0235 +#define WCD9335_CPE_SS_OUTBOX1_14 0x0236 +#define WCD9335_CPE_SS_OUTBOX1_15 0x0237 +#define WCD9335_CPE_SS_INBOX2_0 0x0238 +#define WCD9335_CPE_SS_INBOX2_1 0x0239 +#define WCD9335_CPE_SS_INBOX2_2 0x023a +#define WCD9335_CPE_SS_INBOX2_3 0x023b +#define WCD9335_CPE_SS_INBOX2_4 0x023c +#define WCD9335_CPE_SS_INBOX2_5 0x023d +#define WCD9335_CPE_SS_INBOX2_6 0x023e +#define WCD9335_CPE_SS_INBOX2_7 0x023f +#define WCD9335_CPE_SS_INBOX2_8 0x0240 +#define WCD9335_CPE_SS_INBOX2_9 0x0241 +#define WCD9335_CPE_SS_INBOX2_10 0x0242 +#define WCD9335_CPE_SS_INBOX2_11 0x0243 +#define WCD9335_CPE_SS_INBOX2_12 0x0244 +#define WCD9335_CPE_SS_INBOX2_13 0x0245 +#define WCD9335_CPE_SS_INBOX2_14 0x0246 +#define WCD9335_CPE_SS_INBOX2_15 0x0247 +#define WCD9335_CPE_SS_OUTBOX2_0 0x0248 +#define WCD9335_CPE_SS_OUTBOX2_1 0x0249 +#define WCD9335_CPE_SS_OUTBOX2_2 0x024a +#define WCD9335_CPE_SS_OUTBOX2_3 0x024b +#define WCD9335_CPE_SS_OUTBOX2_4 0x024c +#define WCD9335_CPE_SS_OUTBOX2_5 0x024d +#define WCD9335_CPE_SS_OUTBOX2_6 0x024e +#define WCD9335_CPE_SS_OUTBOX2_7 0x024f +#define WCD9335_CPE_SS_OUTBOX2_8 0x0250 +#define WCD9335_CPE_SS_OUTBOX2_9 0x0251 +#define WCD9335_CPE_SS_OUTBOX2_10 0x0252 +#define WCD9335_CPE_SS_OUTBOX2_11 0x0253 +#define WCD9335_CPE_SS_OUTBOX2_12 0x0254 +#define WCD9335_CPE_SS_OUTBOX2_13 0x0255 +#define WCD9335_CPE_SS_OUTBOX2_14 0x0256 +#define WCD9335_CPE_SS_OUTBOX2_15 0x0257 +#define WCD9335_CPE_SS_OUTBOX1_ACK 0x0258 +#define WCD9335_CPE_SS_OUTBOX2_ACK 0x0259 +#define WCD9335_CPE_SS_EC_BUF_INT_PERIOD 0x025a +#define WCD9335_CPE_SS_US_BUF_INT_PERIOD 0x025b +#define WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD 0x025c +#define WCD9335_CPE_SS_CFG 0x025d +#define WCD9335_CPE_SS_US_EC_MUX_CFG 0x025e +#define WCD9335_CPE_SS_MAD_CTL 0x025f +#define WCD9335_CPE_SS_CPAR_CTL 0x0260 +#define WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD 0x0261 +#define WCD9335_CPE_SS_TX_PP_CFG 0x0262 +#define WCD9335_CPE_SS_DMIC0_CTL 0x0263 +#define WCD9335_CPE_SS_DMIC1_CTL 0x0264 +#define WCD9335_CPE_SS_DMIC2_CTL 0x0265 +#define WCD9335_CPE_SS_DMIC_CFG 0x0266 +#define WCD9335_CPE_SS_SVA_CFG 0x0267 +#define WCD9335_CPE_SS_CPAR_CFG 0x0271 +#define WCD9335_CPE_SS_WDOG_CFG 0x0272 +#define WCD9335_CPE_SS_BACKUP_INT 0x0273 +#define WCD9335_CPE_SS_STATUS 0x0274 +#define WCD9335_CPE_SS_CPE_OCD_CFG 0x0275 +#define WCD9335_CPE_SS_SS_ERROR_INT_MASK 0x0276 +#define WCD9335_CPE_SS_SS_ERROR_INT_STATUS 0x0277 +#define WCD9335_CPE_SS_SS_ERROR_INT_CLEAR 0x0278 +#define WCD9335_SOC_MAD_MAIN_CTL_1 0x0281 +#define WCD9335_SOC_MAD_MAIN_CTL_2 0x0282 +#define WCD9335_SOC_MAD_AUDIO_CTL_1 0x0283 +#define WCD9335_SOC_MAD_AUDIO_CTL_2 0x0284 +#define WCD9335_SOC_MAD_AUDIO_CTL_3 0x0285 +#define WCD9335_SOC_MAD_AUDIO_CTL_4 0x0286 +#define WCD9335_SOC_MAD_AUDIO_CTL_5 0x0287 +#define WCD9335_SOC_MAD_AUDIO_CTL_6 0x0288 +#define WCD9335_SOC_MAD_AUDIO_CTL_7 0x0289 +#define WCD9335_SOC_MAD_AUDIO_CTL_8 0x028a +#define WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR 0x028b +#define WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL 0x028c +#define WCD9335_SOC_MAD_ULTR_CTL_1 0x028d +#define WCD9335_SOC_MAD_ULTR_CTL_2 0x028e +#define WCD9335_SOC_MAD_ULTR_CTL_3 0x028f +#define WCD9335_SOC_MAD_ULTR_CTL_4 0x0290 +#define WCD9335_SOC_MAD_ULTR_CTL_5 0x0291 +#define WCD9335_SOC_MAD_ULTR_CTL_6 0x0292 +#define WCD9335_SOC_MAD_ULTR_CTL_7 0x0293 +#define WCD9335_SOC_MAD_BEACON_CTL_1 0x0294 +#define WCD9335_SOC_MAD_BEACON_CTL_2 0x0295 +#define WCD9335_SOC_MAD_BEACON_CTL_3 0x0296 +#define WCD9335_SOC_MAD_BEACON_CTL_4 0x0297 +#define WCD9335_SOC_MAD_BEACON_CTL_5 0x0298 +#define WCD9335_SOC_MAD_BEACON_CTL_6 0x0299 +#define WCD9335_SOC_MAD_BEACON_CTL_7 0x029a +#define WCD9335_SOC_MAD_BEACON_CTL_8 0x029b +#define WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR 0x029c +#define WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL 0x029d +#define WCD9335_SOC_MAD_INP_SEL 0x029e + +/* Page-6 Registers */ +#define WCD9335_PAGE6_PAGE_REGISTER 0x0600 +#define WCD9335_ANA_BIAS 0x0601 +#define WCD9335_ANA_CLK_TOP 0x0602 +#define WCD9335_ANA_RCO 0x0603 +#define WCD9335_ANA_BUCK_VOUT_A 0x0604 +#define WCD9335_ANA_BUCK_VOUT_D 0x0605 +#define WCD9335_ANA_BUCK_CTL 0x0606 +#define WCD9335_ANA_BUCK_STATUS 0x0607 +#define WCD9335_ANA_RX_SUPPLIES 0x0608 +#define WCD9335_ANA_HPH 0x0609 +#define WCD9335_ANA_EAR 0x060a +#define WCD9335_ANA_LO_1_2 0x060b +#define WCD9335_ANA_LO_3_4 0x060c +#define WCD9335_ANA_MAD_SETUP 0x060d +#define WCD9335_ANA_AMIC1 0x060e +#define WCD9335_ANA_AMIC2 0x060f +#define WCD9335_ANA_AMIC3 0x0610 +#define WCD9335_ANA_AMIC4 0x0611 +#define WCD9335_ANA_AMIC5 0x0612 +#define WCD9335_ANA_AMIC6 0x0613 +#define WCD9335_ANA_MBHC_MECH 0x0614 +#define WCD9335_ANA_MBHC_ELECT 0x0615 +#define WCD9335_ANA_MBHC_ZDET 0x0616 +#define WCD9335_ANA_MBHC_RESULT_1 0x0617 +#define WCD9335_ANA_MBHC_RESULT_2 0x0618 +#define WCD9335_ANA_MBHC_RESULT_3 0x0619 +#define WCD9335_ANA_MBHC_BTN0 0x061a +#define WCD9335_ANA_MBHC_BTN1 0x061b +#define WCD9335_ANA_MBHC_BTN2 0x061c +#define WCD9335_ANA_MBHC_BTN3 0x061d +#define WCD9335_ANA_MBHC_BTN4 0x061e +#define WCD9335_ANA_MBHC_BTN5 0x061f +#define WCD9335_ANA_MBHC_BTN6 0x0620 +#define WCD9335_ANA_MBHC_BTN7 0x0621 +#define WCD9335_ANA_MICB1 0x0622 +#define WCD9335_ANA_MICB2 0x0623 +#define WCD9335_ANA_MICB2_RAMP 0x0624 +#define WCD9335_ANA_MICB3 0x0625 +#define WCD9335_ANA_MICB4 0x0626 +#define WCD9335_ANA_VBADC 0x0627 +#define WCD9335_BIAS_CTL 0x0628 +#define WCD9335_BIAS_VBG_FINE_ADJ 0x0629 +#define WCD9335_CLOCK_TEST_CTL 0x062d +#define WCD9335_RCO_CTRL_1 0x062e +#define WCD9335_RCO_CTRL_2 0x062f +#define WCD9335_RCO_CAL 0x0630 +#define WCD9335_RCO_CAL_1 0x0631 +#define WCD9335_RCO_CAL_2 0x0632 +#define WCD9335_RCO_TEST_CTRL 0x0633 +#define WCD9335_RCO_CAL_OUT_1 0x0634 +#define WCD9335_RCO_CAL_OUT_2 0x0635 +#define WCD9335_RCO_CAL_OUT_3 0x0636 +#define WCD9335_RCO_CAL_OUT_4 0x0637 +#define WCD9335_RCO_CAL_OUT_5 0x0638 +#define WCD9335_SIDO_SIDO_MODE_1 0x063a +#define WCD9335_SIDO_SIDO_MODE_2 0x063b +#define WCD9335_SIDO_SIDO_MODE_3 0x063c +#define WCD9335_SIDO_SIDO_MODE_4 0x063d +#define WCD9335_SIDO_SIDO_VCL_1 0x063e +#define WCD9335_SIDO_SIDO_VCL_2 0x063f +#define WCD9335_SIDO_SIDO_VCL_3 0x0640 +#define WCD9335_SIDO_SIDO_CCL_1 0x0641 +#define WCD9335_SIDO_SIDO_CCL_2 0x0642 +#define WCD9335_SIDO_SIDO_CCL_3 0x0643 +#define WCD9335_SIDO_SIDO_CCL_4 0x0644 +#define WCD9335_SIDO_SIDO_CCL_5 0x0645 +#define WCD9335_SIDO_SIDO_CCL_6 0x0646 +#define WCD9335_SIDO_SIDO_CCL_7 0x0647 +#define WCD9335_SIDO_SIDO_CCL_8 0x0648 +#define WCD9335_SIDO_SIDO_CCL_9 0x0649 +#define WCD9335_SIDO_SIDO_CCL_10 0x064a +#define WCD9335_SIDO_SIDO_FILTER_1 0x064b +#define WCD9335_SIDO_SIDO_FILTER_2 0x064c +#define WCD9335_SIDO_SIDO_DRIVER_1 0x064d +#define WCD9335_SIDO_SIDO_DRIVER_2 0x064e +#define WCD9335_SIDO_SIDO_DRIVER_3 0x064f +#define WCD9335_SIDO_SIDO_CAL_CODE_EXT_1 0x0650 +#define WCD9335_SIDO_SIDO_CAL_CODE_EXT_2 0x0651 +#define WCD9335_SIDO_SIDO_CAL_CODE_OUT_1 0x0652 +#define WCD9335_SIDO_SIDO_CAL_CODE_OUT_2 0x0653 +#define WCD9335_SIDO_SIDO_TEST_1 0x0654 +#define WCD9335_SIDO_SIDO_TEST_2 0x0655 +#define WCD9335_MBHC_CTL_1 0x0656 +#define WCD9335_MBHC_CTL_2 0x0657 +#define WCD9335_MBHC_PLUG_DETECT_CTL 0x0658 +#define WCD9335_MBHC_ZDET_ANA_CTL 0x0659 +#define WCD9335_MBHC_ZDET_RAMP_CTL 0x065a +#define WCD9335_MBHC_FSM_DEBUG 0x065b /* v1.x */ +#define WCD9335_MBHC_FSM_STATUS 0x065b /* v2.0 */ +#define WCD9335_MBHC_TEST_CTL 0x065c +#define WCD9335_VBADC_SUBBLOCK_EN 0x065d +#define WCD9335_VBADC_IBIAS_FE 0x065e +#define WCD9335_VBADC_BIAS_ADC 0x065f +#define WCD9335_VBADC_FE_CTRL 0x0660 +#define WCD9335_VBADC_ADC_REF 0x0661 +#define WCD9335_VBADC_ADC_IO 0x0662 +#define WCD9335_VBADC_ADC_SAR 0x0663 +#define WCD9335_VBADC_DEBUG 0x0664 +#define WCD9335_VBADC_ADC_DOUTMSB 0x0665 +#define WCD9335_VBADC_ADC_DOUTLSB 0x0666 +#define WCD9335_LDOH_MODE 0x0667 +#define WCD9335_LDOH_BIAS 0x0668 +#define WCD9335_LDOH_STB_LOADS 0x0669 +#define WCD9335_LDOH_SLOWRAMP 0x066a +#define WCD9335_MICB1_TEST_CTL_1 0x066b +#define WCD9335_MICB1_TEST_CTL_2 0x066c +#define WCD9335_MICB1_TEST_CTL_3 0x066d +#define WCD9335_MICB2_TEST_CTL_1 0x066e +#define WCD9335_MICB2_TEST_CTL_2 0x066f +#define WCD9335_MICB2_TEST_CTL_3 0x0670 +#define WCD9335_MICB3_TEST_CTL_1 0x0671 +#define WCD9335_MICB3_TEST_CTL_2 0x0672 +#define WCD9335_MICB3_TEST_CTL_3 0x0673 +#define WCD9335_MICB4_TEST_CTL_1 0x0674 +#define WCD9335_MICB4_TEST_CTL_2 0x0675 +#define WCD9335_MICB4_TEST_CTL_3 0x0676 +#define WCD9335_TX_COM_ADC_VCM 0x0677 +#define WCD9335_TX_COM_BIAS_ATEST 0x0678 +#define WCD9335_TX_COM_ADC_INT1_IB 0x0679 +#define WCD9335_TX_COM_ADC_INT2_IB 0x067a +#define WCD9335_TX_COM_TXFE_DIV_CTL 0x067b +#define WCD9335_TX_COM_TXFE_DIV_START 0x067c +#define WCD9335_TX_COM_TXFE_DIV_STOP_9P6M 0x067d +#define WCD9335_TX_COM_TXFE_DIV_STOP_12P288M 0x067e +#define WCD9335_TX_1_2_TEST_EN 0x067f +#define WCD9335_TX_1_2_ADC_IB 0x0680 +#define WCD9335_TX_1_2_ATEST_REFCTL 0x0681 +#define WCD9335_TX_1_2_TEST_CTL 0x0682 +#define WCD9335_TX_1_2_TEST_BLK_EN 0x0683 +#define WCD9335_TX_1_2_TXFE_CLKDIV 0x0684 +#define WCD9335_TX_1_2_SAR1_ERR 0x0685 +#define WCD9335_TX_1_2_SAR2_ERR 0x0686 +#define WCD9335_TX_3_4_TEST_EN 0x0687 +#define WCD9335_TX_3_4_ADC_IB 0x0688 +#define WCD9335_TX_3_4_ATEST_REFCTL 0x0689 +#define WCD9335_TX_3_4_TEST_CTL 0x068a +#define WCD9335_TX_3_4_TEST_BLK_EN 0x068b +#define WCD9335_TX_3_4_TXFE_CLKDIV 0x068c +#define WCD9335_TX_3_4_SAR1_ERR 0x068d +#define WCD9335_TX_3_4_SAR2_ERR 0x068e +#define WCD9335_TX_5_6_TEST_EN 0x068f +#define WCD9335_TX_5_6_ADC_IB 0x0690 +#define WCD9335_TX_5_6_ATEST_REFCTL 0x0691 +#define WCD9335_TX_5_6_TEST_CTL 0x0692 +#define WCD9335_TX_5_6_TEST_BLK_EN 0x0693 +#define WCD9335_TX_5_6_TXFE_CLKDIV 0x0694 +#define WCD9335_TX_5_6_SAR1_ERR 0x0695 +#define WCD9335_TX_5_6_SAR2_ERR 0x0696 +#define WCD9335_CLASSH_MODE_1 0x0697 +#define WCD9335_CLASSH_MODE_2 0x0698 +#define WCD9335_CLASSH_MODE_3 0x0699 +#define WCD9335_CLASSH_CTRL_VCL_1 0x069a +#define WCD9335_CLASSH_CTRL_VCL_2 0x069b +#define WCD9335_CLASSH_CTRL_CCL_1 0x069c +#define WCD9335_CLASSH_CTRL_CCL_2 0x069d +#define WCD9335_CLASSH_CTRL_CCL_3 0x069e +#define WCD9335_CLASSH_CTRL_CCL_4 0x069f +#define WCD9335_CLASSH_CTRL_CCL_5 0x06a0 +#define WCD9335_CLASSH_BUCK_TMUX_A_D 0x06a1 +#define WCD9335_CLASSH_BUCK_SW_DRV_CNTL 0x06a2 +#define WCD9335_CLASSH_SPARE 0x06a3 +#define WCD9335_FLYBACK_EN 0x06a4 +#define WCD9335_FLYBACK_VNEG_CTRL_1 0x06a5 +#define WCD9335_FLYBACK_VNEG_CTRL_2 0x06a6 +#define WCD9335_FLYBACK_VNEG_CTRL_3 0x06a7 +#define WCD9335_FLYBACK_VNEG_CTRL_4 0x06a8 +#define WCD9335_FLYBACK_VNEG_CTRL_5 0x06a9 +#define WCD9335_FLYBACK_VNEG_CTRL_6 0x06aa +#define WCD9335_FLYBACK_VNEG_CTRL_7 0x06ab +#define WCD9335_FLYBACK_VNEG_CTRL_8 0x06ac +#define WCD9335_FLYBACK_VNEG_CTRL_9 0x06ad +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_1 0x06ae +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_2 0x06af +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_3 0x06b0 +#define WCD9335_FLYBACK_VNEG_DAC_CTRL_4 0x06b1 /* v1.x */ +#define WCD9335_FLYBACK_CTRL_1 0x06b1 /* v2.0 */ +#define WCD9335_FLYBACK_TEST_CTL 0x06b2 +#define WCD9335_RX_AUX_SW_CTL 0x06b3 +#define WCD9335_RX_PA_AUX_IN_CONN 0x06b4 +#define WCD9335_RX_TIMER_DIV 0x06b5 +#define WCD9335_RX_OCP_CTL 0x06b6 +#define WCD9335_RX_OCP_COUNT 0x06b7 +#define WCD9335_RX_BIAS_EAR_DAC 0x06b8 +#define WCD9335_RX_BIAS_EAR_AMP 0x06b9 +#define WCD9335_RX_BIAS_HPH_LDO 0x06ba +#define WCD9335_RX_BIAS_HPH_PA 0x06bb +#define WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2 0x06bc +#define WCD9335_RX_BIAS_HPH_RDAC_LDO 0x06bd +#define WCD9335_RX_BIAS_HPH_CNP1 0x06be +#define WCD9335_RX_BIAS_HPH_LOWPOWER 0x06bf +#define WCD9335_RX_BIAS_DIFFLO_PA 0x06c0 +#define WCD9335_RX_BIAS_DIFFLO_REF 0x06c1 +#define WCD9335_RX_BIAS_DIFFLO_LDO 0x06c2 +#define WCD9335_RX_BIAS_SELO_DAC_PA 0x06c3 +#define WCD9335_RX_BIAS_BUCK_RST 0x06c4 +#define WCD9335_RX_BIAS_BUCK_VREF_ERRAMP 0x06c5 +#define WCD9335_RX_BIAS_FLYB_ERRAMP 0x06c6 +#define WCD9335_RX_BIAS_FLYB_BUFF 0x06c7 +#define WCD9335_RX_BIAS_FLYB_MID_RST 0x06c8 +#define WCD9335_HPH_L_STATUS 0x06c9 +#define WCD9335_HPH_R_STATUS 0x06ca +#define WCD9335_HPH_CNP_EN 0x06cb +#define WCD9335_HPH_CNP_WG_CTL 0x06cc +#define WCD9335_HPH_CNP_WG_TIME 0x06cd +#define WCD9335_HPH_OCP_CTL 0x06ce +#define WCD9335_HPH_AUTO_CHOP 0x06cf +#define WCD9335_HPH_CHOP_CTL 0x06d0 +#define WCD9335_HPH_PA_CTL1 0x06d1 +#define WCD9335_HPH_PA_CTL2 0x06d2 +#define WCD9335_HPH_L_EN 0x06d3 +#define WCD9335_HPH_L_TEST 0x06d4 +#define WCD9335_HPH_L_ATEST 0x06d5 +#define WCD9335_HPH_R_EN 0x06d6 +#define WCD9335_HPH_R_TEST 0x06d7 +#define WCD9335_HPH_R_ATEST 0x06d8 +#define WCD9335_HPH_RDAC_CLK_CTL1 0x06d9 +#define WCD9335_HPH_RDAC_CLK_CTL2 0x06da +#define WCD9335_HPH_RDAC_LDO_CTL 0x06db +#define WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL 0x06dc +#define WCD9335_HPH_REFBUFF_UHQA_CTL 0x06dd +#define WCD9335_HPH_REFBUFF_LP_CTL 0x06de +#define WCD9335_HPH_L_DAC_CTL 0x06df +#define WCD9335_HPH_R_DAC_CTL 0x06e0 +#define WCD9335_EAR_EN_REG 0x06e1 +#define WCD9335_EAR_CMBUFF 0x06e2 +#define WCD9335_EAR_ICTL 0x06e3 +#define WCD9335_EAR_EN_DBG_CTL 0x06e4 +#define WCD9335_EAR_CNP 0x06e5 +#define WCD9335_EAR_DAC_CTL_ATEST 0x06e6 +#define WCD9335_EAR_STATUS_REG 0x06e7 +#define WCD9335_EAR_OUT_SHORT 0x06e8 +#define WCD9335_DIFF_LO_MISC 0x06e9 +#define WCD9335_DIFF_LO_LO2_COMPANDER 0x06ea +#define WCD9335_DIFF_LO_LO1_COMPANDER 0x06eb +#define WCD9335_DIFF_LO_COMMON 0x06ec +#define WCD9335_DIFF_LO_BYPASS_EN 0x06ed +#define WCD9335_DIFF_LO_CNP 0x06ee +#define WCD9335_DIFF_LO_CORE_OUT_PROG 0x06ef +#define WCD9335_DIFF_LO_LDO_OUT_PROG 0x06f0 +#define WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ 0x06f1 +#define WCD9335_DIFF_LO_COM_PA_FREQ 0x06f2 +#define WCD9335_DIFF_LO_RESERVED_REG 0x06f3 +#define WCD9335_DIFF_LO_LO1_STATUS_1 0x06f4 +#define WCD9335_DIFF_LO_LO1_STATUS_2 0x06f5 +#define WCD9335_SE_LO_COM1 0x06f6 +#define WCD9335_SE_LO_COM2 0x06f7 +#define WCD9335_SE_LO_LO3_GAIN 0x06f8 +#define WCD9335_SE_LO_LO3_CTRL 0x06f9 +#define WCD9335_SE_LO_LO4_GAIN 0x06fa +#define WCD9335_SE_LO_LO4_CTRL 0x06fb +#define WCD9335_SE_LO_LO3_STATUS 0x06fe +#define WCD9335_SE_LO_LO4_STATUS 0x06ff + +/* Page-10 Registers */ +#define WCD9335_PAGE10_PAGE_REGISTER 0x0a00 +#define WCD9335_CDC_ANC0_CLK_RESET_CTL 0x0a01 +#define WCD9335_CDC_ANC0_MODE_1_CTL 0x0a02 +#define WCD9335_CDC_ANC0_MODE_2_CTL 0x0a03 +#define WCD9335_CDC_ANC0_FF_SHIFT 0x0a04 +#define WCD9335_CDC_ANC0_FB_SHIFT 0x0a05 +#define WCD9335_CDC_ANC0_LPF_FF_A_CTL 0x0a06 +#define WCD9335_CDC_ANC0_LPF_FF_B_CTL 0x0a07 +#define WCD9335_CDC_ANC0_LPF_FB_CTL 0x0a08 +#define WCD9335_CDC_ANC0_SMLPF_CTL 0x0a09 +#define WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL 0x0a0a +#define WCD9335_CDC_ANC0_IIR_ADAPT_CTL 0x0a0b +#define WCD9335_CDC_ANC0_IIR_COEFF_1_CTL 0x0a0c +#define WCD9335_CDC_ANC0_IIR_COEFF_2_CTL 0x0a0d +#define WCD9335_CDC_ANC0_FF_A_GAIN_CTL 0x0a0e +#define WCD9335_CDC_ANC0_FF_B_GAIN_CTL 0x0a0f +#define WCD9335_CDC_ANC0_FB_GAIN_CTL 0x0a10 +#define WCD9335_CDC_ANC1_CLK_RESET_CTL 0x0a19 +#define WCD9335_CDC_ANC1_MODE_1_CTL 0x0a1a +#define WCD9335_CDC_ANC1_MODE_2_CTL 0x0a1b +#define WCD9335_CDC_ANC1_FF_SHIFT 0x0a1c +#define WCD9335_CDC_ANC1_FB_SHIFT 0x0a1d +#define WCD9335_CDC_ANC1_LPF_FF_A_CTL 0x0a1e +#define WCD9335_CDC_ANC1_LPF_FF_B_CTL 0x0a1f +#define WCD9335_CDC_ANC1_LPF_FB_CTL 0x0a20 +#define WCD9335_CDC_ANC1_SMLPF_CTL 0x0a21 +#define WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL 0x0a22 +#define WCD9335_CDC_ANC1_IIR_ADAPT_CTL 0x0a23 +#define WCD9335_CDC_ANC1_IIR_COEFF_1_CTL 0x0a24 +#define WCD9335_CDC_ANC1_IIR_COEFF_2_CTL 0x0a25 +#define WCD9335_CDC_ANC1_FF_A_GAIN_CTL 0x0a26 +#define WCD9335_CDC_ANC1_FF_B_GAIN_CTL 0x0a27 +#define WCD9335_CDC_ANC1_FB_GAIN_CTL 0x0a28 +#define WCD9335_CDC_TX0_TX_PATH_CTL 0x0a31 +#define WCD9335_CDC_TX0_TX_PATH_CFG0 0x0a32 +#define WCD9335_CDC_TX0_TX_PATH_CFG1 0x0a33 +#define WCD9335_CDC_TX0_TX_VOL_CTL 0x0a34 +#define WCD9335_CDC_TX0_TX_PATH_192_CTL 0x0a35 +#define WCD9335_CDC_TX0_TX_PATH_192_CFG 0x0a36 +#define WCD9335_CDC_TX0_TX_PATH_SEC0 0x0a37 +#define WCD9335_CDC_TX0_TX_PATH_SEC1 0x0a38 +#define WCD9335_CDC_TX0_TX_PATH_SEC2 0x0a39 +#define WCD9335_CDC_TX0_TX_PATH_SEC3 0x0a3a +#define WCD9335_CDC_TX0_TX_PATH_SEC4 0x0a3b +#define WCD9335_CDC_TX0_TX_PATH_SEC5 0x0a3c +#define WCD9335_CDC_TX0_TX_PATH_SEC6 0x0a3d +#define WCD9335_CDC_TX0_TX_PATH_SEC7 0x0a3e +#define WCD9335_CDC_TX1_TX_PATH_CTL 0x0a41 +#define WCD9335_CDC_TX1_TX_PATH_CFG0 0x0a42 +#define WCD9335_CDC_TX1_TX_PATH_CFG1 0x0a43 +#define WCD9335_CDC_TX1_TX_VOL_CTL 0x0a44 +#define WCD9335_CDC_TX1_TX_PATH_192_CTL 0x0a45 +#define WCD9335_CDC_TX1_TX_PATH_192_CFG 0x0a46 +#define WCD9335_CDC_TX1_TX_PATH_SEC0 0x0a47 +#define WCD9335_CDC_TX1_TX_PATH_SEC1 0x0a48 +#define WCD9335_CDC_TX1_TX_PATH_SEC2 0x0a49 +#define WCD9335_CDC_TX1_TX_PATH_SEC3 0x0a4a +#define WCD9335_CDC_TX1_TX_PATH_SEC4 0x0a4b +#define WCD9335_CDC_TX1_TX_PATH_SEC5 0x0a4c +#define WCD9335_CDC_TX1_TX_PATH_SEC6 0x0a4d +#define WCD9335_CDC_TX2_TX_PATH_CTL 0x0a51 +#define WCD9335_CDC_TX2_TX_PATH_CFG0 0x0a52 +#define WCD9335_CDC_TX2_TX_PATH_CFG1 0x0a53 +#define WCD9335_CDC_TX2_TX_VOL_CTL 0x0a54 +#define WCD9335_CDC_TX2_TX_PATH_192_CTL 0x0a55 +#define WCD9335_CDC_TX2_TX_PATH_192_CFG 0x0a56 +#define WCD9335_CDC_TX2_TX_PATH_SEC0 0x0a57 +#define WCD9335_CDC_TX2_TX_PATH_SEC1 0x0a58 +#define WCD9335_CDC_TX2_TX_PATH_SEC2 0x0a59 +#define WCD9335_CDC_TX2_TX_PATH_SEC3 0x0a5a +#define WCD9335_CDC_TX2_TX_PATH_SEC4 0x0a5b +#define WCD9335_CDC_TX2_TX_PATH_SEC5 0x0a5c +#define WCD9335_CDC_TX2_TX_PATH_SEC6 0x0a5d +#define WCD9335_CDC_TX3_TX_PATH_CTL 0x0a61 +#define WCD9335_CDC_TX3_TX_PATH_CFG0 0x0a62 +#define WCD9335_CDC_TX3_TX_PATH_CFG1 0x0a63 +#define WCD9335_CDC_TX3_TX_VOL_CTL 0x0a64 +#define WCD9335_CDC_TX3_TX_PATH_192_CTL 0x0a65 +#define WCD9335_CDC_TX3_TX_PATH_192_CFG 0x0a66 +#define WCD9335_CDC_TX3_TX_PATH_SEC0 0x0a67 +#define WCD9335_CDC_TX3_TX_PATH_SEC1 0x0a68 +#define WCD9335_CDC_TX3_TX_PATH_SEC2 0x0a69 +#define WCD9335_CDC_TX3_TX_PATH_SEC3 0x0a6a +#define WCD9335_CDC_TX3_TX_PATH_SEC4 0x0a6b +#define WCD9335_CDC_TX3_TX_PATH_SEC5 0x0a6c +#define WCD9335_CDC_TX3_TX_PATH_SEC6 0x0a6d +#define WCD9335_CDC_TX4_TX_PATH_CTL 0x0a71 +#define WCD9335_CDC_TX4_TX_PATH_CFG0 0x0a72 +#define WCD9335_CDC_TX4_TX_PATH_CFG1 0x0a73 +#define WCD9335_CDC_TX4_TX_VOL_CTL 0x0a74 +#define WCD9335_CDC_TX4_TX_PATH_192_CTL 0x0a75 +#define WCD9335_CDC_TX4_TX_PATH_192_CFG 0x0a76 +#define WCD9335_CDC_TX4_TX_PATH_SEC0 0x0a77 +#define WCD9335_CDC_TX4_TX_PATH_SEC1 0x0a78 +#define WCD9335_CDC_TX4_TX_PATH_SEC2 0x0a79 +#define WCD9335_CDC_TX4_TX_PATH_SEC3 0x0a7a +#define WCD9335_CDC_TX4_TX_PATH_SEC4 0x0a7b +#define WCD9335_CDC_TX4_TX_PATH_SEC5 0x0a7c +#define WCD9335_CDC_TX4_TX_PATH_SEC6 0x0a7d +#define WCD9335_CDC_TX5_TX_PATH_CTL 0x0a81 +#define WCD9335_CDC_TX5_TX_PATH_CFG0 0x0a82 +#define WCD9335_CDC_TX5_TX_PATH_CFG1 0x0a83 +#define WCD9335_CDC_TX5_TX_VOL_CTL 0x0a84 +#define WCD9335_CDC_TX5_TX_PATH_192_CTL 0x0a85 +#define WCD9335_CDC_TX5_TX_PATH_192_CFG 0x0a86 +#define WCD9335_CDC_TX5_TX_PATH_SEC0 0x0a87 +#define WCD9335_CDC_TX5_TX_PATH_SEC1 0x0a88 +#define WCD9335_CDC_TX5_TX_PATH_SEC2 0x0a89 +#define WCD9335_CDC_TX5_TX_PATH_SEC3 0x0a8a +#define WCD9335_CDC_TX5_TX_PATH_SEC4 0x0a8b +#define WCD9335_CDC_TX5_TX_PATH_SEC5 0x0a8c +#define WCD9335_CDC_TX5_TX_PATH_SEC6 0x0a8d +#define WCD9335_CDC_TX6_TX_PATH_CTL 0x0a91 +#define WCD9335_CDC_TX6_TX_PATH_CFG0 0x0a92 +#define WCD9335_CDC_TX6_TX_PATH_CFG1 0x0a93 +#define WCD9335_CDC_TX6_TX_VOL_CTL 0x0a94 +#define WCD9335_CDC_TX6_TX_PATH_192_CTL 0x0a95 +#define WCD9335_CDC_TX6_TX_PATH_192_CFG 0x0a96 +#define WCD9335_CDC_TX6_TX_PATH_SEC0 0x0a97 +#define WCD9335_CDC_TX6_TX_PATH_SEC1 0x0a98 +#define WCD9335_CDC_TX6_TX_PATH_SEC2 0x0a99 +#define WCD9335_CDC_TX6_TX_PATH_SEC3 0x0a9a +#define WCD9335_CDC_TX6_TX_PATH_SEC4 0x0a9b +#define WCD9335_CDC_TX6_TX_PATH_SEC5 0x0a9c +#define WCD9335_CDC_TX6_TX_PATH_SEC6 0x0a9d +#define WCD9335_CDC_TX7_TX_PATH_CTL 0x0aa1 +#define WCD9335_CDC_TX7_TX_PATH_CFG0 0x0aa2 +#define WCD9335_CDC_TX7_TX_PATH_CFG1 0x0aa3 +#define WCD9335_CDC_TX7_TX_VOL_CTL 0x0aa4 +#define WCD9335_CDC_TX7_TX_PATH_192_CTL 0x0aa5 +#define WCD9335_CDC_TX7_TX_PATH_192_CFG 0x0aa6 +#define WCD9335_CDC_TX7_TX_PATH_SEC0 0x0aa7 +#define WCD9335_CDC_TX7_TX_PATH_SEC1 0x0aa8 +#define WCD9335_CDC_TX7_TX_PATH_SEC2 0x0aa9 +#define WCD9335_CDC_TX7_TX_PATH_SEC3 0x0aaa +#define WCD9335_CDC_TX7_TX_PATH_SEC4 0x0aab +#define WCD9335_CDC_TX7_TX_PATH_SEC5 0x0aac +#define WCD9335_CDC_TX7_TX_PATH_SEC6 0x0aad +#define WCD9335_CDC_TX8_TX_PATH_CTL 0x0ab1 +#define WCD9335_CDC_TX8_TX_PATH_CFG0 0x0ab2 +#define WCD9335_CDC_TX8_TX_PATH_CFG1 0x0ab3 +#define WCD9335_CDC_TX8_TX_VOL_CTL 0x0ab4 +#define WCD9335_CDC_TX8_TX_PATH_192_CTL 0x0ab5 +#define WCD9335_CDC_TX8_TX_PATH_192_CFG 0x0ab6 +#define WCD9335_CDC_TX8_TX_PATH_SEC0 0x0ab7 +#define WCD9335_CDC_TX8_TX_PATH_SEC1 0x0ab8 +#define WCD9335_CDC_TX8_TX_PATH_SEC2 0x0ab9 +#define WCD9335_CDC_TX8_TX_PATH_SEC3 0x0aba +#define WCD9335_CDC_TX8_TX_PATH_SEC4 0x0abb +#define WCD9335_CDC_TX8_TX_PATH_SEC5 0x0abc +#define WCD9335_CDC_TX8_TX_PATH_SEC6 0x0abd +#define WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL 0x0ac2 +#define WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0 0x0ac3 +#define WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL 0x0ac6 +#define WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0 0x0ac7 +#define WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL 0x0aca +#define WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0 0x0acb +#define WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL 0x0ace +#define WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0 0x0acf + +/* Page-11 Registers */ +#define WCD9335_PAGE11_PAGE_REGISTER 0x0b00 +#define WCD9335_CDC_COMPANDER1_CTL0 0x0b01 +#define WCD9335_CDC_COMPANDER1_CTL1 0x0b02 +#define WCD9335_CDC_COMPANDER1_CTL2 0x0b03 +#define WCD9335_CDC_COMPANDER1_CTL3 0x0b04 +#define WCD9335_CDC_COMPANDER1_CTL4 0x0b05 +#define WCD9335_CDC_COMPANDER1_CTL5 0x0b06 +#define WCD9335_CDC_COMPANDER1_CTL6 0x0b07 +#define WCD9335_CDC_COMPANDER1_CTL7 0x0b08 +#define WCD9335_CDC_COMPANDER2_CTL0 0x0b09 +#define WCD9335_CDC_COMPANDER2_CTL1 0x0b0a +#define WCD9335_CDC_COMPANDER2_CTL2 0x0b0b +#define WCD9335_CDC_COMPANDER2_CTL3 0x0b0c +#define WCD9335_CDC_COMPANDER2_CTL4 0x0b0d +#define WCD9335_CDC_COMPANDER2_CTL5 0x0b0e +#define WCD9335_CDC_COMPANDER2_CTL6 0x0b0f +#define WCD9335_CDC_COMPANDER2_CTL7 0x0b10 +#define WCD9335_CDC_COMPANDER3_CTL0 0x0b11 +#define WCD9335_CDC_COMPANDER3_CTL1 0x0b12 +#define WCD9335_CDC_COMPANDER3_CTL2 0x0b13 +#define WCD9335_CDC_COMPANDER3_CTL3 0x0b14 +#define WCD9335_CDC_COMPANDER3_CTL4 0x0b15 +#define WCD9335_CDC_COMPANDER3_CTL5 0x0b16 +#define WCD9335_CDC_COMPANDER3_CTL6 0x0b17 +#define WCD9335_CDC_COMPANDER3_CTL7 0x0b18 +#define WCD9335_CDC_COMPANDER4_CTL0 0x0b19 +#define WCD9335_CDC_COMPANDER4_CTL1 0x0b1a +#define WCD9335_CDC_COMPANDER4_CTL2 0x0b1b +#define WCD9335_CDC_COMPANDER4_CTL3 0x0b1c +#define WCD9335_CDC_COMPANDER4_CTL4 0x0b1d +#define WCD9335_CDC_COMPANDER4_CTL5 0x0b1e +#define WCD9335_CDC_COMPANDER4_CTL6 0x0b1f +#define WCD9335_CDC_COMPANDER4_CTL7 0x0b20 +#define WCD9335_CDC_COMPANDER5_CTL0 0x0b21 +#define WCD9335_CDC_COMPANDER5_CTL1 0x0b22 +#define WCD9335_CDC_COMPANDER5_CTL2 0x0b23 +#define WCD9335_CDC_COMPANDER5_CTL3 0x0b24 +#define WCD9335_CDC_COMPANDER5_CTL4 0x0b25 +#define WCD9335_CDC_COMPANDER5_CTL5 0x0b26 +#define WCD9335_CDC_COMPANDER5_CTL6 0x0b27 +#define WCD9335_CDC_COMPANDER5_CTL7 0x0b28 +#define WCD9335_CDC_COMPANDER6_CTL0 0x0b29 +#define WCD9335_CDC_COMPANDER6_CTL1 0x0b2a +#define WCD9335_CDC_COMPANDER6_CTL2 0x0b2b +#define WCD9335_CDC_COMPANDER6_CTL3 0x0b2c +#define WCD9335_CDC_COMPANDER6_CTL4 0x0b2d +#define WCD9335_CDC_COMPANDER6_CTL5 0x0b2e +#define WCD9335_CDC_COMPANDER6_CTL6 0x0b2f +#define WCD9335_CDC_COMPANDER6_CTL7 0x0b30 +#define WCD9335_CDC_COMPANDER7_CTL0 0x0b31 +#define WCD9335_CDC_COMPANDER7_CTL1 0x0b32 +#define WCD9335_CDC_COMPANDER7_CTL2 0x0b33 +#define WCD9335_CDC_COMPANDER7_CTL3 0x0b34 +#define WCD9335_CDC_COMPANDER7_CTL4 0x0b35 +#define WCD9335_CDC_COMPANDER7_CTL5 0x0b36 +#define WCD9335_CDC_COMPANDER7_CTL6 0x0b37 +#define WCD9335_CDC_COMPANDER7_CTL7 0x0b38 +#define WCD9335_CDC_COMPANDER8_CTL0 0x0b39 +#define WCD9335_CDC_COMPANDER8_CTL1 0x0b3a +#define WCD9335_CDC_COMPANDER8_CTL2 0x0b3b +#define WCD9335_CDC_COMPANDER8_CTL3 0x0b3c +#define WCD9335_CDC_COMPANDER8_CTL4 0x0b3d +#define WCD9335_CDC_COMPANDER8_CTL5 0x0b3e +#define WCD9335_CDC_COMPANDER8_CTL6 0x0b3f +#define WCD9335_CDC_COMPANDER8_CTL7 0x0b40 +#define WCD9335_CDC_RX0_RX_PATH_CTL 0x0b41 +#define WCD9335_CDC_RX0_RX_PATH_CFG0 0x0b42 +#define WCD9335_CDC_RX0_RX_PATH_CFG1 0x0b43 +#define WCD9335_CDC_RX0_RX_PATH_CFG2 0x0b44 +#define WCD9335_CDC_RX0_RX_VOL_CTL 0x0b45 +#define WCD9335_CDC_RX0_RX_PATH_MIX_CTL 0x0b46 +#define WCD9335_CDC_RX0_RX_PATH_MIX_CFG 0x0b47 +#define WCD9335_CDC_RX0_RX_VOL_MIX_CTL 0x0b48 +#define WCD9335_CDC_RX0_RX_PATH_SEC0 0x0b49 +#define WCD9335_CDC_RX0_RX_PATH_SEC1 0x0b4a +#define WCD9335_CDC_RX0_RX_PATH_SEC2 0x0b4b +#define WCD9335_CDC_RX0_RX_PATH_SEC3 0x0b4c +#define WCD9335_CDC_RX0_RX_PATH_SEC5 0x0b4e +#define WCD9335_CDC_RX0_RX_PATH_SEC6 0x0b4f +#define WCD9335_CDC_RX0_RX_PATH_SEC7 0x0b50 +#define WCD9335_CDC_RX0_RX_PATH_MIX_SEC0 0x0b51 +#define WCD9335_CDC_RX0_RX_PATH_MIX_SEC1 0x0b52 +#define WCD9335_CDC_RX1_RX_PATH_CTL 0x0b55 +#define WCD9335_CDC_RX1_RX_PATH_CFG0 0x0b56 +#define WCD9335_CDC_RX1_RX_PATH_CFG1 0x0b57 +#define WCD9335_CDC_RX1_RX_PATH_CFG2 0x0b58 +#define WCD9335_CDC_RX1_RX_VOL_CTL 0x0b59 +#define WCD9335_CDC_RX1_RX_PATH_MIX_CTL 0x0b5a +#define WCD9335_CDC_RX1_RX_PATH_MIX_CFG 0x0b5b +#define WCD9335_CDC_RX1_RX_VOL_MIX_CTL 0x0b5c +#define WCD9335_CDC_RX1_RX_PATH_SEC0 0x0b5d +#define WCD9335_CDC_RX1_RX_PATH_SEC1 0x0b5e +#define WCD9335_CDC_RX1_RX_PATH_SEC2 0x0b5f +#define WCD9335_CDC_RX1_RX_PATH_SEC3 0x0b60 +#define WCD9335_CDC_RX1_RX_PATH_SEC4 0x0b61 +#define WCD9335_CDC_RX1_RX_PATH_SEC5 0x0b62 +#define WCD9335_CDC_RX1_RX_PATH_SEC6 0x0b63 +#define WCD9335_CDC_RX1_RX_PATH_SEC7 0x0b64 +#define WCD9335_CDC_RX1_RX_PATH_MIX_SEC0 0x0b65 +#define WCD9335_CDC_RX1_RX_PATH_MIX_SEC1 0x0b66 +#define WCD9335_CDC_RX2_RX_PATH_CTL 0x0b69 +#define WCD9335_CDC_RX2_RX_PATH_CFG0 0x0b6a +#define WCD9335_CDC_RX2_RX_PATH_CFG1 0x0b6b +#define WCD9335_CDC_RX2_RX_PATH_CFG2 0x0b6c +#define WCD9335_CDC_RX2_RX_VOL_CTL 0x0b6d +#define WCD9335_CDC_RX2_RX_PATH_MIX_CTL 0x0b6e +#define WCD9335_CDC_RX2_RX_PATH_MIX_CFG 0x0b6f +#define WCD9335_CDC_RX2_RX_VOL_MIX_CTL 0x0b70 +#define WCD9335_CDC_RX2_RX_PATH_SEC0 0x0b71 +#define WCD9335_CDC_RX2_RX_PATH_SEC1 0x0b72 +#define WCD9335_CDC_RX2_RX_PATH_SEC2 0x0b73 +#define WCD9335_CDC_RX2_RX_PATH_SEC3 0x0b74 +#define WCD9335_CDC_RX2_RX_PATH_SEC4 0x0b75 +#define WCD9335_CDC_RX2_RX_PATH_SEC5 0x0b76 +#define WCD9335_CDC_RX2_RX_PATH_SEC6 0x0b77 +#define WCD9335_CDC_RX2_RX_PATH_SEC7 0x0b78 +#define WCD9335_CDC_RX2_RX_PATH_MIX_SEC0 0x0b79 +#define WCD9335_CDC_RX2_RX_PATH_MIX_SEC1 0x0b7a +#define WCD9335_CDC_RX3_RX_PATH_CTL 0x0b7d +#define WCD9335_CDC_RX3_RX_PATH_CFG0 0x0b7e +#define WCD9335_CDC_RX3_RX_PATH_CFG1 0x0b7f +#define WCD9335_CDC_RX3_RX_PATH_CFG2 0x0b80 +#define WCD9335_CDC_RX3_RX_VOL_CTL 0x0b81 +#define WCD9335_CDC_RX3_RX_PATH_MIX_CTL 0x0b82 +#define WCD9335_CDC_RX3_RX_PATH_MIX_CFG 0x0b83 +#define WCD9335_CDC_RX3_RX_VOL_MIX_CTL 0x0b84 +#define WCD9335_CDC_RX3_RX_PATH_SEC0 0x0b85 +#define WCD9335_CDC_RX3_RX_PATH_SEC1 0x0b86 +#define WCD9335_CDC_RX3_RX_PATH_SEC2 0x0b87 +#define WCD9335_CDC_RX3_RX_PATH_SEC3 0x0b88 +#define WCD9335_CDC_RX3_RX_PATH_SEC5 0x0b8a +#define WCD9335_CDC_RX3_RX_PATH_SEC6 0x0b8b +#define WCD9335_CDC_RX3_RX_PATH_SEC7 0x0b8c +#define WCD9335_CDC_RX3_RX_PATH_MIX_SEC0 0x0b8d +#define WCD9335_CDC_RX3_RX_PATH_MIX_SEC1 0x0b8e +#define WCD9335_CDC_RX4_RX_PATH_CTL 0x0b91 +#define WCD9335_CDC_RX4_RX_PATH_CFG0 0x0b92 +#define WCD9335_CDC_RX4_RX_PATH_CFG1 0x0b93 +#define WCD9335_CDC_RX4_RX_PATH_CFG2 0x0b94 +#define WCD9335_CDC_RX4_RX_VOL_CTL 0x0b95 +#define WCD9335_CDC_RX4_RX_PATH_MIX_CTL 0x0b96 +#define WCD9335_CDC_RX4_RX_PATH_MIX_CFG 0x0b97 +#define WCD9335_CDC_RX4_RX_VOL_MIX_CTL 0x0b98 +#define WCD9335_CDC_RX4_RX_PATH_SEC0 0x0b99 +#define WCD9335_CDC_RX4_RX_PATH_SEC1 0x0b9a +#define WCD9335_CDC_RX4_RX_PATH_SEC2 0x0b9b +#define WCD9335_CDC_RX4_RX_PATH_SEC3 0x0b9c +#define WCD9335_CDC_RX4_RX_PATH_SEC5 0x0b9e +#define WCD9335_CDC_RX4_RX_PATH_SEC6 0x0b9f +#define WCD9335_CDC_RX4_RX_PATH_SEC7 0x0ba0 +#define WCD9335_CDC_RX4_RX_PATH_MIX_SEC0 0x0ba1 +#define WCD9335_CDC_RX4_RX_PATH_MIX_SEC1 0x0ba2 +#define WCD9335_CDC_RX5_RX_PATH_CTL 0x0ba5 +#define WCD9335_CDC_RX5_RX_PATH_CFG0 0x0ba6 +#define WCD9335_CDC_RX5_RX_PATH_CFG1 0x0ba7 +#define WCD9335_CDC_RX5_RX_PATH_CFG2 0x0ba8 +#define WCD9335_CDC_RX5_RX_VOL_CTL 0x0ba9 +#define WCD9335_CDC_RX5_RX_PATH_MIX_CTL 0x0baa +#define WCD9335_CDC_RX5_RX_PATH_MIX_CFG 0x0bab +#define WCD9335_CDC_RX5_RX_VOL_MIX_CTL 0x0bac +#define WCD9335_CDC_RX5_RX_PATH_SEC0 0x0bad +#define WCD9335_CDC_RX5_RX_PATH_SEC1 0x0bae +#define WCD9335_CDC_RX5_RX_PATH_SEC2 0x0baf +#define WCD9335_CDC_RX5_RX_PATH_SEC3 0x0bb0 +#define WCD9335_CDC_RX5_RX_PATH_SEC5 0x0bb2 +#define WCD9335_CDC_RX5_RX_PATH_SEC6 0x0bb3 +#define WCD9335_CDC_RX5_RX_PATH_SEC7 0x0bb4 +#define WCD9335_CDC_RX5_RX_PATH_MIX_SEC0 0x0bb5 +#define WCD9335_CDC_RX5_RX_PATH_MIX_SEC1 0x0bb6 +#define WCD9335_CDC_RX6_RX_PATH_CTL 0x0bb9 +#define WCD9335_CDC_RX6_RX_PATH_CFG0 0x0bba +#define WCD9335_CDC_RX6_RX_PATH_CFG1 0x0bbb +#define WCD9335_CDC_RX6_RX_PATH_CFG2 0x0bbc +#define WCD9335_CDC_RX6_RX_VOL_CTL 0x0bbd +#define WCD9335_CDC_RX6_RX_PATH_MIX_CTL 0x0bbe +#define WCD9335_CDC_RX6_RX_PATH_MIX_CFG 0x0bbf +#define WCD9335_CDC_RX6_RX_VOL_MIX_CTL 0x0bc0 +#define WCD9335_CDC_RX6_RX_PATH_SEC0 0x0bc1 +#define WCD9335_CDC_RX6_RX_PATH_SEC1 0x0bc2 +#define WCD9335_CDC_RX6_RX_PATH_SEC2 0x0bc3 +#define WCD9335_CDC_RX6_RX_PATH_SEC3 0x0bc4 +#define WCD9335_CDC_RX6_RX_PATH_SEC5 0x0bc6 +#define WCD9335_CDC_RX6_RX_PATH_SEC6 0x0bc7 +#define WCD9335_CDC_RX6_RX_PATH_SEC7 0x0bc8 +#define WCD9335_CDC_RX6_RX_PATH_MIX_SEC0 0x0bc9 +#define WCD9335_CDC_RX6_RX_PATH_MIX_SEC1 0x0bca +#define WCD9335_CDC_RX7_RX_PATH_CTL 0x0bcd +#define WCD9335_CDC_RX7_RX_PATH_CFG0 0x0bce +#define WCD9335_CDC_RX7_RX_PATH_CFG1 0x0bcf +#define WCD9335_CDC_RX7_RX_PATH_CFG2 0x0bd0 +#define WCD9335_CDC_RX7_RX_VOL_CTL 0x0bd1 +#define WCD9335_CDC_RX7_RX_PATH_MIX_CTL 0x0bd2 +#define WCD9335_CDC_RX7_RX_PATH_MIX_CFG 0x0bd3 +#define WCD9335_CDC_RX7_RX_VOL_MIX_CTL 0x0bd4 +#define WCD9335_CDC_RX7_RX_PATH_SEC0 0x0bd5 +#define WCD9335_CDC_RX7_RX_PATH_SEC1 0x0bd6 +#define WCD9335_CDC_RX7_RX_PATH_SEC2 0x0bd7 +#define WCD9335_CDC_RX7_RX_PATH_SEC3 0x0bd8 +#define WCD9335_CDC_RX7_RX_PATH_SEC5 0x0bda +#define WCD9335_CDC_RX7_RX_PATH_SEC6 0x0bdb +#define WCD9335_CDC_RX7_RX_PATH_SEC7 0x0bdc +#define WCD9335_CDC_RX7_RX_PATH_MIX_SEC0 0x0bdd +#define WCD9335_CDC_RX7_RX_PATH_MIX_SEC1 0x0bde +#define WCD9335_CDC_RX8_RX_PATH_CTL 0x0be1 +#define WCD9335_CDC_RX8_RX_PATH_CFG0 0x0be2 +#define WCD9335_CDC_RX8_RX_PATH_CFG1 0x0be3 +#define WCD9335_CDC_RX8_RX_PATH_CFG2 0x0be4 +#define WCD9335_CDC_RX8_RX_VOL_CTL 0x0be5 +#define WCD9335_CDC_RX8_RX_PATH_MIX_CTL 0x0be6 +#define WCD9335_CDC_RX8_RX_PATH_MIX_CFG 0x0be7 +#define WCD9335_CDC_RX8_RX_VOL_MIX_CTL 0x0be8 +#define WCD9335_CDC_RX8_RX_PATH_SEC0 0x0be9 +#define WCD9335_CDC_RX8_RX_PATH_SEC1 0x0bea +#define WCD9335_CDC_RX8_RX_PATH_SEC2 0x0beb +#define WCD9335_CDC_RX8_RX_PATH_SEC3 0x0bec +#define WCD9335_CDC_RX8_RX_PATH_SEC5 0x0bee +#define WCD9335_CDC_RX8_RX_PATH_SEC6 0x0bef +#define WCD9335_CDC_RX8_RX_PATH_SEC7 0x0bf0 +#define WCD9335_CDC_RX8_RX_PATH_MIX_SEC0 0x0bf1 +#define WCD9335_CDC_RX8_RX_PATH_MIX_SEC1 0x0bf2 + +/* Page-12 Registers */ +#define WCD9335_PAGE12_PAGE_REGISTER 0x0c00 +#define WCD9335_CDC_CLSH_CRC 0x0c01 +#define WCD9335_CDC_CLSH_DLY_CTRL 0x0c02 +#define WCD9335_CDC_CLSH_DECAY_CTRL 0x0c03 +#define WCD9335_CDC_CLSH_HPH_V_PA 0x0c04 +#define WCD9335_CDC_CLSH_EAR_V_PA 0x0c05 +#define WCD9335_CDC_CLSH_HPH_V_HD 0x0c06 +#define WCD9335_CDC_CLSH_EAR_V_HD 0x0c07 +#define WCD9335_CDC_CLSH_K1_MSB 0x0c08 +#define WCD9335_CDC_CLSH_K1_LSB 0x0c09 +#define WCD9335_CDC_CLSH_K2_MSB 0x0c0a +#define WCD9335_CDC_CLSH_K2_LSB 0x0c0b +#define WCD9335_CDC_CLSH_IDLE_CTRL 0x0c0c +#define WCD9335_CDC_CLSH_IDLE_HPH 0x0c0d +#define WCD9335_CDC_CLSH_IDLE_EAR 0x0c0e +#define WCD9335_CDC_CLSH_TEST0 0x0c0f +#define WCD9335_CDC_CLSH_TEST1 0x0c10 +#define WCD9335_CDC_CLSH_OVR_VREF 0x0c11 +#define WCD9335_CDC_BOOST0_BOOST_PATH_CTL 0x0c19 +#define WCD9335_CDC_BOOST0_BOOST_CTL 0x0c1a +#define WCD9335_CDC_BOOST0_BOOST_CFG1 0x0c1b +#define WCD9335_CDC_BOOST0_BOOST_CFG2 0x0c1c +#define WCD9335_CDC_BOOST1_BOOST_PATH_CTL 0x0c21 +#define WCD9335_CDC_BOOST1_BOOST_CTL 0x0c22 +#define WCD9335_CDC_BOOST1_BOOST_CFG1 0x0c23 +#define WCD9335_CDC_BOOST1_BOOST_CFG2 0x0c24 +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_0 0x0c29 +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_1 0x0c2a +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_2 0x0c2b +#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_3 0x0c2c +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0 0x0c2d +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1 0x0c2e +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2 0x0c2f +#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3 0x0c30 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0 0x0c31 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1 0x0c32 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2 0x0c33 +#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3 0x0c34 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_0 0x0c35 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_1 0x0c36 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_2 0x0c37 +#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_3 0x0c38 +#define WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG 0x0c39 +#define WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS 0x0c3a +#define WCD9335_CDC_VBAT_VBAT_PATH_CTL 0x0c3d +#define WCD9335_CDC_VBAT_VBAT_CFG 0x0c3e +#define WCD9335_CDC_VBAT_VBAT_ADC_CAL1 0x0c3f +#define WCD9335_CDC_VBAT_VBAT_ADC_CAL2 0x0c40 +#define WCD9335_CDC_VBAT_VBAT_ADC_CAL3 0x0c41 +#define WCD9335_CDC_VBAT_VBAT_PK_EST1 0x0c42 +#define WCD9335_CDC_VBAT_VBAT_PK_EST2 0x0c43 +#define WCD9335_CDC_VBAT_VBAT_PK_EST3 0x0c44 +#define WCD9335_CDC_VBAT_VBAT_RF_PROC1 0x0c45 +#define WCD9335_CDC_VBAT_VBAT_RF_PROC2 0x0c46 +#define WCD9335_CDC_VBAT_VBAT_TAC1 0x0c47 +#define WCD9335_CDC_VBAT_VBAT_TAC2 0x0c48 +#define WCD9335_CDC_VBAT_VBAT_TAC3 0x0c49 +#define WCD9335_CDC_VBAT_VBAT_TAC4 0x0c4a +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD1 0x0c4b +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD2 0x0c4c +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD3 0x0c4d +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD4 0x0c4e +#define WCD9335_CDC_VBAT_VBAT_DEBUG1 0x0c4f +#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON 0x0c50 +#define WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL 0x0c51 +#define WCD9335_SPLINE_SRC0_CLK_RST_CTL_0 0x0c55 +#define WCD9335_SPLINE_SRC0_STATUS 0x0c56 +#define WCD9335_SPLINE_SRC1_CLK_RST_CTL_0 0x0c6d +#define WCD9335_SPLINE_SRC1_STATUS 0x0c6e +#define WCD9335_SPLINE_SRC2_CLK_RST_CTL_0 0x0c85 +#define WCD9335_SPLINE_SRC2_STATUS 0x0c86 +#define WCD9335_SPLINE_SRC3_CLK_RST_CTL_0 0x0c9d +#define WCD9335_SPLINE_SRC3_STATUS 0x0c9e +#define WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL 0x0cb5 +#define WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1 0x0cb6 +#define WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL 0x0cb9 +#define WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1 0x0cba + +/* Page-13 Registers */ +#define WCD9335_PAGE13_PAGE_REGISTER 0x0d00 +#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0 0x0d01 +#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1 0x0d02 +#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0 0x0d03 +#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1 0x0d04 +#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0 0x0d05 +#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1 0x0d06 +#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0 0x0d07 +#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1 0x0d08 +#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0 0x0d09 +#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1 0x0d0a +#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0 0x0d0b +#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1 0x0d0c +#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0 0x0d0d +#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1 0x0d0e +#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0 0x0d0f +#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1 0x0d10 +#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0 0x0d11 +#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1 0x0d12 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0 0x0d13 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1 0x0d14 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2 0x0d15 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3 0x0d16 +#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4 0x0d17 +#define WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 0x0d18 +#define WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1 0x0d19 +#define WCD9335_CDC_RX_INP_MUX_ANC_CFG0 0x0d1a +#define WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0 0x0d1b +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 0x0d1d +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 0x0d1e +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0 0x0d1f +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1 0x0d20 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0 0x0d21 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1 0x0d22 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0 0x0d23 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1 0x0d24 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 0x0d25 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0 0x0d26 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0 0x0d27 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0 0x0d28 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0 0x0d29 +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0 0x0d2b +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0 0x0d2c +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0 0x0d2d +#define WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0 0x0d2e +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0 0x0d31 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1 0x0d32 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2 0x0d33 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3 0x0d34 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0 0x0d35 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1 0x0d36 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2 0x0d37 +#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3 0x0d38 +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0 0x0d3a +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1 0x0d3b +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2 0x0d3c +#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3 0x0d3d +#define WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41 +#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42 +#define WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL 0x0d43 +#define WCD9335_CDC_PROX_DETECT_PROX_CTL 0x0d49 +#define WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0 0x0d4a +#define WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1 0x0d4b +#define WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB 0x0d4c +#define WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB 0x0d4d +#define WCD9335_CDC_PROX_DETECT_PROX_STATUS 0x0d4e +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL 0x0d4f +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB 0x0d50 +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB 0x0d51 +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD 0x0d52 +#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD 0x0d53 +#define WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT 0x0d54 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL 0x0d55 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL 0x0d56 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL 0x0d57 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL 0x0d58 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL 0x0d59 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL 0x0d5a +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL 0x0d5b +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL 0x0d5c +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL 0x0d5d +#define WCD9335_CDC_SIDETONE_IIR0_IIR_CTL 0x0d5e +#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL 0x0d5f +#define WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL 0x0d60 +#define WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL 0x0d61 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL 0x0d65 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL 0x0d66 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL 0x0d67 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL 0x0d68 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL 0x0d69 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL 0x0d6a +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL 0x0d6b +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL 0x0d6c +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL 0x0d6d +#define WCD9335_CDC_SIDETONE_IIR1_IIR_CTL 0x0d6e +#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL 0x0d6f +#define WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL 0x0d70 +#define WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL 0x0d71 +#define WCD9335_CDC_TOP_TOP_CFG0 0x0d81 +#define WCD9335_CDC_TOP_TOP_CFG1 0x0d82 +#define WCD9335_CDC_TOP_TOP_CFG2 0x0d83 +#define WCD9335_CDC_TOP_TOP_CFG3 0x0d84 +#define WCD9335_CDC_TOP_TOP_CFG4 0x0d85 +#define WCD9335_CDC_TOP_TOP_CFG5 0x0d86 +#define WCD9335_CDC_TOP_TOP_CFG6 0x0d87 +#define WCD9335_CDC_TOP_TOP_CFG7 0x0d88 +#define WCD9335_CDC_TOP_HPHL_COMP_WR_LSB 0x0d89 +#define WCD9335_CDC_TOP_HPHL_COMP_WR_MSB 0x0d8a +#define WCD9335_CDC_TOP_HPHL_COMP_LUT 0x0d8b +#define WCD9335_CDC_TOP_HPHL_COMP_RD_LSB 0x0d8c +#define WCD9335_CDC_TOP_HPHL_COMP_RD_MSB 0x0d8d +#define WCD9335_CDC_TOP_HPHR_COMP_WR_LSB 0x0d8e +#define WCD9335_CDC_TOP_HPHR_COMP_WR_MSB 0x0d8f +#define WCD9335_CDC_TOP_HPHR_COMP_LUT 0x0d90 +#define WCD9335_CDC_TOP_HPHR_COMP_RD_LSB 0x0d91 +#define WCD9335_CDC_TOP_HPHR_COMP_RD_MSB 0x0d92 +#define WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB 0x0d93 +#define WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB 0x0d94 +#define WCD9335_CDC_TOP_DIFFL_COMP_LUT 0x0d95 +#define WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB 0x0d96 +#define WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB 0x0d97 +#define WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB 0x0d98 +#define WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB 0x0d99 +#define WCD9335_CDC_TOP_DIFFR_COMP_LUT 0x0d9a +#define WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB 0x0d9b +#define WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB 0x0d9c + +/* Page-0x80 Registers */ +#define WCD9335_PAGE80_PAGE_REGISTER 0x8000 +#define WCD9335_TLMM_BIST_MODE_PINCFG 0x8001 +#define WCD9335_TLMM_RF_PA_ON_PINCFG 0x8002 +#define WCD9335_TLMM_INTR1_PINCFG 0x8003 +#define WCD9335_TLMM_INTR2_PINCFG 0x8004 +#define WCD9335_TLMM_SWR_DATA_PINCFG 0x8005 +#define WCD9335_TLMM_SWR_CLK_PINCFG 0x8006 +#define WCD9335_TLMM_SLIMBUS_DATA2_PINCFG 0x8007 +#define WCD9335_TLMM_I2C_CLK_PINCFG 0x8008 +#define WCD9335_TLMM_I2C_DATA_PINCFG 0x8009 +#define WCD9335_TLMM_I2S_RX_SD0_PINCFG 0x800a +#define WCD9335_TLMM_I2S_RX_SD1_PINCFG 0x800b +#define WCD9335_TLMM_I2S_RX_SCK_PINCFG 0x800c +#define WCD9335_TLMM_I2S_RX_WS_PINCFG 0x800d +#define WCD9335_TLMM_I2S_TX_SD0_PINCFG 0x800e +#define WCD9335_TLMM_I2S_TX_SD1_PINCFG 0x800f +#define WCD9335_TLMM_I2S_TX_SCK_PINCFG 0x8010 +#define WCD9335_TLMM_I2S_TX_WS_PINCFG 0x8011 +#define WCD9335_TLMM_DMIC1_CLK_PINCFG 0x8012 +#define WCD9335_TLMM_DMIC1_DATA_PINCFG 0x8013 +#define WCD9335_TLMM_DMIC2_CLK_PINCFG 0x8014 +#define WCD9335_TLMM_DMIC2_DATA_PINCFG 0x8015 +#define WCD9335_TLMM_DMIC3_CLK_PINCFG 0x8016 +#define WCD9335_TLMM_DMIC3_DATA_PINCFG 0x8017 +#define WCD9335_TLMM_JTDI_PINCFG 0x8018 +#define WCD9335_TLMM_JTDO_PINCFG 0x8019 +#define WCD9335_TLMM_JTMS_PINCFG 0x801a +#define WCD9335_TLMM_JTCK_PINCFG 0x801b +#define WCD9335_TLMM_JTRST_PINCFG 0x801c +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_0 0x8031 +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_1 0x8032 +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_2 0x8033 +#define WCD9335_TEST_DEBUG_PIN_CTL_OE_3 0x8034 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_0 0x8035 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_1 0x8036 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_2 0x8037 +#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_3 0x8038 +#define WCD9335_TEST_DEBUG_PAD_DRVCTL 0x8039 +#define WCD9335_TEST_DEBUG_PIN_STATUS 0x803a +#define WCD9335_TEST_DEBUG_NPL_DLY_TEST_1 0x803b +#define WCD9335_TEST_DEBUG_NPL_DLY_TEST_2 0x803c +#define WCD9335_TEST_DEBUG_MEM_CTRL 0x803d +#define WCD9335_TEST_DEBUG_DEBUG_BUS_SEL 0x8041 +#define WCD9335_TEST_DEBUG_DEBUG_JTAG 0x8042 +#define WCD9335_TEST_DEBUG_DEBUG_EN_1 0x8043 +#define WCD9335_TEST_DEBUG_DEBUG_EN_2 0x8044 +#define WCD9335_TEST_DEBUG_DEBUG_EN_3 0x8045 +#define WCD9335_MAX_REGISTER 0x80FF + +/* SLIMBUS Slave Registers */ +#define TASHA_SLIM_PGD_PORT_INT_EN0 (0x30) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_RX_0 (0x34) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_RX_1 (0x35) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_TX_0 (0x36) +#define TASHA_SLIM_PGD_PORT_INT_STATUS_TX_1 (0x37) +#define TASHA_SLIM_PGD_PORT_INT_CLR_RX_0 (0x38) +#define TASHA_SLIM_PGD_PORT_INT_CLR_RX_1 (0x39) +#define TASHA_SLIM_PGD_PORT_INT_CLR_TX_0 (0x3A) +#define TASHA_SLIM_PGD_PORT_INT_CLR_TX_1 (0x3B) +#define TASHA_SLIM_PGD_PORT_INT_RX_SOURCE0 (0x60) +#define TASHA_SLIM_PGD_PORT_INT_TX_SOURCE0 (0x70) + +/* Macros for Packing Register Writes into a U32 */ +#define TASHA_PACKED_REG_SIZE sizeof(u32) + +#define TASHA_CODEC_PACK_ENTRY(reg, mask, val) ((val & 0xff)|\ + ((mask & 0xff) << 8)|((reg & 0xffff) << 16)) +#define TASHA_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0) +#endif diff --git a/include/linux/mfd/wcd934x/irq.h b/include/linux/mfd/wcd934x/irq.h new file mode 100644 index 000000000000..1a18be376eb1 --- /dev/null +++ b/include/linux/mfd/wcd934x/irq.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 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 __WCD934X_IRQ_H_ +#define __WCD934X_IRQ_H_ + +enum { + /* INTR_REG 0 */ + WCD934X_IRQ_MISC = 1, + WCD934X_IRQ_HPH_PA_OCPL_FAULT, + WCD934X_IRQ_HPH_PA_OCPR_FAULT, + WCD934X_IRQ_EAR_PA_OCP_FAULT, + WCD934X_IRQ_HPH_PA_CNPL_COMPLETE, + WCD934X_IRQ_HPH_PA_CNPR_COMPLETE, + WCD934X_IRQ_EAR_PA_CNP_COMPLETE, + /* INTR_REG 1 */ + WCD934X_IRQ_MBHC_SW_DET, + WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, + WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, + WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, + WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + WCD934X_IRQ_RESERVED_0, + WCD934X_IRQ_RESERVED_1, + WCD934X_IRQ_RESERVED_2, + /* INTR_REG 2 */ + WCD934X_IRQ_LINE_PA1_CNP_COMPLETE, + WCD934X_IRQ_LINE_PA2_CNP_COMPLETE, + WCD934X_IRQ_SLNQ_ANALOG_ERROR, + WCD934X_IRQ_RESERVED_3, + WCD934X_IRQ_SOUNDWIRE, + WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE, + WCD934X_IRQ_RCO_ERROR, + WCD934X_IRQ_CPE_ERROR, + /* INTR_REG 3 */ + WCD934X_IRQ_MAD_AUDIO, + WCD934X_IRQ_MAD_BEACON, + WCD934X_IRQ_MAD_ULTRASOUND, + WCD934X_IRQ_VBAT_ATTACK, + WCD934X_IRQ_VBAT_RESTORE, + WCD934X_IRQ_CPE1_INTR, + WCD934X_IRQ_RESERVED_4, + WCD934X_IRQ_SLNQ_DIGITAL, + WCD934X_NUM_IRQS, +}; + +#endif diff --git a/include/linux/mfd/wcd934x/registers.h b/include/linux/mfd/wcd934x/registers.h new file mode 100644 index 000000000000..a824875096e4 --- /dev/null +++ b/include/linux/mfd/wcd934x/registers.h @@ -0,0 +1,1848 @@ +/* + * 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 _WCD934X_REGISTERS_H +#define _WCD934X_REGISTERS_H + +#define WCD934X_PAGE_SIZE 256 +#define WCD934X_NUM_PAGES 256 + +extern const u8 * const wcd934x_reg[WCD934X_NUM_PAGES]; + +enum { + WCD934X_PAGE_0 = 0, + WCD934X_PAGE_1, + WCD934X_PAGE_2, + WCD934X_PAGE_4 = 4, + WCD934X_PAGE_5, + WCD934X_PAGE_6, + WCD934X_PAGE_7, + WCD934X_PAGE_10 = 0xA, + WCD934X_PAGE_11, + WCD934X_PAGE_12, + WCD934X_PAGE_13, + WCD934X_PAGE_14, + WCD934X_PAGE_15, + WCD934X_PAGE_0x50, + WCD934X_PAGE_0X80, +}; + +enum { + WCD934X_WRITE = 0, + WCD934X_READ, + WCD934X_READ_WRITE, +}; + +/* Page-0 Registers */ +#define WCD934X_PAGE0_PAGE_REGISTER 0x0000 +#define WCD934X_CODEC_RPM_CLK_BYPASS 0x0001 +#define WCD934X_CODEC_RPM_CLK_GATE 0x0002 +#define WCD934X_CODEC_RPM_CLK_MCLK_CFG 0x0003 +#define WCD934X_CODEC_RPM_CLK_MCLK2_CFG 0x0004 +#define WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL 0x0005 +#define WCD934X_CODEC_RPM_RST_CTL 0x0009 +#define WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL 0x0011 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0 0x0021 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE1 0x0022 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2 0x0023 +#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3 0x0024 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_CTL 0x0025 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0 0x0026 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1 0x0027 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0 0x0029 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 0x002a +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 0x002b +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3 0x002c +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4 0x002d +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5 0x002e +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6 0x002f +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7 0x0030 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8 0x0031 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9 0x0032 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10 0x0033 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11 0x0034 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12 0x0035 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13 0x0036 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14 0x0037 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15 0x0038 +#define WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS 0x0039 +#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO 0x003a +#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1 0x003b +#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2 0x003c +#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3 0x003d +#define WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL 0x003e +#define WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL 0x003f +#define WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE 0x0040 +#define WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN 0x0041 +#define WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE 0x0042 +#define WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA 0x0043 +#define WCD934X_DATA_HUB_RX0_CFG 0x0051 +#define WCD934X_DATA_HUB_RX1_CFG 0x0052 +#define WCD934X_DATA_HUB_RX2_CFG 0x0053 +#define WCD934X_DATA_HUB_RX3_CFG 0x0054 +#define WCD934X_DATA_HUB_RX4_CFG 0x0055 +#define WCD934X_DATA_HUB_RX5_CFG 0x0056 +#define WCD934X_DATA_HUB_RX6_CFG 0x0057 +#define WCD934X_DATA_HUB_RX7_CFG 0x0058 +#define WCD934X_DATA_HUB_SB_TX0_INP_CFG 0x0061 +#define WCD934X_DATA_HUB_SB_TX1_INP_CFG 0x0062 +#define WCD934X_DATA_HUB_SB_TX2_INP_CFG 0x0063 +#define WCD934X_DATA_HUB_SB_TX3_INP_CFG 0x0064 +#define WCD934X_DATA_HUB_SB_TX4_INP_CFG 0x0065 +#define WCD934X_DATA_HUB_SB_TX5_INP_CFG 0x0066 +#define WCD934X_DATA_HUB_SB_TX6_INP_CFG 0x0067 +#define WCD934X_DATA_HUB_SB_TX7_INP_CFG 0x0068 +#define WCD934X_DATA_HUB_SB_TX8_INP_CFG 0x0069 +#define WCD934X_DATA_HUB_SB_TX9_INP_CFG 0x006a +#define WCD934X_DATA_HUB_SB_TX10_INP_CFG 0x006b +#define WCD934X_DATA_HUB_SB_TX11_INP_CFG 0x006c +#define WCD934X_DATA_HUB_SB_TX13_INP_CFG 0x006e +#define WCD934X_DATA_HUB_SB_TX14_INP_CFG 0x006f +#define WCD934X_DATA_HUB_SB_TX15_INP_CFG 0x0070 +#define WCD934X_DATA_HUB_I2S_TX0_CFG 0x0071 +#define WCD934X_DATA_HUB_I2S_TX1_0_CFG 0x0073 +#define WCD934X_DATA_HUB_I2S_TX1_1_CFG 0x0074 +#define WCD934X_DATA_HUB_I2S_0_CTL 0x0081 +#define WCD934X_DATA_HUB_I2S_1_CTL 0x0082 +#define WCD934X_DATA_HUB_I2S_2_CTL 0x0083 +#define WCD934X_DATA_HUB_I2S_3_CTL 0x0084 +#define WCD934X_DATA_HUB_I2S_CLKSRC_CTL 0x0085 +#define WCD934X_DATA_HUB_I2S_COMMON_CTL 0x0086 +#define WCD934X_DATA_HUB_I2S_0_TDM_CTL 0x0087 +#define WCD934X_DATA_HUB_I2S_STATUS 0x0088 +#define WCD934X_DMA_RDMA_CTL_0 0x0091 +#define WCD934X_DMA_CH_2_3_CFG_RDMA_0 0x0092 +#define WCD934X_DMA_CH_0_1_CFG_RDMA_0 0x0093 +#define WCD934X_DMA_RDMA_CTL_1 0x0094 +#define WCD934X_DMA_CH_2_3_CFG_RDMA_1 0x0095 +#define WCD934X_DMA_CH_0_1_CFG_RDMA_1 0x0096 +#define WCD934X_DMA_RDMA_CTL_2 0x0097 +#define WCD934X_DMA_CH_2_3_CFG_RDMA_2 0x0098 +#define WCD934X_DMA_CH_0_1_CFG_RDMA_2 0x0099 +#define WCD934X_DMA_RDMA_CTL_3 0x009A +#define WCD934X_DMA_CH_2_3_CFG_RDMA_3 0x009B +#define WCD934X_DMA_CH_0_1_CFG_RDMA_3 0x009C +#define WCD934X_DMA_RDMA_CTL_4 0x009D +#define WCD934X_DMA_CH_2_3_CFG_RDMA_4 0x009E +#define WCD934X_DMA_CH_0_1_CFG_RDMA_4 0x009F +#define WCD934X_DMA_RDMA4_PRT_CFG 0x00b1 +#define WCD934X_DMA_RDMA_SBTX0_7_CFG 0x00b9 +#define WCD934X_DMA_RDMA_SBTX8_11_CFG 0x00ba +#define WCD934X_DMA_WDMA_CTL_0 0x00c1 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_0 0x00c2 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_0 0x00c3 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_0 0x00c4 +#define WCD934X_DMA_WDMA_CTL_1 0x00C6 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_1 0x00C7 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_1 0x00C8 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_1 0x00C9 +#define WCD934X_DMA_WDMA_CTL_2 0x00CB +#define WCD934X_DMA_CH_4_5_CFG_WDMA_2 0x00CC +#define WCD934X_DMA_CH_2_3_CFG_WDMA_2 0x00CD +#define WCD934X_DMA_CH_0_1_CFG_WDMA_2 0x00CE +#define WCD934X_DMA_WDMA_CTL_3 0x00D0 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_3 0x00D1 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_3 0x00D2 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_3 0x00D3 +#define WCD934X_DMA_WDMA_CTL_4 0x00D5 +#define WCD934X_DMA_CH_4_5_CFG_WDMA_4 0x00D6 +#define WCD934X_DMA_CH_2_3_CFG_WDMA_4 0x00D7 +#define WCD934X_DMA_CH_0_1_CFG_WDMA_4 0x00D8 +#define WCD934X_DMA_WDMA0_PRT_CFG 0x00E1 +#define WCD934X_DMA_WDMA3_PRT_CFG 0x00E2 +#define WCD934X_DMA_WDMA4_PRT0_3_CFG 0x00E3 +#define WCD934X_DMA_WDMA4_PRT4_7_CFG 0x00E4 +#define WCD934X_PAGE1_PAGE_REGISTER 0x0100 +#define WCD934X_CPE_FLL_USER_CTL_0 0x0101 +#define WCD934X_CPE_FLL_USER_CTL_1 0x0102 +#define WCD934X_CPE_FLL_USER_CTL_2 0x0103 +#define WCD934X_CPE_FLL_USER_CTL_3 0x0104 +#define WCD934X_CPE_FLL_USER_CTL_4 0x0105 +#define WCD934X_CPE_FLL_USER_CTL_5 0x0106 +#define WCD934X_CPE_FLL_USER_CTL_6 0x0107 +#define WCD934X_CPE_FLL_USER_CTL_7 0x0108 +#define WCD934X_CPE_FLL_USER_CTL_8 0x0109 +#define WCD934X_CPE_FLL_USER_CTL_9 0x010a +#define WCD934X_CPE_FLL_L_VAL_CTL_0 0x010b +#define WCD934X_CPE_FLL_L_VAL_CTL_1 0x010c +#define WCD934X_CPE_FLL_DSM_FRAC_CTL_0 0x010d +#define WCD934X_CPE_FLL_DSM_FRAC_CTL_1 0x010e +#define WCD934X_CPE_FLL_CONFIG_CTL_0 0x010f +#define WCD934X_CPE_FLL_CONFIG_CTL_1 0x0110 +#define WCD934X_CPE_FLL_CONFIG_CTL_2 0x0111 +#define WCD934X_CPE_FLL_CONFIG_CTL_3 0x0112 +#define WCD934X_CPE_FLL_CONFIG_CTL_4 0x0113 +#define WCD934X_CPE_FLL_TEST_CTL_0 0x0114 +#define WCD934X_CPE_FLL_TEST_CTL_1 0x0115 +#define WCD934X_CPE_FLL_TEST_CTL_2 0x0116 +#define WCD934X_CPE_FLL_TEST_CTL_3 0x0117 +#define WCD934X_CPE_FLL_TEST_CTL_4 0x0118 +#define WCD934X_CPE_FLL_TEST_CTL_5 0x0119 +#define WCD934X_CPE_FLL_TEST_CTL_6 0x011a +#define WCD934X_CPE_FLL_TEST_CTL_7 0x011b +#define WCD934X_CPE_FLL_FREQ_CTL_0 0x011c +#define WCD934X_CPE_FLL_FREQ_CTL_1 0x011d +#define WCD934X_CPE_FLL_FREQ_CTL_2 0x011e +#define WCD934X_CPE_FLL_FREQ_CTL_3 0x011f +#define WCD934X_CPE_FLL_SSC_CTL_0 0x0120 +#define WCD934X_CPE_FLL_SSC_CTL_1 0x0121 +#define WCD934X_CPE_FLL_SSC_CTL_2 0x0122 +#define WCD934X_CPE_FLL_SSC_CTL_3 0x0123 +#define WCD934X_CPE_FLL_FLL_MODE 0x0124 +#define WCD934X_CPE_FLL_STATUS_0 0x0125 +#define WCD934X_CPE_FLL_STATUS_1 0x0126 +#define WCD934X_CPE_FLL_STATUS_2 0x0127 +#define WCD934X_CPE_FLL_STATUS_3 0x0128 +#define WCD934X_I2S_FLL_USER_CTL_0 0x0141 +#define WCD934X_I2S_FLL_USER_CTL_1 0x0142 +#define WCD934X_I2S_FLL_USER_CTL_2 0x0143 +#define WCD934X_I2S_FLL_USER_CTL_3 0x0144 +#define WCD934X_I2S_FLL_USER_CTL_4 0x0145 +#define WCD934X_I2S_FLL_USER_CTL_5 0x0146 +#define WCD934X_I2S_FLL_USER_CTL_6 0x0147 +#define WCD934X_I2S_FLL_USER_CTL_7 0x0148 +#define WCD934X_I2S_FLL_USER_CTL_8 0x0149 +#define WCD934X_I2S_FLL_USER_CTL_9 0x014a +#define WCD934X_I2S_FLL_L_VAL_CTL_0 0x014b +#define WCD934X_I2S_FLL_L_VAL_CTL_1 0x014c +#define WCD934X_I2S_FLL_DSM_FRAC_CTL_0 0x014d +#define WCD934X_I2S_FLL_DSM_FRAC_CTL_1 0x014e +#define WCD934X_I2S_FLL_CONFIG_CTL_0 0x014f +#define WCD934X_I2S_FLL_CONFIG_CTL_1 0x0150 +#define WCD934X_I2S_FLL_CONFIG_CTL_2 0x0151 +#define WCD934X_I2S_FLL_CONFIG_CTL_3 0x0152 +#define WCD934X_I2S_FLL_CONFIG_CTL_4 0x0153 +#define WCD934X_I2S_FLL_TEST_CTL_0 0x0154 +#define WCD934X_I2S_FLL_TEST_CTL_1 0x0155 +#define WCD934X_I2S_FLL_TEST_CTL_2 0x0156 +#define WCD934X_I2S_FLL_TEST_CTL_3 0x0157 +#define WCD934X_I2S_FLL_TEST_CTL_4 0x0158 +#define WCD934X_I2S_FLL_TEST_CTL_5 0x0159 +#define WCD934X_I2S_FLL_TEST_CTL_6 0x015a +#define WCD934X_I2S_FLL_TEST_CTL_7 0x015b +#define WCD934X_I2S_FLL_FREQ_CTL_0 0x015c +#define WCD934X_I2S_FLL_FREQ_CTL_1 0x015d +#define WCD934X_I2S_FLL_FREQ_CTL_2 0x015e +#define WCD934X_I2S_FLL_FREQ_CTL_3 0x015f +#define WCD934X_I2S_FLL_SSC_CTL_0 0x0160 +#define WCD934X_I2S_FLL_SSC_CTL_1 0x0161 +#define WCD934X_I2S_FLL_SSC_CTL_2 0x0162 +#define WCD934X_I2S_FLL_SSC_CTL_3 0x0163 +#define WCD934X_I2S_FLL_FLL_MODE 0x0164 +#define WCD934X_I2S_FLL_STATUS_0 0x0165 +#define WCD934X_I2S_FLL_STATUS_1 0x0166 +#define WCD934X_I2S_FLL_STATUS_2 0x0167 +#define WCD934X_I2S_FLL_STATUS_3 0x0168 +#define WCD934X_SB_FLL_USER_CTL_0 0x0181 +#define WCD934X_SB_FLL_USER_CTL_1 0x0182 +#define WCD934X_SB_FLL_USER_CTL_2 0x0183 +#define WCD934X_SB_FLL_USER_CTL_3 0x0184 +#define WCD934X_SB_FLL_USER_CTL_4 0x0185 +#define WCD934X_SB_FLL_USER_CTL_5 0x0186 +#define WCD934X_SB_FLL_USER_CTL_6 0x0187 +#define WCD934X_SB_FLL_USER_CTL_7 0x0188 +#define WCD934X_SB_FLL_USER_CTL_8 0x0189 +#define WCD934X_SB_FLL_USER_CTL_9 0x018a +#define WCD934X_SB_FLL_L_VAL_CTL_0 0x018b +#define WCD934X_SB_FLL_L_VAL_CTL_1 0x018c +#define WCD934X_SB_FLL_DSM_FRAC_CTL_0 0x018d +#define WCD934X_SB_FLL_DSM_FRAC_CTL_1 0x018e +#define WCD934X_SB_FLL_CONFIG_CTL_0 0x018f +#define WCD934X_SB_FLL_CONFIG_CTL_1 0x0190 +#define WCD934X_SB_FLL_CONFIG_CTL_2 0x0191 +#define WCD934X_SB_FLL_CONFIG_CTL_3 0x0192 +#define WCD934X_SB_FLL_CONFIG_CTL_4 0x0193 +#define WCD934X_SB_FLL_TEST_CTL_0 0x0194 +#define WCD934X_SB_FLL_TEST_CTL_1 0x0195 +#define WCD934X_SB_FLL_TEST_CTL_2 0x0196 +#define WCD934X_SB_FLL_TEST_CTL_3 0x0197 +#define WCD934X_SB_FLL_TEST_CTL_4 0x0198 +#define WCD934X_SB_FLL_TEST_CTL_5 0x0199 +#define WCD934X_SB_FLL_TEST_CTL_6 0x019a +#define WCD934X_SB_FLL_TEST_CTL_7 0x019b +#define WCD934X_SB_FLL_FREQ_CTL_0 0x019c +#define WCD934X_SB_FLL_FREQ_CTL_1 0x019d +#define WCD934X_SB_FLL_FREQ_CTL_2 0x019e +#define WCD934X_SB_FLL_FREQ_CTL_3 0x019f +#define WCD934X_SB_FLL_SSC_CTL_0 0x01a0 +#define WCD934X_SB_FLL_SSC_CTL_1 0x01a1 +#define WCD934X_SB_FLL_SSC_CTL_2 0x01a2 +#define WCD934X_SB_FLL_SSC_CTL_3 0x01a3 +#define WCD934X_SB_FLL_FLL_MODE 0x01a4 +#define WCD934X_SB_FLL_STATUS_0 0x01a5 +#define WCD934X_SB_FLL_STATUS_1 0x01a6 +#define WCD934X_SB_FLL_STATUS_2 0x01a7 +#define WCD934X_SB_FLL_STATUS_3 0x01a8 +#define WCD934X_PAGE2_PAGE_REGISTER 0x0200 +#define WCD934X_CPE_SS_CPE_CTL 0x0201 +#define WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0 0x0202 +#define WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1 0x0203 +#define WCD934X_CPE_SS_PWR_CPEFLL_CTL 0x0204 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0 0x0205 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1 0x0206 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE 0x0207 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0 0x0208 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1 0x0209 +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2 0x020a +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3 0x020b +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4 0x020c +#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5 0x020d +#define WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN 0x020e +#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL 0x020f +#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL 0x0210 +#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1 0x0211 +#define WCD934X_CPE_SS_US_BUF_INT_PERIOD 0x0212 +#define WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD 0x0213 +#define WCD934X_CPE_SS_SVA_CFG 0x0214 +#define WCD934X_CPE_SS_US_CFG 0x0215 +#define WCD934X_CPE_SS_MAD_CTL 0x0216 +#define WCD934X_CPE_SS_CPAR_CTL 0x0217 +#define WCD934X_CPE_SS_DMIC0_CTL 0x0218 +#define WCD934X_CPE_SS_DMIC1_CTL 0x0219 +#define WCD934X_CPE_SS_DMIC2_CTL 0x021a +#define WCD934X_CPE_SS_DMIC_CFG 0x021b +#define WCD934X_CPE_SS_CPAR_CFG 0x021c +#define WCD934X_CPE_SS_WDOG_CFG 0x021d +#define WCD934X_CPE_SS_BACKUP_INT 0x021e +#define WCD934X_CPE_SS_STATUS 0x021f +#define WCD934X_CPE_SS_CPE_OCD_CFG 0x0220 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A 0x0221 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B 0x0222 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A 0x0223 +#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B 0x0224 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A 0x0225 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B 0x0226 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A 0x0227 +#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B 0x0228 +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A 0x0229 +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B 0x022a +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A 0x022b +#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B 0x022c +#define WCD934X_SOC_MAD_MAIN_CTL_1 0x0281 +#define WCD934X_SOC_MAD_MAIN_CTL_2 0x0282 +#define WCD934X_SOC_MAD_AUDIO_CTL_1 0x0283 +#define WCD934X_SOC_MAD_AUDIO_CTL_2 0x0284 +#define WCD934X_SOC_MAD_AUDIO_CTL_3 0x0285 +#define WCD934X_SOC_MAD_AUDIO_CTL_4 0x0286 +#define WCD934X_SOC_MAD_AUDIO_CTL_5 0x0287 +#define WCD934X_SOC_MAD_AUDIO_CTL_6 0x0288 +#define WCD934X_SOC_MAD_AUDIO_CTL_7 0x0289 +#define WCD934X_SOC_MAD_AUDIO_CTL_8 0x028a +#define WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR 0x028b +#define WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL 0x028c +#define WCD934X_SOC_MAD_ULTR_CTL_1 0x028d +#define WCD934X_SOC_MAD_ULTR_CTL_2 0x028e +#define WCD934X_SOC_MAD_ULTR_CTL_3 0x028f +#define WCD934X_SOC_MAD_ULTR_CTL_4 0x0290 +#define WCD934X_SOC_MAD_ULTR_CTL_5 0x0291 +#define WCD934X_SOC_MAD_ULTR_CTL_6 0x0292 +#define WCD934X_SOC_MAD_ULTR_CTL_7 0x0293 +#define WCD934X_SOC_MAD_BEACON_CTL_1 0x0294 +#define WCD934X_SOC_MAD_BEACON_CTL_2 0x0295 +#define WCD934X_SOC_MAD_BEACON_CTL_3 0x0296 +#define WCD934X_SOC_MAD_BEACON_CTL_4 0x0297 +#define WCD934X_SOC_MAD_BEACON_CTL_5 0x0298 +#define WCD934X_SOC_MAD_BEACON_CTL_6 0x0299 +#define WCD934X_SOC_MAD_BEACON_CTL_7 0x029a +#define WCD934X_SOC_MAD_BEACON_CTL_8 0x029b +#define WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR 0x029c +#define WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL 0x029d +#define WCD934X_SOC_MAD_INP_SEL 0x029e +#define WCD934X_PAGE4_PAGE_REGISTER 0x0400 +#define WCD934X_INTR_CFG 0x0401 +#define WCD934X_INTR_CLR_COMMIT 0x0402 +#define WCD934X_INTR_PIN1_MASK0 0x0409 +#define WCD934X_INTR_PIN1_MASK1 0x040a +#define WCD934X_INTR_PIN1_MASK2 0x040b +#define WCD934X_INTR_PIN1_MASK3 0x040c +#define WCD934X_INTR_PIN1_STATUS0 0x0411 +#define WCD934X_INTR_PIN1_STATUS1 0x0412 +#define WCD934X_INTR_PIN1_STATUS2 0x0413 +#define WCD934X_INTR_PIN1_STATUS3 0x0414 +#define WCD934X_INTR_PIN1_CLEAR0 0x0419 +#define WCD934X_INTR_PIN1_CLEAR1 0x041a +#define WCD934X_INTR_PIN1_CLEAR2 0x041b +#define WCD934X_INTR_PIN1_CLEAR3 0x041c +#define WCD934X_INTR_PIN2_MASK3 0x0424 +#define WCD934X_INTR_PIN2_STATUS3 0x042c +#define WCD934X_INTR_PIN2_CLEAR3 0x0434 +#define WCD934X_INTR_CPESS_SUMRY_MASK2 0x043b +#define WCD934X_INTR_CPESS_SUMRY_MASK3 0x043c +#define WCD934X_INTR_CPESS_SUMRY_STATUS2 0x0443 +#define WCD934X_INTR_CPESS_SUMRY_STATUS3 0x0444 +#define WCD934X_INTR_CPESS_SUMRY_CLEAR2 0x044b +#define WCD934X_INTR_CPESS_SUMRY_CLEAR3 0x044c +#define WCD934X_INTR_LEVEL0 0x0461 +#define WCD934X_INTR_LEVEL1 0x0462 +#define WCD934X_INTR_LEVEL2 0x0463 +#define WCD934X_INTR_LEVEL3 0x0464 +#define WCD934X_INTR_BYPASS0 0x0469 +#define WCD934X_INTR_BYPASS1 0x046a +#define WCD934X_INTR_BYPASS2 0x046b +#define WCD934X_INTR_BYPASS3 0x046c +#define WCD934X_INTR_SET0 0x0471 +#define WCD934X_INTR_SET1 0x0472 +#define WCD934X_INTR_SET2 0x0473 +#define WCD934X_INTR_SET3 0x0474 +#define WCD934X_INTR_CODEC_MISC_MASK 0x04b1 +#define WCD934X_INTR_CODEC_MISC_STATUS 0x04b2 +#define WCD934X_INTR_CODEC_MISC_CLEAR 0x04b3 +#define WCD934X_PAGE5_PAGE_REGISTER 0x0500 +#define WCD934X_SLNQ_DIG_DEVICE 0x0501 +#define WCD934X_SLNQ_DIG_REVISION 0x0502 +#define WCD934X_SLNQ_DIG_H_COMMAND 0x0511 +#define WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB 0x0512 +#define WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB 0x0513 +#define WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB 0x0514 +#define WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB 0x0515 +#define WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB 0x0516 +#define WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB 0x0517 +#define WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB 0x0518 +#define WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB 0x0519 +#define WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB 0x051a +#define WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB 0x051b +#define WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB 0x051c +#define WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB 0x051d +#define WCD934X_SLNQ_DIG_COMM_CTL 0x0520 +#define WCD934X_SLNQ_DIG_FRAME_CTRL 0x0542 +#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2 0x055c +#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4 0x055d +#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5 0x055e +#define WCD934X_SLNQ_DIG_SW_EVENT_RD 0x0561 +#define WCD934X_SLNQ_DIG_SW_EVENT_CTRL 0x0562 +#define WCD934X_SLNQ_DIG_PDM_SELECT_1 0x0563 +#define WCD934X_SLNQ_DIG_PDM_SELECT_2 0x0564 +#define WCD934X_SLNQ_DIG_PDM_SELECT_3 0x0565 +#define WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ 0x0566 +#define WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL 0x0569 +#define WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL 0x056a +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB 0x056b +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB 0x056c +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB 0x056d +#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB 0x056e +#define WCD934X_SLNQ_DIG_RAM_CNTRL 0x0571 +#define WCD934X_SLNQ_DIG_SRAM_BANK 0x0572 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_0 0x0573 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1 0x0574 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2 0x0575 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3 0x0576 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_4 0x0577 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_5 0x0578 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_6 0x0579 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_7 0x057a +#define WCD934X_SLNQ_DIG_SRAM_BYTE_8 0x057b +#define WCD934X_SLNQ_DIG_SRAM_BYTE_9 0x057c +#define WCD934X_SLNQ_DIG_SRAM_BYTE_A 0x057d +#define WCD934X_SLNQ_DIG_SRAM_BYTE_B 0x057e +#define WCD934X_SLNQ_DIG_SRAM_BYTE_C 0x057f +#define WCD934X_SLNQ_DIG_SRAM_BYTE_D 0x0580 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_E 0x0581 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_F 0x0582 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_10 0x0583 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_11 0x0584 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_12 0x0585 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_13 0x0586 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_14 0x0587 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_15 0x0588 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_16 0x0589 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_17 0x058a +#define WCD934X_SLNQ_DIG_SRAM_BYTE_18 0x058b +#define WCD934X_SLNQ_DIG_SRAM_BYTE_19 0x058c +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1A 0x058d +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1B 0x058e +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1C 0x058f +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1D 0x0590 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1E 0x0591 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_1F 0x0592 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_20 0x0593 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_21 0x0594 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_22 0x0595 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_23 0x0596 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_24 0x0597 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_25 0x0598 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_26 0x0599 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_27 0x059a +#define WCD934X_SLNQ_DIG_SRAM_BYTE_28 0x059b +#define WCD934X_SLNQ_DIG_SRAM_BYTE_29 0x059c +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2A 0x059d +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2B 0x059e +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2C 0x059f +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2D 0x05a0 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2E 0x05a1 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_2F 0x05a2 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_30 0x05a3 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_31 0x05a4 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_32 0x05a5 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_33 0x05a6 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_34 0x05a7 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_35 0x05a8 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_36 0x05a9 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_37 0x05aa +#define WCD934X_SLNQ_DIG_SRAM_BYTE_38 0x05ab +#define WCD934X_SLNQ_DIG_SRAM_BYTE_39 0x05ac +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3A 0x05ad +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3B 0x05ae +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3C 0x05af +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3D 0x05b0 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3E 0x05b1 +#define WCD934X_SLNQ_DIG_SRAM_BYTE_3F 0x05b2 +#define WCD934X_SLNQ_DIG_TOP_CTRL1 0x05b3 +#define WCD934X_SLNQ_DIG_TOP_CTRL2 0x05b4 +#define WCD934X_SLNQ_DIG_PDM_CTRL 0x05b5 +#define WCD934X_SLNQ_DIG_PDM_MUTE_CTRL 0x05b6 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL 0x05b7 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS 0x05b8 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_FS 0x05b9 +#define WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL 0x05ba +#define WCD934X_SLNQ_DIG_GPOUT_ENABLE 0x05bb +#define WCD934X_SLNQ_DIG_GPOUT_VAL 0x05bc +#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK 0x05be +#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS 0x05bf +#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR 0x05c0 +#define WCD934X_SLNQ_DIG_IP_TESTING 0x05c1 +#define WCD934X_SLNQ_DIG_INTERRUPT_CNTRL 0x05e3 +#define WCD934X_SLNQ_DIG_INTERRUPT_CNT 0x05e9 +#define WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB 0x05eb +#define WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB 0x05ec +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK0 0x05f1 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK1 0x05f2 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK2 0x05f3 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK3 0x05f4 +#define WCD934X_SLNQ_DIG_INTERRUPT_MASK4 0x05f5 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS0 0x05f6 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS1 0x05f7 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS2 0x05f8 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS3 0x05f9 +#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS4 0x05fa +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR0 0x05fb +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR1 0x05fc +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR2 0x05fd +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR3 0x05fe +#define WCD934X_SLNQ_DIG_INTERRUPT_CLR4 0x05ff +#define WCD934X_ANA_PAGE_REGISTER 0x0600 +#define WCD934X_ANA_BIAS 0x0601 +#define WCD934X_ANA_RCO 0x0603 +#define WCD934X_ANA_PAGE6_SPARE2 0x0604 +#define WCD934X_ANA_PAGE6_SPARE3 0x0605 +#define WCD934X_ANA_BUCK_CTL 0x0606 +#define WCD934X_ANA_BUCK_STATUS 0x0607 +#define WCD934X_ANA_RX_SUPPLIES 0x0608 +#define WCD934X_ANA_HPH 0x0609 +#define WCD934X_ANA_EAR 0x060a +#define WCD934X_ANA_LO_1_2 0x060b +#define WCD934X_ANA_MAD_SETUP 0x060d +#define WCD934X_ANA_AMIC1 0x060e +#define WCD934X_ANA_AMIC2 0x060f +#define WCD934X_ANA_AMIC3 0x0610 +#define WCD934X_ANA_AMIC4 0x0611 +#define WCD934X_ANA_MBHC_MECH 0x0614 +#define WCD934X_ANA_MBHC_ELECT 0x0615 +#define WCD934X_ANA_MBHC_ZDET 0x0616 +#define WCD934X_ANA_MBHC_RESULT_1 0x0617 +#define WCD934X_ANA_MBHC_RESULT_2 0x0618 +#define WCD934X_ANA_MBHC_RESULT_3 0x0619 +#define WCD934X_ANA_MBHC_BTN0 0x061a +#define WCD934X_ANA_MBHC_BTN1 0x061b +#define WCD934X_ANA_MBHC_BTN2 0x061c +#define WCD934X_ANA_MBHC_BTN3 0x061d +#define WCD934X_ANA_MBHC_BTN4 0x061e +#define WCD934X_ANA_MBHC_BTN5 0x061f +#define WCD934X_ANA_MBHC_BTN6 0x0620 +#define WCD934X_ANA_MBHC_BTN7 0x0621 +#define WCD934X_ANA_MICB1 0x0622 +#define WCD934X_ANA_MICB2 0x0623 +#define WCD934X_ANA_MICB2_RAMP 0x0624 +#define WCD934X_ANA_MICB3 0x0625 +#define WCD934X_ANA_MICB4 0x0626 +#define WCD934X_ANA_VBADC 0x0627 +#define WCD934X_BIAS_CTL 0x0628 +#define WCD934X_BIAS_VBG_FINE_ADJ 0x0629 +#define WCD934X_RCO_CTRL_1 0x062e +#define WCD934X_RCO_CTRL_2 0x062f +#define WCD934X_RCO_CAL 0x0630 +#define WCD934X_RCO_CAL_1 0x0631 +#define WCD934X_RCO_CAL_2 0x0632 +#define WCD934X_RCO_TEST_CTRL 0x0633 +#define WCD934X_RCO_CAL_OUT_1 0x0634 +#define WCD934X_RCO_CAL_OUT_2 0x0635 +#define WCD934X_RCO_CAL_OUT_3 0x0636 +#define WCD934X_RCO_CAL_OUT_4 0x0637 +#define WCD934X_RCO_CAL_OUT_5 0x0638 +#define WCD934X_SIDO_MODE_1 0x063a +#define WCD934X_SIDO_MODE_2 0x063b +#define WCD934X_SIDO_MODE_3 0x063c +#define WCD934X_SIDO_MODE_4 0x063d +#define WCD934X_SIDO_VCL_1 0x063e +#define WCD934X_SIDO_VCL_2 0x063f +#define WCD934X_SIDO_VCL_3 0x0640 +#define WCD934X_SIDO_CCL_1 0x0641 +#define WCD934X_SIDO_CCL_2 0x0642 +#define WCD934X_SIDO_CCL_3 0x0643 +#define WCD934X_SIDO_CCL_4 0x0644 +#define WCD934X_SIDO_CCL_5 0x0645 +#define WCD934X_SIDO_CCL_6 0x0646 +#define WCD934X_SIDO_CCL_7 0x0647 +#define WCD934X_SIDO_CCL_8 0x0648 +#define WCD934X_SIDO_CCL_9 0x0649 +#define WCD934X_SIDO_CCL_10 0x064a +#define WCD934X_SIDO_FILTER_1 0x064b +#define WCD934X_SIDO_FILTER_2 0x064c +#define WCD934X_SIDO_DRIVER_1 0x064d +#define WCD934X_SIDO_DRIVER_2 0x064e +#define WCD934X_SIDO_DRIVER_3 0x064f +#define WCD934X_SIDO_CAL_CODE_EXT_1 0x0650 +#define WCD934X_SIDO_CAL_CODE_EXT_2 0x0651 +#define WCD934X_SIDO_CAL_CODE_OUT_1 0x0652 +#define WCD934X_SIDO_CAL_CODE_OUT_2 0x0653 +#define WCD934X_SIDO_TEST_1 0x0654 +#define WCD934X_SIDO_TEST_2 0x0655 +#define WCD934X_MBHC_CTL_CLK 0x0656 +#define WCD934X_MBHC_CTL_ANA 0x0657 +#define WCD934X_MBHC_CTL_SPARE_1 0x0658 +#define WCD934X_MBHC_CTL_SPARE_2 0x0659 +#define WCD934X_MBHC_CTL_BCS 0x065a +#define WCD934X_MBHC_STATUS_SPARE_1 0x065b +#define WCD934X_MBHC_TEST_CTL 0x065c +#define WCD934X_VBADC_SUBBLOCK_EN 0x065d +#define WCD934X_VBADC_IBIAS_FE 0x065e +#define WCD934X_VBADC_BIAS_ADC 0x065f +#define WCD934X_VBADC_FE_CTRL 0x0660 +#define WCD934X_VBADC_ADC_REF 0x0661 +#define WCD934X_VBADC_ADC_IO 0x0662 +#define WCD934X_VBADC_ADC_SAR 0x0663 +#define WCD934X_VBADC_DEBUG 0x0664 +#define WCD934X_LDOH_MODE 0x0667 +#define WCD934X_LDOH_BIAS 0x0668 +#define WCD934X_LDOH_STB_LOADS 0x0669 +#define WCD934X_LDOH_SLOWRAMP 0x066a +#define WCD934X_MICB1_TEST_CTL_1 0x066b +#define WCD934X_MICB1_TEST_CTL_2 0x066c +#define WCD934X_MICB1_TEST_CTL_3 0x066d +#define WCD934X_MICB2_TEST_CTL_1 0x066e +#define WCD934X_MICB2_TEST_CTL_2 0x066f +#define WCD934X_MICB2_TEST_CTL_3 0x0670 +#define WCD934X_MICB3_TEST_CTL_1 0x0671 +#define WCD934X_MICB3_TEST_CTL_2 0x0672 +#define WCD934X_MICB3_TEST_CTL_3 0x0673 +#define WCD934X_MICB4_TEST_CTL_1 0x0674 +#define WCD934X_MICB4_TEST_CTL_2 0x0675 +#define WCD934X_MICB4_TEST_CTL_3 0x0676 +#define WCD934X_TX_COM_ADC_VCM 0x0677 +#define WCD934X_TX_COM_BIAS_ATEST 0x0678 +#define WCD934X_TX_COM_ADC_INT1_IB 0x0679 +#define WCD934X_TX_COM_ADC_INT2_IB 0x067a +#define WCD934X_TX_COM_TXFE_DIV_CTL 0x067b +#define WCD934X_TX_COM_TXFE_DIV_START 0x067c +#define WCD934X_TX_COM_TXFE_DIV_STOP_9P6M 0x067d +#define WCD934X_TX_COM_TXFE_DIV_STOP_12P288M 0x067e +#define WCD934X_TX_1_2_TEST_EN 0x067f +#define WCD934X_TX_1_2_ADC_IB 0x0680 +#define WCD934X_TX_1_2_ATEST_REFCTL 0x0681 +#define WCD934X_TX_1_2_TEST_CTL 0x0682 +#define WCD934X_TX_1_2_TEST_BLK_EN 0x0683 +#define WCD934X_TX_1_2_TXFE_CLKDIV 0x0684 +#define WCD934X_TX_1_2_SAR1_ERR 0x0685 +#define WCD934X_TX_1_2_SAR2_ERR 0x0686 +#define WCD934X_TX_3_4_TEST_EN 0x0687 +#define WCD934X_TX_3_4_ADC_IB 0x0688 +#define WCD934X_TX_3_4_ATEST_REFCTL 0x0689 +#define WCD934X_TX_3_4_TEST_CTL 0x068a +#define WCD934X_TX_3_4_TEST_BLK_EN 0x068b +#define WCD934X_TX_3_4_TXFE_CLKDIV 0x068c +#define WCD934X_TX_3_4_SAR1_ERR 0x068d +#define WCD934X_TX_3_4_SAR2_ERR 0x068e +#define WCD934X_CLASSH_MODE_1 0x0697 +#define WCD934X_CLASSH_MODE_2 0x0698 +#define WCD934X_CLASSH_MODE_3 0x0699 +#define WCD934X_CLASSH_CTRL_VCL_1 0x069a +#define WCD934X_CLASSH_CTRL_VCL_2 0x069b +#define WCD934X_CLASSH_CTRL_CCL_1 0x069c +#define WCD934X_CLASSH_CTRL_CCL_2 0x069d +#define WCD934X_CLASSH_CTRL_CCL_3 0x069e +#define WCD934X_CLASSH_CTRL_CCL_4 0x069f +#define WCD934X_CLASSH_CTRL_CCL_5 0x06a0 +#define WCD934X_CLASSH_BUCK_TMUX_A_D 0x06a1 +#define WCD934X_CLASSH_BUCK_SW_DRV_CNTL 0x06a2 +#define WCD934X_CLASSH_SPARE 0x06a3 +#define WCD934X_FLYBACK_EN 0x06a4 +#define WCD934X_FLYBACK_VNEG_CTRL_1 0x06a5 +#define WCD934X_FLYBACK_VNEG_CTRL_2 0x06a6 +#define WCD934X_FLYBACK_VNEG_CTRL_3 0x06a7 +#define WCD934X_FLYBACK_VNEG_CTRL_4 0x06a8 +#define WCD934X_FLYBACK_VNEG_CTRL_5 0x06a9 +#define WCD934X_FLYBACK_VNEG_CTRL_6 0x06aa +#define WCD934X_FLYBACK_VNEG_CTRL_7 0x06ab +#define WCD934X_FLYBACK_VNEG_CTRL_8 0x06ac +#define WCD934X_FLYBACK_VNEG_CTRL_9 0x06ad +#define WCD934X_FLYBACK_VNEGDAC_CTRL_1 0x06ae +#define WCD934X_FLYBACK_VNEGDAC_CTRL_2 0x06af +#define WCD934X_FLYBACK_VNEGDAC_CTRL_3 0x06b0 +#define WCD934X_FLYBACK_CTRL_1 0x06b1 +#define WCD934X_FLYBACK_TEST_CTL 0x06b2 +#define WCD934X_RX_AUX_SW_CTL 0x06b3 +#define WCD934X_RX_PA_AUX_IN_CONN 0x06b4 +#define WCD934X_RX_TIMER_DIV 0x06b5 +#define WCD934X_RX_OCP_CTL 0x06b6 +#define WCD934X_RX_OCP_COUNT 0x06b7 +#define WCD934X_RX_BIAS_EAR_DAC 0x06b8 +#define WCD934X_RX_BIAS_EAR_AMP 0x06b9 +#define WCD934X_RX_BIAS_HPH_LDO 0x06ba +#define WCD934X_RX_BIAS_HPH_PA 0x06bb +#define WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2 0x06bc +#define WCD934X_RX_BIAS_HPH_RDAC_LDO 0x06bd +#define WCD934X_RX_BIAS_HPH_CNP1 0x06be +#define WCD934X_RX_BIAS_HPH_LOWPOWER 0x06bf +#define WCD934X_RX_BIAS_DIFFLO_PA 0x06c0 +#define WCD934X_RX_BIAS_DIFFLO_REF 0x06c1 +#define WCD934X_RX_BIAS_DIFFLO_LDO 0x06c2 +#define WCD934X_RX_BIAS_SELO_DAC_PA 0x06c3 +#define WCD934X_RX_BIAS_BUCK_RST 0x06c4 +#define WCD934X_RX_BIAS_BUCK_VREF_ERRAMP 0x06c5 +#define WCD934X_RX_BIAS_FLYB_ERRAMP 0x06c6 +#define WCD934X_RX_BIAS_FLYB_BUFF 0x06c7 +#define WCD934X_RX_BIAS_FLYB_MID_RST 0x06c8 +#define WCD934X_HPH_L_STATUS 0x06c9 +#define WCD934X_HPH_R_STATUS 0x06ca +#define WCD934X_HPH_CNP_EN 0x06cb +#define WCD934X_HPH_CNP_WG_CTL 0x06cc +#define WCD934X_HPH_CNP_WG_TIME 0x06cd +#define WCD934X_HPH_OCP_CTL 0x06ce +#define WCD934X_HPH_AUTO_CHOP 0x06cf +#define WCD934X_HPH_CHOP_CTL 0x06d0 +#define WCD934X_HPH_PA_CTL1 0x06d1 +#define WCD934X_HPH_PA_CTL2 0x06d2 +#define WCD934X_HPH_L_EN 0x06d3 +#define WCD934X_HPH_L_TEST 0x06d4 +#define WCD934X_HPH_L_ATEST 0x06d5 +#define WCD934X_HPH_R_EN 0x06d6 +#define WCD934X_HPH_R_TEST 0x06d7 +#define WCD934X_HPH_R_ATEST 0x06d8 +#define WCD934X_HPH_RDAC_CLK_CTL1 0x06d9 +#define WCD934X_HPH_RDAC_CLK_CTL2 0x06da +#define WCD934X_HPH_RDAC_LDO_CTL 0x06db +#define WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL 0x06dc +#define WCD934X_HPH_REFBUFF_UHQA_CTL 0x06dd +#define WCD934X_HPH_REFBUFF_LP_CTL 0x06de +#define WCD934X_HPH_L_DAC_CTL 0x06df +#define WCD934X_HPH_R_DAC_CTL 0x06e0 +#define WCD934X_EAR_EN_REG 0x06e1 +#define WCD934X_EAR_CMBUFF 0x06e2 +#define WCD934X_EAR_ICTL 0x06e3 +#define WCD934X_EAR_EN_DBG_CTL 0x06e4 +#define WCD934X_EAR_CNP 0x06e5 +#define WCD934X_EAR_DAC_CTL_ATEST 0x06e6 +#define WCD934X_EAR_STATUS_REG 0x06e7 +#define WCD934X_EAR_EAR_MISC 0x06e8 +#define WCD934X_DIFF_LO_MISC 0x06e9 +#define WCD934X_DIFF_LO_LO2_COMPANDER 0x06ea +#define WCD934X_DIFF_LO_LO1_COMPANDER 0x06eb +#define WCD934X_DIFF_LO_COMMON 0x06ec +#define WCD934X_DIFF_LO_BYPASS_EN 0x06ed +#define WCD934X_DIFF_LO_CNP 0x06ee +#define WCD934X_DIFF_LO_CORE_OUT_PROG 0x06ef +#define WCD934X_DIFF_LO_LDO_OUT_PROG 0x06f0 +#define WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ 0x06f1 +#define WCD934X_DIFF_LO_COM_PA_FREQ 0x06f2 +#define WCD934X_DIFF_LO_RESERVED_REG 0x06f3 +#define WCD934X_DIFF_LO_LO1_STATUS_1 0x06f4 +#define WCD934X_DIFF_LO_LO1_STATUS_2 0x06f5 +#define WCD934X_ANA_NEW_PAGE_REGISTER 0x0700 +#define WCD934X_HPH_NEW_ANA_HPH2 0x0701 +#define WCD934X_HPH_NEW_ANA_HPH3 0x0702 +#define WCD934X_SLNQ_ANA_EN 0x0703 +#define WCD934X_SLNQ_ANA_STATUS 0x0704 +#define WCD934X_SLNQ_ANA_LDO_CONFIG 0x0705 +#define WCD934X_SLNQ_ANA_LDO_OCP_CONFIG 0x0706 +#define WCD934X_SLNQ_ANA_TX_LDO_CONFIG 0x0707 +#define WCD934X_SLNQ_ANA_TX_DRV_CONFIG 0x0708 +#define WCD934X_SLNQ_ANA_RX_CONFIG_1 0x0709 +#define WCD934X_SLNQ_ANA_RX_CONFIG_2 0x070a +#define WCD934X_SLNQ_ANA_PLL_ENABLES 0x070b +#define WCD934X_SLNQ_ANA_PLL_PRESET 0x070c +#define WCD934X_SLNQ_ANA_PLL_STATUS 0x070d +#define WCD934X_CLK_SYS_PLL_ENABLES 0x070e +#define WCD934X_CLK_SYS_PLL_PRESET 0x070f +#define WCD934X_CLK_SYS_PLL_STATUS 0x0710 +#define WCD934X_CLK_SYS_MCLK_PRG 0x0711 +#define WCD934X_CLK_SYS_MCLK2_PRG1 0x0712 +#define WCD934X_CLK_SYS_MCLK2_PRG2 0x0713 +#define WCD934X_CLK_SYS_XO_PRG 0x0714 +#define WCD934X_CLK_SYS_XO_CAP_XTP 0x0715 +#define WCD934X_CLK_SYS_XO_CAP_XTM 0x0716 +#define WCD934X_BOOST_BST_EN_DLY 0x0718 +#define WCD934X_BOOST_CTRL_ILIM 0x0719 +#define WCD934X_BOOST_VOUT_SETTING 0x071a +#define WCD934X_SIDO_NEW_VOUT_A_STARTUP 0x071b +#define WCD934X_SIDO_NEW_VOUT_D_STARTUP 0x071c +#define WCD934X_SIDO_NEW_VOUT_D_FREQ1 0x071d +#define WCD934X_SIDO_NEW_VOUT_D_FREQ2 0x071e +#define WCD934X_MBHC_NEW_ELECT_REM_CLAMP_CTL 0x071f +#define WCD934X_MBHC_NEW_CTL_1 0x0720 +#define WCD934X_MBHC_NEW_CTL_2 0x0721 +#define WCD934X_MBHC_NEW_PLUG_DETECT_CTL 0x0722 +#define WCD934X_MBHC_NEW_ZDET_ANA_CTL 0x0723 +#define WCD934X_MBHC_NEW_ZDET_RAMP_CTL 0x0724 +#define WCD934X_MBHC_NEW_FSM_STATUS 0x0725 +#define WCD934X_MBHC_NEW_ADC_RESULT 0x0726 +#define WCD934X_TX_NEW_AMIC_4_5_SEL 0x0727 +#define WCD934X_VBADC_NEW_ADC_MODE 0x072f +#define WCD934X_VBADC_NEW_ADC_DOUTMSB 0x0730 +#define WCD934X_VBADC_NEW_ADC_DOUTLSB 0x0731 +#define WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL 0x0732 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL 0x0733 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L 0x0733 +#define WCD934X_HPH_NEW_INT_RDAC_VREF_CTL 0x0734 +#define WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL 0x0735 +#define WCD934X_HPH_NEW_INT_RDAC_MISC1 0x0736 +#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R 0x0736 +#define WCD934X_HPH_NEW_INT_PA_MISC1 0x0737 +#define WCD934X_HPH_NEW_INT_PA_MISC2 0x0738 +#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC 0x0739 +#define WCD934X_HPH_NEW_INT_HPH_TIMER1 0x073a +#define WCD934X_HPH_NEW_INT_HPH_TIMER2 0x073b +#define WCD934X_HPH_NEW_INT_HPH_TIMER3 0x073c +#define WCD934X_HPH_NEW_INT_HPH_TIMER4 0x073d +#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC2 0x073e +#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC3 0x073f +#define WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI 0x0745 +#define WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP 0x0746 +#define WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP 0x0747 +#define WCD934X_SLNQ_INT_ANA_INT_LDO_TEST 0x074b +#define WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1 0x074c +#define WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2 0x074d +#define WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST 0x074e +#define WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST 0x074f +#define WCD934X_SLNQ_INT_ANA_INT_RX_TEST 0x0750 +#define WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS 0x0751 +#define WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1 0x0752 +#define WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2 0x0753 +#define WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL 0x0754 +#define WCD934X_SLNQ_INT_ANA_INT_RESERVED_1 0x0755 +#define WCD934X_SLNQ_INT_ANA_INT_RESERVED_2 0x0756 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0 0x0757 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1 0x0758 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0 0x0759 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1 0x075a +#define WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0 0x075b +#define WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1 0x075c +#define WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL 0x075d +#define WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL 0x075e +#define WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL 0x075f +#define WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0 0x0760 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG 0x0761 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG 0x0762 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1 0x0763 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG 0x0764 +#define WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG 0x0765 +#define WCD934X_CLK_SYS_INT_POST_DIV_REG0 0x076c +#define WCD934X_CLK_SYS_INT_POST_DIV_REG1 0x076d +#define WCD934X_CLK_SYS_INT_REF_DIV_REG0 0x076e +#define WCD934X_CLK_SYS_INT_REF_DIV_REG1 0x076f +#define WCD934X_CLK_SYS_INT_FILTER_REG0 0x0770 +#define WCD934X_CLK_SYS_INT_FILTER_REG1 0x0771 +#define WCD934X_CLK_SYS_INT_PLL_L_VAL 0x0772 +#define WCD934X_CLK_SYS_INT_PLL_M_VAL 0x0773 +#define WCD934X_CLK_SYS_INT_PLL_N_VAL 0x0774 +#define WCD934X_CLK_SYS_INT_TEST_REG0 0x0775 +#define WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG 0x0776 +#define WCD934X_CLK_SYS_INT_VCO_PROG 0x0777 +#define WCD934X_CLK_SYS_INT_TEST_REG1 0x0778 +#define WCD934X_CLK_SYS_INT_LDO_LOCK_CFG 0x0779 +#define WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG 0x077a +#define WCD934X_CLK_SYS_INT_CLK_TEST1 0x077b +#define WCD934X_CLK_SYS_INT_CLK_TEST2 0x077c +#define WCD934X_CLK_SYS_INT_CLK_TEST3 0x077d +#define WCD934X_CLK_SYS_INT_XO_TEST1 0x077e +#define WCD934X_CLK_SYS_INT_XO_TEST2 0x077f +#define WCD934X_BOOST_INT_VCOMP_HYST 0x0787 +#define WCD934X_BOOST_INT_VLOOP_FILTER 0x0788 +#define WCD934X_BOOST_INT_CTRL_IDELTA 0x0789 +#define WCD934X_BOOST_INT_CTRL_ILIM_STARTUP 0x078a +#define WCD934X_BOOST_INT_CTRL_MIN_ONTIME 0x078b +#define WCD934X_BOOST_INT_CTRL_MAX_ONTIME 0x078c +#define WCD934X_BOOST_INT_CTRL_TIMING 0x078d +#define WCD934X_BOOST_INT_TMUX_A_D 0x078e +#define WCD934X_BOOST_INT_SW_DRV_CNTL 0x078f +#define WCD934X_BOOST_INT_SPARE1 0x0790 +#define WCD934X_BOOST_INT_SPARE2 0x0791 +#define WCD934X_SIDO_NEW_INT_RAMP_STATUS 0x0796 +#define WCD934X_SIDO_NEW_INT_SPARE_1 0x0797 +#define WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A 0x0798 +#define WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D 0x0799 +#define WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT 0x079a +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL 0x079b +#define WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL 0x079c +#define WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST 0x079d +#define WCD934X_SIDO_NEW_INT_RAMP_CTL_A 0x079e +#define WCD934X_SIDO_NEW_INT_RAMP_CTL_D 0x079f +#define WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD 0x07a0 +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1 0x07a1 +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2 0x07a2 +#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3 0x07a3 +#define WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1 0x07a4 +#define WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2 0x07a5 +#define WCD934X_MBHC_NEW_INT_SLNQ_HPF 0x07af +#define WCD934X_MBHC_NEW_INT_SLNQ_REF 0x07b0 +#define WCD934X_MBHC_NEW_INT_SLNQ_COMP 0x07b1 +#define WCD934X_MBHC_NEW_INT_SPARE_2 0x07b2 +#define WCD934X_PAGE10_PAGE_REGISTER 0x0a00 +#define WCD934X_CDC_ANC0_CLK_RESET_CTL 0x0a01 +#define WCD934X_CDC_ANC0_MODE_1_CTL 0x0a02 +#define WCD934X_CDC_ANC0_MODE_2_CTL 0x0a03 +#define WCD934X_CDC_ANC0_FF_SHIFT 0x0a04 +#define WCD934X_CDC_ANC0_FB_SHIFT 0x0a05 +#define WCD934X_CDC_ANC0_LPF_FF_A_CTL 0x0a06 +#define WCD934X_CDC_ANC0_LPF_FF_B_CTL 0x0a07 +#define WCD934X_CDC_ANC0_LPF_FB_CTL 0x0a08 +#define WCD934X_CDC_ANC0_SMLPF_CTL 0x0a09 +#define WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL 0x0a0a +#define WCD934X_CDC_ANC0_IIR_ADAPT_CTL 0x0a0b +#define WCD934X_CDC_ANC0_IIR_COEFF_1_CTL 0x0a0c +#define WCD934X_CDC_ANC0_IIR_COEFF_2_CTL 0x0a0d +#define WCD934X_CDC_ANC0_FF_A_GAIN_CTL 0x0a0e +#define WCD934X_CDC_ANC0_FF_B_GAIN_CTL 0x0a0f +#define WCD934X_CDC_ANC0_FB_GAIN_CTL 0x0a10 +#define WCD934X_CDC_ANC0_RC_COMMON_CTL 0x0a11 +#define WCD934X_CDC_ANC0_FIFO_COMMON_CTL 0x0a13 +#define WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR 0x0a14 +#define WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR 0x0a15 +#define WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR 0x0a16 +#define WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR 0x0a17 +#define WCD934X_CDC_ANC0_STATUS_FIFO 0x0a18 +#define WCD934X_CDC_ANC1_CLK_RESET_CTL 0x0a19 +#define WCD934X_CDC_ANC1_MODE_1_CTL 0x0a1a +#define WCD934X_CDC_ANC1_MODE_2_CTL 0x0a1b +#define WCD934X_CDC_ANC1_FF_SHIFT 0x0a1c +#define WCD934X_CDC_ANC1_FB_SHIFT 0x0a1d +#define WCD934X_CDC_ANC1_LPF_FF_A_CTL 0x0a1e +#define WCD934X_CDC_ANC1_LPF_FF_B_CTL 0x0a1f +#define WCD934X_CDC_ANC1_LPF_FB_CTL 0x0a20 +#define WCD934X_CDC_ANC1_SMLPF_CTL 0x0a21 +#define WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL 0x0a22 +#define WCD934X_CDC_ANC1_IIR_ADAPT_CTL 0x0a23 +#define WCD934X_CDC_ANC1_IIR_COEFF_1_CTL 0x0a24 +#define WCD934X_CDC_ANC1_IIR_COEFF_2_CTL 0x0a25 +#define WCD934X_CDC_ANC1_FF_A_GAIN_CTL 0x0a26 +#define WCD934X_CDC_ANC1_FF_B_GAIN_CTL 0x0a27 +#define WCD934X_CDC_ANC1_FB_GAIN_CTL 0x0a28 +#define WCD934X_CDC_ANC1_RC_COMMON_CTL 0x0a29 +#define WCD934X_CDC_ANC1_FIFO_COMMON_CTL 0x0a2b +#define WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR 0x0a2c +#define WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR 0x0a2d +#define WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR 0x0a2e +#define WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR 0x0a2f +#define WCD934X_CDC_ANC1_STATUS_FIFO 0x0a30 +#define WCD934X_CDC_TX0_TX_PATH_CTL 0x0a31 +#define WCD934X_CDC_TX0_TX_PATH_CFG0 0x0a32 +#define WCD934X_CDC_TX0_TX_PATH_CFG1 0x0a33 +#define WCD934X_CDC_TX0_TX_VOL_CTL 0x0a34 +#define WCD934X_CDC_TX0_TX_PATH_192_CTL 0x0a35 +#define WCD934X_CDC_TX0_TX_PATH_192_CFG 0x0a36 +#define WCD934X_CDC_TX0_TX_PATH_SEC0 0x0a37 +#define WCD934X_CDC_TX0_TX_PATH_SEC1 0x0a38 +#define WCD934X_CDC_TX0_TX_PATH_SEC2 0x0a39 +#define WCD934X_CDC_TX0_TX_PATH_SEC3 0x0a3a +#define WCD934X_CDC_TX0_TX_PATH_SEC4 0x0a3b +#define WCD934X_CDC_TX0_TX_PATH_SEC5 0x0a3c +#define WCD934X_CDC_TX0_TX_PATH_SEC6 0x0a3d +#define WCD934X_CDC_TX0_TX_PATH_SEC7 0x0a3e +#define WCD934X_CDC_TX1_TX_PATH_CTL 0x0a41 +#define WCD934X_CDC_TX1_TX_PATH_CFG0 0x0a42 +#define WCD934X_CDC_TX1_TX_PATH_CFG1 0x0a43 +#define WCD934X_CDC_TX1_TX_VOL_CTL 0x0a44 +#define WCD934X_CDC_TX1_TX_PATH_192_CTL 0x0a45 +#define WCD934X_CDC_TX1_TX_PATH_192_CFG 0x0a46 +#define WCD934X_CDC_TX1_TX_PATH_SEC0 0x0a47 +#define WCD934X_CDC_TX1_TX_PATH_SEC1 0x0a48 +#define WCD934X_CDC_TX1_TX_PATH_SEC2 0x0a49 +#define WCD934X_CDC_TX1_TX_PATH_SEC3 0x0a4a +#define WCD934X_CDC_TX1_TX_PATH_SEC4 0x0a4b +#define WCD934X_CDC_TX1_TX_PATH_SEC5 0x0a4c +#define WCD934X_CDC_TX1_TX_PATH_SEC6 0x0a4d +#define WCD934X_CDC_TX2_TX_PATH_CTL 0x0a51 +#define WCD934X_CDC_TX2_TX_PATH_CFG0 0x0a52 +#define WCD934X_CDC_TX2_TX_PATH_CFG1 0x0a53 +#define WCD934X_CDC_TX2_TX_VOL_CTL 0x0a54 +#define WCD934X_CDC_TX2_TX_PATH_192_CTL 0x0a55 +#define WCD934X_CDC_TX2_TX_PATH_192_CFG 0x0a56 +#define WCD934X_CDC_TX2_TX_PATH_SEC0 0x0a57 +#define WCD934X_CDC_TX2_TX_PATH_SEC1 0x0a58 +#define WCD934X_CDC_TX2_TX_PATH_SEC2 0x0a59 +#define WCD934X_CDC_TX2_TX_PATH_SEC3 0x0a5a +#define WCD934X_CDC_TX2_TX_PATH_SEC4 0x0a5b +#define WCD934X_CDC_TX2_TX_PATH_SEC5 0x0a5c +#define WCD934X_CDC_TX2_TX_PATH_SEC6 0x0a5d +#define WCD934X_CDC_TX3_TX_PATH_CTL 0x0a61 +#define WCD934X_CDC_TX3_TX_PATH_CFG0 0x0a62 +#define WCD934X_CDC_TX3_TX_PATH_CFG1 0x0a63 +#define WCD934X_CDC_TX3_TX_VOL_CTL 0x0a64 +#define WCD934X_CDC_TX3_TX_PATH_192_CTL 0x0a65 +#define WCD934X_CDC_TX3_TX_PATH_192_CFG 0x0a66 +#define WCD934X_CDC_TX3_TX_PATH_SEC0 0x0a67 +#define WCD934X_CDC_TX3_TX_PATH_SEC1 0x0a68 +#define WCD934X_CDC_TX3_TX_PATH_SEC2 0x0a69 +#define WCD934X_CDC_TX3_TX_PATH_SEC3 0x0a6a +#define WCD934X_CDC_TX3_TX_PATH_SEC4 0x0a6b +#define WCD934X_CDC_TX3_TX_PATH_SEC5 0x0a6c +#define WCD934X_CDC_TX3_TX_PATH_SEC6 0x0a6d +#define WCD934X_CDC_TX4_TX_PATH_CTL 0x0a71 +#define WCD934X_CDC_TX4_TX_PATH_CFG0 0x0a72 +#define WCD934X_CDC_TX4_TX_PATH_CFG1 0x0a73 +#define WCD934X_CDC_TX4_TX_VOL_CTL 0x0a74 +#define WCD934X_CDC_TX4_TX_PATH_192_CTL 0x0a75 +#define WCD934X_CDC_TX4_TX_PATH_192_CFG 0x0a76 +#define WCD934X_CDC_TX4_TX_PATH_SEC0 0x0a77 +#define WCD934X_CDC_TX4_TX_PATH_SEC1 0x0a78 +#define WCD934X_CDC_TX4_TX_PATH_SEC2 0x0a79 +#define WCD934X_CDC_TX4_TX_PATH_SEC3 0x0a7a +#define WCD934X_CDC_TX4_TX_PATH_SEC4 0x0a7b +#define WCD934X_CDC_TX4_TX_PATH_SEC5 0x0a7c +#define WCD934X_CDC_TX4_TX_PATH_SEC6 0x0a7d +#define WCD934X_CDC_TX5_TX_PATH_CTL 0x0a81 +#define WCD934X_CDC_TX5_TX_PATH_CFG0 0x0a82 +#define WCD934X_CDC_TX5_TX_PATH_CFG1 0x0a83 +#define WCD934X_CDC_TX5_TX_VOL_CTL 0x0a84 +#define WCD934X_CDC_TX5_TX_PATH_192_CTL 0x0a85 +#define WCD934X_CDC_TX5_TX_PATH_192_CFG 0x0a86 +#define WCD934X_CDC_TX5_TX_PATH_SEC0 0x0a87 +#define WCD934X_CDC_TX5_TX_PATH_SEC1 0x0a88 +#define WCD934X_CDC_TX5_TX_PATH_SEC2 0x0a89 +#define WCD934X_CDC_TX5_TX_PATH_SEC3 0x0a8a +#define WCD934X_CDC_TX5_TX_PATH_SEC4 0x0a8b +#define WCD934X_CDC_TX5_TX_PATH_SEC5 0x0a8c +#define WCD934X_CDC_TX5_TX_PATH_SEC6 0x0a8d +#define WCD934X_CDC_TX6_TX_PATH_CTL 0x0a91 +#define WCD934X_CDC_TX6_TX_PATH_CFG0 0x0a92 +#define WCD934X_CDC_TX6_TX_PATH_CFG1 0x0a93 +#define WCD934X_CDC_TX6_TX_VOL_CTL 0x0a94 +#define WCD934X_CDC_TX6_TX_PATH_192_CTL 0x0a95 +#define WCD934X_CDC_TX6_TX_PATH_192_CFG 0x0a96 +#define WCD934X_CDC_TX6_TX_PATH_SEC0 0x0a97 +#define WCD934X_CDC_TX6_TX_PATH_SEC1 0x0a98 +#define WCD934X_CDC_TX6_TX_PATH_SEC2 0x0a99 +#define WCD934X_CDC_TX6_TX_PATH_SEC3 0x0a9a +#define WCD934X_CDC_TX6_TX_PATH_SEC4 0x0a9b +#define WCD934X_CDC_TX6_TX_PATH_SEC5 0x0a9c +#define WCD934X_CDC_TX6_TX_PATH_SEC6 0x0a9d +#define WCD934X_CDC_TX7_TX_PATH_CTL 0x0aa1 +#define WCD934X_CDC_TX7_TX_PATH_CFG0 0x0aa2 +#define WCD934X_CDC_TX7_TX_PATH_CFG1 0x0aa3 +#define WCD934X_CDC_TX7_TX_VOL_CTL 0x0aa4 +#define WCD934X_CDC_TX7_TX_PATH_192_CTL 0x0aa5 +#define WCD934X_CDC_TX7_TX_PATH_192_CFG 0x0aa6 +#define WCD934X_CDC_TX7_TX_PATH_SEC0 0x0aa7 +#define WCD934X_CDC_TX7_TX_PATH_SEC1 0x0aa8 +#define WCD934X_CDC_TX7_TX_PATH_SEC2 0x0aa9 +#define WCD934X_CDC_TX7_TX_PATH_SEC3 0x0aaa +#define WCD934X_CDC_TX7_TX_PATH_SEC4 0x0aab +#define WCD934X_CDC_TX7_TX_PATH_SEC5 0x0aac +#define WCD934X_CDC_TX7_TX_PATH_SEC6 0x0aad +#define WCD934X_CDC_TX8_TX_PATH_CTL 0x0ab1 +#define WCD934X_CDC_TX8_TX_PATH_CFG0 0x0ab2 +#define WCD934X_CDC_TX8_TX_PATH_CFG1 0x0ab3 +#define WCD934X_CDC_TX8_TX_VOL_CTL 0x0ab4 +#define WCD934X_CDC_TX8_TX_PATH_192_CTL 0x0ab5 +#define WCD934X_CDC_TX8_TX_PATH_192_CFG 0x0ab6 +#define WCD934X_CDC_TX8_TX_PATH_SEC0 0x0ab7 +#define WCD934X_CDC_TX8_TX_PATH_SEC1 0x0ab8 +#define WCD934X_CDC_TX8_TX_PATH_SEC2 0x0ab9 +#define WCD934X_CDC_TX8_TX_PATH_SEC3 0x0aba +#define WCD934X_CDC_TX8_TX_PATH_SEC4 0x0abb +#define WCD934X_CDC_TX8_TX_PATH_SEC5 0x0abc +#define WCD934X_CDC_TX8_TX_PATH_SEC6 0x0abd +#define WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL 0x0ac2 +#define WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0 0x0ac3 +#define WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL 0x0ac6 +#define WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0 0x0ac7 +#define WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL 0x0aca +#define WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0 0x0acb +#define WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL 0x0ace +#define WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0 0x0acf +#define WCD934X_PAGE11_PAGE_REGISTER 0x0b00 +#define WCD934X_CDC_COMPANDER1_CTL0 0x0b01 +#define WCD934X_CDC_COMPANDER1_CTL1 0x0b02 +#define WCD934X_CDC_COMPANDER1_CTL2 0x0b03 +#define WCD934X_CDC_COMPANDER1_CTL3 0x0b04 +#define WCD934X_CDC_COMPANDER1_CTL4 0x0b05 +#define WCD934X_CDC_COMPANDER1_CTL5 0x0b06 +#define WCD934X_CDC_COMPANDER1_CTL6 0x0b07 +#define WCD934X_CDC_COMPANDER1_CTL7 0x0b08 +#define WCD934X_CDC_COMPANDER2_CTL0 0x0b09 +#define WCD934X_CDC_COMPANDER2_CTL1 0x0b0a +#define WCD934X_CDC_COMPANDER2_CTL2 0x0b0b +#define WCD934X_CDC_COMPANDER2_CTL3 0x0b0c +#define WCD934X_CDC_COMPANDER2_CTL4 0x0b0d +#define WCD934X_CDC_COMPANDER2_CTL5 0x0b0e +#define WCD934X_CDC_COMPANDER2_CTL6 0x0b0f +#define WCD934X_CDC_COMPANDER2_CTL7 0x0b10 +#define WCD934X_CDC_COMPANDER3_CTL0 0x0b11 +#define WCD934X_CDC_COMPANDER3_CTL1 0x0b12 +#define WCD934X_CDC_COMPANDER3_CTL2 0x0b13 +#define WCD934X_CDC_COMPANDER3_CTL3 0x0b14 +#define WCD934X_CDC_COMPANDER3_CTL4 0x0b15 +#define WCD934X_CDC_COMPANDER3_CTL5 0x0b16 +#define WCD934X_CDC_COMPANDER3_CTL6 0x0b17 +#define WCD934X_CDC_COMPANDER3_CTL7 0x0b18 +#define WCD934X_CDC_COMPANDER4_CTL0 0x0b19 +#define WCD934X_CDC_COMPANDER4_CTL1 0x0b1a +#define WCD934X_CDC_COMPANDER4_CTL2 0x0b1b +#define WCD934X_CDC_COMPANDER4_CTL3 0x0b1c +#define WCD934X_CDC_COMPANDER4_CTL4 0x0b1d +#define WCD934X_CDC_COMPANDER4_CTL5 0x0b1e +#define WCD934X_CDC_COMPANDER4_CTL6 0x0b1f +#define WCD934X_CDC_COMPANDER4_CTL7 0x0b20 +#define WCD934X_CDC_COMPANDER7_CTL0 0x0b31 +#define WCD934X_CDC_COMPANDER7_CTL1 0x0b32 +#define WCD934X_CDC_COMPANDER7_CTL2 0x0b33 +#define WCD934X_CDC_COMPANDER7_CTL3 0x0b34 +#define WCD934X_CDC_COMPANDER7_CTL4 0x0b35 +#define WCD934X_CDC_COMPANDER7_CTL5 0x0b36 +#define WCD934X_CDC_COMPANDER7_CTL6 0x0b37 +#define WCD934X_CDC_COMPANDER7_CTL7 0x0b38 +#define WCD934X_CDC_COMPANDER8_CTL0 0x0b39 +#define WCD934X_CDC_COMPANDER8_CTL1 0x0b3a +#define WCD934X_CDC_COMPANDER8_CTL2 0x0b3b +#define WCD934X_CDC_COMPANDER8_CTL3 0x0b3c +#define WCD934X_CDC_COMPANDER8_CTL4 0x0b3d +#define WCD934X_CDC_COMPANDER8_CTL5 0x0b3e +#define WCD934X_CDC_COMPANDER8_CTL6 0x0b3f +#define WCD934X_CDC_COMPANDER8_CTL7 0x0b40 +#define WCD934X_CDC_RX0_RX_PATH_CTL 0x0b41 +#define WCD934X_CDC_RX0_RX_PATH_CFG0 0x0b42 +#define WCD934X_CDC_RX0_RX_PATH_CFG1 0x0b43 +#define WCD934X_CDC_RX0_RX_PATH_CFG2 0x0b44 +#define WCD934X_CDC_RX0_RX_VOL_CTL 0x0b45 +#define WCD934X_CDC_RX0_RX_PATH_MIX_CTL 0x0b46 +#define WCD934X_CDC_RX0_RX_PATH_MIX_CFG 0x0b47 +#define WCD934X_CDC_RX0_RX_VOL_MIX_CTL 0x0b48 +#define WCD934X_CDC_RX0_RX_PATH_SEC0 0x0b49 +#define WCD934X_CDC_RX0_RX_PATH_SEC1 0x0b4a +#define WCD934X_CDC_RX0_RX_PATH_SEC2 0x0b4b +#define WCD934X_CDC_RX0_RX_PATH_SEC3 0x0b4c +#define WCD934X_CDC_RX0_RX_PATH_SEC5 0x0b4e +#define WCD934X_CDC_RX0_RX_PATH_SEC6 0x0b4f +#define WCD934X_CDC_RX0_RX_PATH_SEC7 0x0b50 +#define WCD934X_CDC_RX0_RX_PATH_MIX_SEC0 0x0b51 +#define WCD934X_CDC_RX0_RX_PATH_MIX_SEC1 0x0b52 +#define WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL 0x0b53 +#define WCD934X_CDC_RX1_RX_PATH_CTL 0x0b55 +#define WCD934X_CDC_RX1_RX_PATH_CFG0 0x0b56 +#define WCD934X_CDC_RX1_RX_PATH_CFG1 0x0b57 +#define WCD934X_CDC_RX1_RX_PATH_CFG2 0x0b58 +#define WCD934X_CDC_RX1_RX_VOL_CTL 0x0b59 +#define WCD934X_CDC_RX1_RX_PATH_MIX_CTL 0x0b5a +#define WCD934X_CDC_RX1_RX_PATH_MIX_CFG 0x0b5b +#define WCD934X_CDC_RX1_RX_VOL_MIX_CTL 0x0b5c +#define WCD934X_CDC_RX1_RX_PATH_SEC0 0x0b5d +#define WCD934X_CDC_RX1_RX_PATH_SEC1 0x0b5e +#define WCD934X_CDC_RX1_RX_PATH_SEC2 0x0b5f +#define WCD934X_CDC_RX1_RX_PATH_SEC3 0x0b60 +#define WCD934X_CDC_RX1_RX_PATH_SEC4 0x0b61 +#define WCD934X_CDC_RX1_RX_PATH_SEC5 0x0b62 +#define WCD934X_CDC_RX1_RX_PATH_SEC6 0x0b63 +#define WCD934X_CDC_RX1_RX_PATH_SEC7 0x0b64 +#define WCD934X_CDC_RX1_RX_PATH_MIX_SEC0 0x0b65 +#define WCD934X_CDC_RX1_RX_PATH_MIX_SEC1 0x0b66 +#define WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL 0x0b67 +#define WCD934X_CDC_RX2_RX_PATH_CTL 0x0b69 +#define WCD934X_CDC_RX2_RX_PATH_CFG0 0x0b6a +#define WCD934X_CDC_RX2_RX_PATH_CFG1 0x0b6b +#define WCD934X_CDC_RX2_RX_PATH_CFG2 0x0b6c +#define WCD934X_CDC_RX2_RX_VOL_CTL 0x0b6d +#define WCD934X_CDC_RX2_RX_PATH_MIX_CTL 0x0b6e +#define WCD934X_CDC_RX2_RX_PATH_MIX_CFG 0x0b6f +#define WCD934X_CDC_RX2_RX_VOL_MIX_CTL 0x0b70 +#define WCD934X_CDC_RX2_RX_PATH_SEC0 0x0b71 +#define WCD934X_CDC_RX2_RX_PATH_SEC1 0x0b72 +#define WCD934X_CDC_RX2_RX_PATH_SEC2 0x0b73 +#define WCD934X_CDC_RX2_RX_PATH_SEC3 0x0b74 +#define WCD934X_CDC_RX2_RX_PATH_SEC4 0x0b75 +#define WCD934X_CDC_RX2_RX_PATH_SEC5 0x0b76 +#define WCD934X_CDC_RX2_RX_PATH_SEC6 0x0b77 +#define WCD934X_CDC_RX2_RX_PATH_SEC7 0x0b78 +#define WCD934X_CDC_RX2_RX_PATH_MIX_SEC0 0x0b79 +#define WCD934X_CDC_RX2_RX_PATH_MIX_SEC1 0x0b7a +#define WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL 0x0b7b +#define WCD934X_CDC_RX3_RX_PATH_CTL 0x0b7d +#define WCD934X_CDC_RX3_RX_PATH_CFG0 0x0b7e +#define WCD934X_CDC_RX3_RX_PATH_CFG1 0x0b7f +#define WCD934X_CDC_RX3_RX_PATH_CFG2 0x0b80 +#define WCD934X_CDC_RX3_RX_VOL_CTL 0x0b81 +#define WCD934X_CDC_RX3_RX_PATH_MIX_CTL 0x0b82 +#define WCD934X_CDC_RX3_RX_PATH_MIX_CFG 0x0b83 +#define WCD934X_CDC_RX3_RX_VOL_MIX_CTL 0x0b84 +#define WCD934X_CDC_RX3_RX_PATH_SEC0 0x0b85 +#define WCD934X_CDC_RX3_RX_PATH_SEC1 0x0b86 +#define WCD934X_CDC_RX3_RX_PATH_SEC2 0x0b87 +#define WCD934X_CDC_RX3_RX_PATH_SEC3 0x0b88 +#define WCD934X_CDC_RX3_RX_PATH_SEC5 0x0b8a +#define WCD934X_CDC_RX3_RX_PATH_SEC6 0x0b8b +#define WCD934X_CDC_RX3_RX_PATH_SEC7 0x0b8c +#define WCD934X_CDC_RX3_RX_PATH_MIX_SEC0 0x0b8d +#define WCD934X_CDC_RX3_RX_PATH_MIX_SEC1 0x0b8e +#define WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL 0x0b8f +#define WCD934X_CDC_RX4_RX_PATH_CTL 0x0b91 +#define WCD934X_CDC_RX4_RX_PATH_CFG0 0x0b92 +#define WCD934X_CDC_RX4_RX_PATH_CFG1 0x0b93 +#define WCD934X_CDC_RX4_RX_PATH_CFG2 0x0b94 +#define WCD934X_CDC_RX4_RX_VOL_CTL 0x0b95 +#define WCD934X_CDC_RX4_RX_PATH_MIX_CTL 0x0b96 +#define WCD934X_CDC_RX4_RX_PATH_MIX_CFG 0x0b97 +#define WCD934X_CDC_RX4_RX_VOL_MIX_CTL 0x0b98 +#define WCD934X_CDC_RX4_RX_PATH_SEC0 0x0b99 +#define WCD934X_CDC_RX4_RX_PATH_SEC1 0x0b9a +#define WCD934X_CDC_RX4_RX_PATH_SEC2 0x0b9b +#define WCD934X_CDC_RX4_RX_PATH_SEC3 0x0b9c +#define WCD934X_CDC_RX4_RX_PATH_SEC5 0x0b9e +#define WCD934X_CDC_RX4_RX_PATH_SEC6 0x0b9f +#define WCD934X_CDC_RX4_RX_PATH_SEC7 0x0ba0 +#define WCD934X_CDC_RX4_RX_PATH_MIX_SEC0 0x0ba1 +#define WCD934X_CDC_RX4_RX_PATH_MIX_SEC1 0x0ba2 +#define WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL 0x0ba3 +#define WCD934X_CDC_RX7_RX_PATH_CTL 0x0bcd +#define WCD934X_CDC_RX7_RX_PATH_CFG0 0x0bce +#define WCD934X_CDC_RX7_RX_PATH_CFG1 0x0bcf +#define WCD934X_CDC_RX7_RX_PATH_CFG2 0x0bd0 +#define WCD934X_CDC_RX7_RX_VOL_CTL 0x0bd1 +#define WCD934X_CDC_RX7_RX_PATH_MIX_CTL 0x0bd2 +#define WCD934X_CDC_RX7_RX_PATH_MIX_CFG 0x0bd3 +#define WCD934X_CDC_RX7_RX_VOL_MIX_CTL 0x0bd4 +#define WCD934X_CDC_RX7_RX_PATH_SEC0 0x0bd5 +#define WCD934X_CDC_RX7_RX_PATH_SEC1 0x0bd6 +#define WCD934X_CDC_RX7_RX_PATH_SEC2 0x0bd7 +#define WCD934X_CDC_RX7_RX_PATH_SEC3 0x0bd8 +#define WCD934X_CDC_RX7_RX_PATH_SEC5 0x0bda +#define WCD934X_CDC_RX7_RX_PATH_SEC6 0x0bdb +#define WCD934X_CDC_RX7_RX_PATH_SEC7 0x0bdc +#define WCD934X_CDC_RX7_RX_PATH_MIX_SEC0 0x0bdd +#define WCD934X_CDC_RX7_RX_PATH_MIX_SEC1 0x0bde +#define WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL 0x0bdf +#define WCD934X_CDC_RX8_RX_PATH_CTL 0x0be1 +#define WCD934X_CDC_RX8_RX_PATH_CFG0 0x0be2 +#define WCD934X_CDC_RX8_RX_PATH_CFG1 0x0be3 +#define WCD934X_CDC_RX8_RX_PATH_CFG2 0x0be4 +#define WCD934X_CDC_RX8_RX_VOL_CTL 0x0be5 +#define WCD934X_CDC_RX8_RX_PATH_MIX_CTL 0x0be6 +#define WCD934X_CDC_RX8_RX_PATH_MIX_CFG 0x0be7 +#define WCD934X_CDC_RX8_RX_VOL_MIX_CTL 0x0be8 +#define WCD934X_CDC_RX8_RX_PATH_SEC0 0x0be9 +#define WCD934X_CDC_RX8_RX_PATH_SEC1 0x0bea +#define WCD934X_CDC_RX8_RX_PATH_SEC2 0x0beb +#define WCD934X_CDC_RX8_RX_PATH_SEC3 0x0bec +#define WCD934X_CDC_RX8_RX_PATH_SEC5 0x0bee +#define WCD934X_CDC_RX8_RX_PATH_SEC6 0x0bef +#define WCD934X_CDC_RX8_RX_PATH_SEC7 0x0bf0 +#define WCD934X_CDC_RX8_RX_PATH_MIX_SEC0 0x0bf1 +#define WCD934X_CDC_RX8_RX_PATH_MIX_SEC1 0x0bf2 +#define WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL 0x0bf3 +#define WCD934X_PAGE12_PAGE_REGISTER 0x0c00 +#define WCD934X_CDC_CLSH_CRC 0x0c01 +#define WCD934X_CDC_CLSH_DLY_CTRL 0x0c02 +#define WCD934X_CDC_CLSH_DECAY_CTRL 0x0c03 +#define WCD934X_CDC_CLSH_HPH_V_PA 0x0c04 +#define WCD934X_CDC_CLSH_EAR_V_PA 0x0c05 +#define WCD934X_CDC_CLSH_HPH_V_HD 0x0c06 +#define WCD934X_CDC_CLSH_EAR_V_HD 0x0c07 +#define WCD934X_CDC_CLSH_K1_MSB 0x0c08 +#define WCD934X_CDC_CLSH_K1_LSB 0x0c09 +#define WCD934X_CDC_CLSH_K2_MSB 0x0c0a +#define WCD934X_CDC_CLSH_K2_LSB 0x0c0b +#define WCD934X_CDC_CLSH_IDLE_CTRL 0x0c0c +#define WCD934X_CDC_CLSH_IDLE_HPH 0x0c0d +#define WCD934X_CDC_CLSH_IDLE_EAR 0x0c0e +#define WCD934X_CDC_CLSH_TEST0 0x0c0f +#define WCD934X_CDC_CLSH_TEST1 0x0c10 +#define WCD934X_CDC_CLSH_OVR_VREF 0x0c11 +#define WCD934X_CDC_BOOST0_BOOST_PATH_CTL 0x0c19 +#define WCD934X_CDC_BOOST0_BOOST_CTL 0x0c1a +#define WCD934X_CDC_BOOST0_BOOST_CFG1 0x0c1b +#define WCD934X_CDC_BOOST0_BOOST_CFG2 0x0c1c +#define WCD934X_CDC_BOOST1_BOOST_PATH_CTL 0x0c21 +#define WCD934X_CDC_BOOST1_BOOST_CTL 0x0c22 +#define WCD934X_CDC_BOOST1_BOOST_CFG1 0x0c23 +#define WCD934X_CDC_BOOST1_BOOST_CFG2 0x0c24 +#define WCD934X_CDC_VBAT_VBAT_PATH_CTL 0x0c3d +#define WCD934X_CDC_VBAT_VBAT_CFG 0x0c3e +#define WCD934X_CDC_VBAT_VBAT_ADC_CAL1 0x0c3f +#define WCD934X_CDC_VBAT_VBAT_ADC_CAL2 0x0c40 +#define WCD934X_CDC_VBAT_VBAT_ADC_CAL3 0x0c41 +#define WCD934X_CDC_VBAT_VBAT_PK_EST1 0x0c42 +#define WCD934X_CDC_VBAT_VBAT_PK_EST2 0x0c43 +#define WCD934X_CDC_VBAT_VBAT_PK_EST3 0x0c44 +#define WCD934X_CDC_VBAT_VBAT_RF_PROC1 0x0c45 +#define WCD934X_CDC_VBAT_VBAT_RF_PROC2 0x0c46 +#define WCD934X_CDC_VBAT_VBAT_TAC1 0x0c47 +#define WCD934X_CDC_VBAT_VBAT_TAC2 0x0c48 +#define WCD934X_CDC_VBAT_VBAT_TAC3 0x0c49 +#define WCD934X_CDC_VBAT_VBAT_TAC4 0x0c4a +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD1 0x0c4b +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD2 0x0c4c +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD3 0x0c4d +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD4 0x0c4e +#define WCD934X_CDC_VBAT_VBAT_DEBUG1 0x0c4f +#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON 0x0c50 +#define WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL 0x0c51 +#define WCD934X_CDC_VBAT_VBAT_BAN 0x0c52 +#define WCD934X_MIXING_ASRC0_CLK_RST_CTL 0x0c55 +#define WCD934X_MIXING_ASRC0_CTL0 0x0c56 +#define WCD934X_MIXING_ASRC0_CTL1 0x0c57 +#define WCD934X_MIXING_ASRC0_FIFO_CTL 0x0c58 +#define WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB 0x0c59 +#define WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB 0x0c5a +#define WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB 0x0c5b +#define WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB 0x0c5c +#define WCD934X_MIXING_ASRC0_STATUS_FIFO 0x0c5d +#define WCD934X_MIXING_ASRC1_CLK_RST_CTL 0x0c61 +#define WCD934X_MIXING_ASRC1_CTL0 0x0c62 +#define WCD934X_MIXING_ASRC1_CTL1 0x0c63 +#define WCD934X_MIXING_ASRC1_FIFO_CTL 0x0c64 +#define WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB 0x0c65 +#define WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB 0x0c66 +#define WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB 0x0c67 +#define WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB 0x0c68 +#define WCD934X_MIXING_ASRC1_STATUS_FIFO 0x0c69 +#define WCD934X_MIXING_ASRC2_CLK_RST_CTL 0x0c6d +#define WCD934X_MIXING_ASRC2_CTL0 0x0c6e +#define WCD934X_MIXING_ASRC2_CTL1 0x0c6f +#define WCD934X_MIXING_ASRC2_FIFO_CTL 0x0c70 +#define WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB 0x0c71 +#define WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB 0x0c72 +#define WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB 0x0c73 +#define WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB 0x0c74 +#define WCD934X_MIXING_ASRC2_STATUS_FIFO 0x0c75 +#define WCD934X_MIXING_ASRC3_CLK_RST_CTL 0x0c79 +#define WCD934X_MIXING_ASRC3_CTL0 0x0c7a +#define WCD934X_MIXING_ASRC3_CTL1 0x0c7b +#define WCD934X_MIXING_ASRC3_FIFO_CTL 0x0c7c +#define WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB 0x0c7d +#define WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB 0x0c7e +#define WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB 0x0c7f +#define WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB 0x0c80 +#define WCD934X_MIXING_ASRC3_STATUS_FIFO 0x0c81 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_0 0x0c85 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_1 0x0c86 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_2 0x0c87 +#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_3 0x0c88 +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0 0x0c89 +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1 0x0c8a +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2 0x0c8b +#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3 0x0c8c +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0 0x0c8d +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1 0x0c8e +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2 0x0c8f +#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3 0x0c90 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_0 0x0c91 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_1 0x0c92 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_2 0x0c93 +#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_3 0x0c94 +#define WCD934X_SWR_AHB_BRIDGE_ACCESS_CFG 0x0c95 +#define WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS 0x0c96 +#define WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL 0x0cb5 +#define WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1 0x0cb6 +#define WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL 0x0cb9 +#define WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1 0x0cba +#define WCD934X_SIDETONE_ASRC0_CLK_RST_CTL 0x0cbd +#define WCD934X_SIDETONE_ASRC0_CTL0 0x0cbe +#define WCD934X_SIDETONE_ASRC0_CTL1 0x0cbf +#define WCD934X_SIDETONE_ASRC0_FIFO_CTL 0x0cc0 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB 0x0cc1 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB 0x0cc2 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB 0x0cc3 +#define WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB 0x0cc4 +#define WCD934X_SIDETONE_ASRC0_STATUS_FIFO 0x0cc5 +#define WCD934X_SIDETONE_ASRC1_CLK_RST_CTL 0x0cc9 +#define WCD934X_SIDETONE_ASRC1_CTL0 0x0cca +#define WCD934X_SIDETONE_ASRC1_CTL1 0x0ccb +#define WCD934X_SIDETONE_ASRC1_FIFO_CTL 0x0ccc +#define WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB 0x0ccd +#define WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB 0x0cce +#define WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB 0x0ccf +#define WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB 0x0cd0 +#define WCD934X_SIDETONE_ASRC1_STATUS_FIFO 0x0cd1 +#define WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL 0x0cd5 +#define WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0 0x0cd6 +#define WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL 0x0cdd +#define WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0 0x0cde +#define WCD934X_EC_ASRC0_CLK_RST_CTL 0x0ce5 +#define WCD934X_EC_ASRC0_CTL0 0x0ce6 +#define WCD934X_EC_ASRC0_CTL1 0x0ce7 +#define WCD934X_EC_ASRC0_FIFO_CTL 0x0ce8 +#define WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB 0x0ce9 +#define WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB 0x0cea +#define WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB 0x0ceb +#define WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB 0x0cec +#define WCD934X_EC_ASRC0_STATUS_FIFO 0x0ced +#define WCD934X_EC_ASRC1_CLK_RST_CTL 0x0cf1 +#define WCD934X_EC_ASRC1_CTL0 0x0cf2 +#define WCD934X_EC_ASRC1_CTL1 0x0cf3 +#define WCD934X_EC_ASRC1_FIFO_CTL 0x0cf4 +#define WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB 0x0cf5 +#define WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB 0x0cf6 +#define WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB 0x0cf7 +#define WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB 0x0cf8 +#define WCD934X_EC_ASRC1_STATUS_FIFO 0x0cf9 +#define WCD934X_PAGE13_PAGE_REGISTER 0x0d00 +#define WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0 0x0d01 +#define WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1 0x0d02 +#define WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0 0x0d03 +#define WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1 0x0d04 +#define WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0 0x0d05 +#define WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1 0x0d06 +#define WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0 0x0d07 +#define WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1 0x0d08 +#define WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0 0x0d09 +#define WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1 0x0d0a +#define WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0 0x0d0f +#define WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1 0x0d10 +#define WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0 0x0d11 +#define WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1 0x0d12 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0 0x0d13 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1 0x0d14 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2 0x0d15 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3 0x0d16 +#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4 0x0d17 +#define WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0 0x0d18 +#define WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1 0x0d19 +#define WCD934X_CDC_RX_INP_MUX_ANC_CFG0 0x0d1a +#define WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0 0x0d1b +#define WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0 0x0d1c +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 0x0d1d +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 0x0d1e +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0 0x0d1f +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1 0x0d20 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0 0x0d21 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1 0x0d22 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0 0x0d23 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1 0x0d25 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 0x0d26 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0 0x0d27 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0 0x0d28 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0 0x0d29 +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0 0x0d2a +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0 0x0d2b +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0 0x0d2c +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0 0x0d2d +#define WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0 0x0d2e +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0 0x0d31 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1 0x0d32 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2 0x0d33 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3 0x0d34 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0 0x0d35 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1 0x0d36 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2 0x0d37 +#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3 0x0d38 +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0 0x0d3a +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1 0x0d3b +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2 0x0d3c +#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3 0x0d3d +#define WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41 +#define WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42 +#define WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL 0x0d43 +#define WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL 0x0d44 +#define WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL 0x0d45 +#define WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL 0x0d46 +#define WCD934X_CDC_PROX_DETECT_PROX_CTL 0x0d49 +#define WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0 0x0d4a +#define WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1 0x0d4b +#define WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB 0x0d4c +#define WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB 0x0d4d +#define WCD934X_CDC_PROX_DETECT_PROX_STATUS 0x0d4e +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL 0x0d4f +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB 0x0d50 +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB 0x0d51 +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD 0x0d52 +#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD 0x0d53 +#define WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT 0x0d54 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL 0x0d55 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL 0x0d56 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL 0x0d57 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL 0x0d58 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL 0x0d59 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL 0x0d5a +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL 0x0d5b +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL 0x0d5c +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL 0x0d5d +#define WCD934X_CDC_SIDETONE_IIR0_IIR_CTL 0x0d5e +#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL 0x0d5f +#define WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL 0x0d60 +#define WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL 0x0d61 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL 0x0d65 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL 0x0d66 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL 0x0d67 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL 0x0d68 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL 0x0d69 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL 0x0d6a +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL 0x0d6b +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL 0x0d6c +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL 0x0d6d +#define WCD934X_CDC_SIDETONE_IIR1_IIR_CTL 0x0d6e +#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL 0x0d6f +#define WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL 0x0d70 +#define WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL 0x0d71 +#define WCD934X_CDC_TOP_TOP_CFG0 0x0d81 +#define WCD934X_CDC_TOP_TOP_CFG1 0x0d82 +#define WCD934X_CDC_TOP_TOP_CFG7 0x0d88 +#define WCD934X_CDC_TOP_HPHL_COMP_WR_LSB 0x0d89 +#define WCD934X_CDC_TOP_HPHL_COMP_WR_MSB 0x0d8a +#define WCD934X_CDC_TOP_HPHL_COMP_LUT 0x0d8b +#define WCD934X_CDC_TOP_HPHL_COMP_RD_LSB 0x0d8c +#define WCD934X_CDC_TOP_HPHL_COMP_RD_MSB 0x0d8d +#define WCD934X_CDC_TOP_HPHR_COMP_WR_LSB 0x0d8e +#define WCD934X_CDC_TOP_HPHR_COMP_WR_MSB 0x0d8f +#define WCD934X_CDC_TOP_HPHR_COMP_LUT 0x0d90 +#define WCD934X_CDC_TOP_HPHR_COMP_RD_LSB 0x0d91 +#define WCD934X_CDC_TOP_HPHR_COMP_RD_MSB 0x0d92 +#define WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB 0x0d93 +#define WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB 0x0d94 +#define WCD934X_CDC_TOP_DIFFL_COMP_LUT 0x0d95 +#define WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB 0x0d96 +#define WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB 0x0d97 +#define WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB 0x0d98 +#define WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB 0x0d99 +#define WCD934X_CDC_TOP_DIFFR_COMP_LUT 0x0d9a +#define WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB 0x0d9b +#define WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB 0x0d9c +#define WCD934X_CDC_DSD0_PATH_CTL 0x0db1 +#define WCD934X_CDC_DSD0_CFG0 0x0db2 +#define WCD934X_CDC_DSD0_CFG1 0x0db3 +#define WCD934X_CDC_DSD0_CFG2 0x0db4 +#define WCD934X_CDC_DSD0_CFG3 0x0db5 +#define WCD934X_CDC_DSD0_CFG4 0x0db6 +#define WCD934X_CDC_DSD0_CFG5 0x0db7 +#define WCD934X_CDC_DSD1_PATH_CTL 0x0dc1 +#define WCD934X_CDC_DSD1_CFG0 0x0dc2 +#define WCD934X_CDC_DSD1_CFG1 0x0dc3 +#define WCD934X_CDC_DSD1_CFG2 0x0dc4 +#define WCD934X_CDC_DSD1_CFG3 0x0dc5 +#define WCD934X_CDC_DSD1_CFG4 0x0dc6 +#define WCD934X_CDC_DSD1_CFG5 0x0dc7 +#define WCD934X_CDC_RX_IDLE_DET_PATH_CTL 0x0dd1 +#define WCD934X_CDC_RX_IDLE_DET_CFG0 0x0dd2 +#define WCD934X_CDC_RX_IDLE_DET_CFG1 0x0dd3 +#define WCD934X_CDC_RX_IDLE_DET_CFG2 0x0dd4 +#define WCD934X_CDC_RX_IDLE_DET_CFG3 0x0dd5 +#define WCD934X_PAGE14_PAGE_REGISTER 0x0e00 +#define WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL 0x0e01 +#define WCD934X_CDC_RATE_EST0_RE_CTL 0x0e02 +#define WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL 0x0e03 +#define WCD934X_CDC_RATE_EST0_RE_TIMER 0x0e04 +#define WCD934X_CDC_RATE_EST0_RE_BW_SW 0x0e05 +#define WCD934X_CDC_RATE_EST0_RE_THRESH 0x0e06 +#define WCD934X_CDC_RATE_EST0_RE_STATUS 0x0e07 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL 0x0e09 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2 0x0e0c +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1 0x0e0d +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2 0x0e0e +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3 0x0e0f +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4 0x0e10 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5 0x0e11 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1 0x0e12 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2 0x0e13 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3 0x0e14 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4 0x0e15 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5 0x0e16 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1 0x0e17 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2 0x0e18 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3 0x0e19 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4 0x0e1a +#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5 0x0e1b +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1 0x0e1c +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2 0x0e1d +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3 0x0e1e +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4 0x0e1f +#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5 0x0e20 +#define WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG 0x0e21 +#define WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG 0x0e22 +#define WCD934X_CDC_RATE_EST0_RE_PH_DET 0x0e23 +#define WCD934X_CDC_RATE_EST0_RE_DIAG_CLR 0x0e24 +#define WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE 0x0e25 +#define WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE 0x0e26 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0 0x0e27 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8 0x0e28 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16 0x0e29 +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24 0x0e2a +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32 0x0e2b +#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43 0x0e2c +#define WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL 0x0e31 +#define WCD934X_CDC_RATE_EST1_RE_CTL 0x0e32 +#define WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL 0x0e33 +#define WCD934X_CDC_RATE_EST1_RE_TIMER 0x0e34 +#define WCD934X_CDC_RATE_EST1_RE_BW_SW 0x0e35 +#define WCD934X_CDC_RATE_EST1_RE_THRESH 0x0e36 +#define WCD934X_CDC_RATE_EST1_RE_STATUS 0x0e37 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL 0x0e39 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2 0x0e3c +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1 0x0e3d +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2 0x0e3e +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3 0x0e3f +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4 0x0e40 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5 0x0e41 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1 0x0e42 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2 0x0e43 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3 0x0e44 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4 0x0e45 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5 0x0e46 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1 0x0e47 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2 0x0e48 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3 0x0e49 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4 0x0e4a +#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5 0x0e4b +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1 0x0e4c +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2 0x0e4d +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3 0x0e4e +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4 0x0e4f +#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5 0x0e50 +#define WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG 0x0e51 +#define WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG 0x0e52 +#define WCD934X_CDC_RATE_EST1_RE_PH_DET 0x0e53 +#define WCD934X_CDC_RATE_EST1_RE_DIAG_CLR 0x0e54 +#define WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE 0x0e55 +#define WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE 0x0e56 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0 0x0e57 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8 0x0e58 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16 0x0e59 +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24 0x0e5a +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32 0x0e5b +#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43 0x0e5c +#define WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL 0x0e61 +#define WCD934X_CDC_RATE_EST2_RE_CTL 0x0e62 +#define WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL 0x0e63 +#define WCD934X_CDC_RATE_EST2_RE_TIMER 0x0e64 +#define WCD934X_CDC_RATE_EST2_RE_BW_SW 0x0e65 +#define WCD934X_CDC_RATE_EST2_RE_THRESH 0x0e66 +#define WCD934X_CDC_RATE_EST2_RE_STATUS 0x0e67 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL 0x0e69 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2 0x0e6c +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1 0x0e6d +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2 0x0e6e +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3 0x0e6f +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4 0x0e70 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5 0x0e71 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1 0x0e72 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2 0x0e73 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3 0x0e74 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4 0x0e75 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5 0x0e76 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1 0x0e77 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2 0x0e78 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3 0x0e79 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4 0x0e7a +#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5 0x0e7b +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1 0x0e7c +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2 0x0e7d +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3 0x0e7e +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4 0x0e7f +#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5 0x0e80 +#define WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG 0x0e81 +#define WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG 0x0e82 +#define WCD934X_CDC_RATE_EST2_RE_PH_DET 0x0e83 +#define WCD934X_CDC_RATE_EST2_RE_DIAG_CLR 0x0e84 +#define WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE 0x0e85 +#define WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE 0x0e86 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0 0x0e87 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8 0x0e88 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16 0x0e89 +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24 0x0e8a +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32 0x0e8b +#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43 0x0e8c +#define WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL 0x0e91 +#define WCD934X_CDC_RATE_EST3_RE_CTL 0x0e92 +#define WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL 0x0e93 +#define WCD934X_CDC_RATE_EST3_RE_TIMER 0x0e94 +#define WCD934X_CDC_RATE_EST3_RE_BW_SW 0x0e95 +#define WCD934X_CDC_RATE_EST3_RE_THRESH 0x0e96 +#define WCD934X_CDC_RATE_EST3_RE_STATUS 0x0e97 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL 0x0e99 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2 0x0e9c +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1 0x0e9d +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2 0x0e9e +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3 0x0e9f +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4 0x0ea0 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5 0x0ea1 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1 0x0ea2 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2 0x0ea3 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3 0x0ea4 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4 0x0ea5 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5 0x0ea6 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1 0x0ea7 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2 0x0ea8 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3 0x0ea9 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4 0x0eaa +#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5 0x0eab +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1 0x0eac +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2 0x0ead +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3 0x0eae +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4 0x0eaf +#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5 0x0eb0 +#define WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG 0x0eb1 +#define WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG 0x0eb2 +#define WCD934X_CDC_RATE_EST3_RE_PH_DET 0x0eb3 +#define WCD934X_CDC_RATE_EST3_RE_DIAG_CLR 0x0eb4 +#define WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE 0x0eb5 +#define WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE 0x0eb6 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0 0x0eb7 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8 0x0eb8 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16 0x0eb9 +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24 0x0eba +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32 0x0ebb +#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43 0x0ebc +#define WCD934X_PAGE15_PAGE_REGISTER 0x0f00 +#define WCD934X_SPLINE_SRC0_CLK_RST_CTL_0 0x0f01 +#define WCD934X_SPLINE_SRC0_STATUS 0x0f02 +#define WCD934X_SPLINE_SRC1_CLK_RST_CTL_0 0x0f19 +#define WCD934X_SPLINE_SRC1_STATUS 0x0f1a +#define WCD934X_SPLINE_SRC2_CLK_RST_CTL_0 0x0f31 +#define WCD934X_SPLINE_SRC2_STATUS 0x0f32 +#define WCD934X_SPLINE_SRC3_CLK_RST_CTL_0 0x0f49 +#define WCD934X_SPLINE_SRC3_STATUS 0x0f4a +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0 0x0fa1 +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1 0x0fa2 +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2 0x0fa3 +#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3 0x0fa4 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0 0x0fa5 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1 0x0fa6 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2 0x0fa7 +#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3 0x0fa8 +#define WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0 0x0fa9 +#define WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1 0x0faa +#define WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0 0x0fab +#define WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL 0x0fac +#define WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL 0x0fad +#define WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL 0x0fae +#define WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL 0x0faf +#define WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR 0x0fb0 +#define WCD934X_PAGE80_PAGE_REGISTER 0x5000 +#define WCD934X_CODEC_CPR_WR_DATA_0 0x5001 +#define WCD934X_CODEC_CPR_WR_DATA_1 0x5002 +#define WCD934X_CODEC_CPR_WR_DATA_2 0x5003 +#define WCD934X_CODEC_CPR_WR_DATA_3 0x5004 +#define WCD934X_CODEC_CPR_WR_ADDR_0 0x5005 +#define WCD934X_CODEC_CPR_WR_ADDR_1 0x5006 +#define WCD934X_CODEC_CPR_WR_ADDR_2 0x5007 +#define WCD934X_CODEC_CPR_WR_ADDR_3 0x5008 +#define WCD934X_CODEC_CPR_RD_ADDR_0 0x5009 +#define WCD934X_CODEC_CPR_RD_ADDR_1 0x500a +#define WCD934X_CODEC_CPR_RD_ADDR_2 0x500b +#define WCD934X_CODEC_CPR_RD_ADDR_3 0x500c +#define WCD934X_CODEC_CPR_RD_DATA_0 0x500d +#define WCD934X_CODEC_CPR_RD_DATA_1 0x500e +#define WCD934X_CODEC_CPR_RD_DATA_2 0x500f +#define WCD934X_CODEC_CPR_RD_DATA_3 0x5010 +#define WCD934X_CODEC_CPR_ACCESS_CFG 0x5011 +#define WCD934X_CODEC_CPR_ACCESS_STATUS 0x5012 +#define WCD934X_CODEC_CPR_NOM_CX_VDD 0x5021 +#define WCD934X_CODEC_CPR_SVS_CX_VDD 0x5022 +#define WCD934X_CODEC_CPR_SVS2_CX_VDD 0x5023 +#define WCD934X_CODEC_CPR_NOM_MX_VDD 0x5024 +#define WCD934X_CODEC_CPR_SVS_MX_VDD 0x5025 +#define WCD934X_CODEC_CPR_SVS2_MX_VDD 0x5026 +#define WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD 0x5027 +#define WCD934X_CODEC_CPR_MAX_SVS2_STEP 0x5028 +#define WCD934X_CODEC_CPR_CTL 0x5029 +#define WCD934X_CODEC_CPR_SW_MODECHNG_STATUS 0x502a +#define WCD934X_CODEC_CPR_SW_MODECHNG_START 0x502b +#define WCD934X_CODEC_CPR_CPR_STATUS 0x502c +#define WCD934X_PAGE128_PAGE_REGISTER 0x8000 +#define WCD934X_TLMM_BIST_MODE_PINCFG 0x8001 +#define WCD934X_TLMM_RF_PA_ON_PINCFG 0x8002 +#define WCD934X_TLMM_INTR1_PINCFG 0x8003 +#define WCD934X_TLMM_INTR2_PINCFG 0x8004 +#define WCD934X_TLMM_SWR_DATA_PINCFG 0x8005 +#define WCD934X_TLMM_SWR_CLK_PINCFG 0x8006 +#define WCD934X_TLMM_I2S_2_SCK_PINCFG 0x8007 +#define WCD934X_TLMM_SLIMBUS_DATA1_PINCFG 0x8008 +#define WCD934X_TLMM_SLIMBUS_DATA2_PINCFG 0x8009 +#define WCD934X_TLMM_SLIMBUS_CLK_PINCFG 0x800a +#define WCD934X_TLMM_I2C_CLK_PINCFG 0x800b +#define WCD934X_TLMM_I2C_DATA_PINCFG 0x800c +#define WCD934X_TLMM_I2S_0_RX_PINCFG 0x800d +#define WCD934X_TLMM_I2S_0_TX_PINCFG 0x800e +#define WCD934X_TLMM_I2S_0_SCK_PINCFG 0x800f +#define WCD934X_TLMM_I2S_0_WS_PINCFG 0x8010 +#define WCD934X_TLMM_I2S_1_RX_PINCFG 0x8011 +#define WCD934X_TLMM_I2S_1_TX_PINCFG 0x8012 +#define WCD934X_TLMM_I2S_1_SCK_PINCFG 0x8013 +#define WCD934X_TLMM_I2S_1_WS_PINCFG 0x8014 +#define WCD934X_TLMM_DMIC1_CLK_PINCFG 0x8015 +#define WCD934X_TLMM_DMIC1_DATA_PINCFG 0x8016 +#define WCD934X_TLMM_DMIC2_CLK_PINCFG 0x8017 +#define WCD934X_TLMM_DMIC2_DATA_PINCFG 0x8018 +#define WCD934X_TLMM_DMIC3_CLK_PINCFG 0x8019 +#define WCD934X_TLMM_DMIC3_DATA_PINCFG 0x801a +#define WCD934X_TLMM_JTCK_PINCFG 0x801b +#define WCD934X_TLMM_GPIO1_PINCFG 0x801c +#define WCD934X_TLMM_GPIO2_PINCFG 0x801d +#define WCD934X_TLMM_GPIO3_PINCFG 0x801e +#define WCD934X_TLMM_GPIO4_PINCFG 0x801f +#define WCD934X_TLMM_SPI_S_CSN_PINCFG 0x8020 +#define WCD934X_TLMM_SPI_S_CLK_PINCFG 0x8021 +#define WCD934X_TLMM_SPI_S_DOUT_PINCFG 0x8022 +#define WCD934X_TLMM_SPI_S_DIN_PINCFG 0x8023 +#define WCD934X_TLMM_BA_N_PINCFG 0x8024 +#define WCD934X_TLMM_GPIO0_PINCFG 0x8025 +#define WCD934X_TLMM_I2S_2_RX_PINCFG 0x8026 +#define WCD934X_TLMM_I2S_2_WS_PINCFG 0x8027 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_0 0x8031 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_1 0x8032 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_2 0x8033 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_3 0x8034 +#define WCD934X_TEST_DEBUG_PIN_CTL_OE_4 0x8035 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_0 0x8036 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_1 0x8037 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_2 0x8038 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_3 0x8039 +#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_4 0x803a +#define WCD934X_TEST_DEBUG_PAD_DRVCTL_0 0x803b +#define WCD934X_TEST_DEBUG_PAD_DRVCTL_1 0x803c +#define WCD934X_TEST_DEBUG_PIN_STATUS 0x803d +#define WCD934X_TEST_DEBUG_NPL_DLY_TEST_1 0x803e +#define WCD934X_TEST_DEBUG_NPL_DLY_TEST_2 0x803f +#define WCD934X_TEST_DEBUG_MEM_CTRL 0x8040 +#define WCD934X_TEST_DEBUG_DEBUG_BUS_SEL 0x8041 +#define WCD934X_TEST_DEBUG_DEBUG_JTAG 0x8042 +#define WCD934X_TEST_DEBUG_DEBUG_EN_1 0x8043 +#define WCD934X_TEST_DEBUG_DEBUG_EN_2 0x8044 +#define WCD934X_TEST_DEBUG_DEBUG_EN_3 0x8045 +#define WCD934X_TEST_DEBUG_DEBUG_EN_4 0x8046 +#define WCD934X_TEST_DEBUG_DEBUG_EN_5 0x8047 +#define WCD934X_TEST_DEBUG_ANA_DTEST_DIR 0x804a +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0 0x804b +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1 0x804c +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2 0x804d +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3 0x804e +#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4 0x804f +#define WCD934X_TEST_DEBUG_SYSMEM_CTRL 0x8050 +#define WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY 0x8051 +#define WCD934X_TEST_DEBUG_LVAL_NOM_LOW 0x8052 +#define WCD934X_TEST_DEBUG_LVAL_NOM_HIGH 0x8053 +#define WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW 0x8054 +#define WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH 0x8055 +#define WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR 0x8056 +#define WCD934X_TEST_DEBUG_CODEC_DIAGS 0x8057 +#define WCD934X_MAX_REGISTER 0x80FF + +/* SLIMBUS Slave Registers */ +#define WCD934X_SLIM_PGD_PORT_INT_RX_EN0 (0x30) +#define WCD934X_SLIM_PGD_PORT_INT_TX_EN0 (0x32) +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0 (0x34) +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_1 (0x35) +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_0 (0x36) +#define WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1 (0x37) +#define WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 (0x38) +#define WCD934X_SLIM_PGD_PORT_INT_CLR_RX_1 (0x39) +#define WCD934X_SLIM_PGD_PORT_INT_CLR_TX_0 (0x3A) +#define WCD934X_SLIM_PGD_PORT_INT_CLR_TX_1 (0x3B) +#define WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 (0x60) +#define WCD934X_SLIM_PGD_PORT_INT_TX_SOURCE0 (0x70) + +#endif diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h new file mode 100644 index 000000000000..b4c1be40ff31 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/core.h @@ -0,0 +1,440 @@ +/* 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. + */ + +#ifndef __MFD_TABLA_CORE_H__ +#define __MFD_TABLA_CORE_H__ + +#include +#include +#include +#include +#include + +#define WCD9XXX_MAX_IRQ_REGS 4 +#define WCD9XXX_MAX_NUM_IRQS (WCD9XXX_MAX_IRQ_REGS * 8) +#define WCD9XXX_SLIM_NUM_PORT_REG 3 +#define TABLA_VERSION_1_0 0 +#define TABLA_VERSION_1_1 1 +#define TABLA_VERSION_2_0 2 +#define TABLA_IS_1_X(ver) \ + (((ver == TABLA_VERSION_1_0) || (ver == TABLA_VERSION_1_1)) ? 1 : 0) +#define TABLA_IS_2_0(ver) ((ver == TABLA_VERSION_2_0) ? 1 : 0) + +#define WCD9XXX_SUPPLY_BUCK_NAME "cdc-vdd-buck" + +#define SITAR_VERSION_1P0 0 +#define SITAR_VERSION_1P1 1 +#define SITAR_IS_1P0(ver) \ + ((ver == SITAR_VERSION_1P0) ? 1 : 0) +#define SITAR_IS_1P1(ver) \ + ((ver == SITAR_VERSION_1P1) ? 1 : 0) + +#define TAIKO_VERSION_1_0 1 +#define TAIKO_IS_1_0(ver) \ + ((ver == TAIKO_VERSION_1_0) ? 1 : 0) + +#define TAPAN_VERSION_1_0 0 +#define TAPAN_IS_1_0(ver) \ + ((ver == TAPAN_VERSION_1_0) ? 1 : 0) + +#define TOMTOM_VERSION_1_0 1 +#define TOMTOM_IS_1_0(ver) \ + ((ver == TOMTOM_VERSION_1_0) ? 1 : 0) + +#define TASHA_VERSION_1_0 0 +#define TASHA_VERSION_1_1 1 +#define TASHA_VERSION_2_0 2 + +#define TASHA_IS_1_0(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_1_0) ? 1 : 0) : 0) + +#define TASHA_IS_1_1(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_1_1) ? 1 : 0) : 0) + +#define TASHA_IS_2_0(wcd) \ + ((wcd->type == WCD9335 || wcd->type == WCD9326) ? \ + ((wcd->version == TASHA_VERSION_2_0) ? 1 : 0) : 0) + +/* + * As fine version info cannot be retrieved before tavil probe. + * Define three coarse versions for possible future use before tavil probe. + */ +#define TAVIL_VERSION_1_0 0 +#define TAVIL_VERSION_1_1 1 +#define TAVIL_VERSION_WCD9340_1_0 2 +#define TAVIL_VERSION_WCD9341_1_0 3 +#define TAVIL_VERSION_WCD9340_1_1 4 +#define TAVIL_VERSION_WCD9341_1_1 5 + +#define TAVIL_IS_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_1_0 || \ + wcd->version == TAVIL_VERSION_WCD9340_1_0 || \ + wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_1_1 || \ + wcd->version == TAVIL_VERSION_WCD9340_1_1 || \ + wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9340_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9340_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9341_1_0(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9340_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9340_1_1) ? 1 : 0) : 0) +#define TAVIL_IS_WCD9341_1_1(wcd) \ + ((wcd->type == WCD934X) ? \ + ((wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0) + +#define IS_CODEC_TYPE(wcd, wcdtype) \ + ((wcd->type == wcdtype) ? true : false) +#define IS_CODEC_VERSION(wcd, wcdversion) \ + ((wcd->version == wcdversion) ? true : false) + +enum { + CDC_V_1_0, + CDC_V_1_1, + CDC_V_2_0, +}; + +enum codec_variant { + WCD9XXX, + WCD9330, + WCD9335, + WCD9326, + WCD934X, +}; + +enum wcd9xxx_slim_slave_addr_type { + WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0, + WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1, +}; + +enum wcd9xxx_pm_state { + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE, + WCD9XXX_PM_ASLEEP, +}; + +enum { + WCD9XXX_INTR_STATUS_BASE = 0, + WCD9XXX_INTR_CLEAR_BASE, + WCD9XXX_INTR_MASK_BASE, + WCD9XXX_INTR_LEVEL_BASE, + WCD9XXX_INTR_CLR_COMMIT, + WCD9XXX_INTR_REG_MAX, +}; + +enum wcd9xxx_intf_status { + WCD9XXX_INTERFACE_TYPE_PROBING, + WCD9XXX_INTERFACE_TYPE_SLIMBUS, + WCD9XXX_INTERFACE_TYPE_I2C, +}; + +enum { + /* INTR_REG 0 */ + WCD9XXX_IRQ_SLIMBUS = 0, + WCD9XXX_IRQ_MBHC_REMOVAL, + WCD9XXX_IRQ_MBHC_SHORT_TERM, + WCD9XXX_IRQ_MBHC_PRESS, + WCD9XXX_IRQ_MBHC_RELEASE, + WCD9XXX_IRQ_MBHC_POTENTIAL, + WCD9XXX_IRQ_MBHC_INSERTION, + WCD9XXX_IRQ_BG_PRECHARGE, + /* INTR_REG 1 */ + WCD9XXX_IRQ_PA1_STARTUP, + WCD9XXX_IRQ_PA2_STARTUP, + WCD9XXX_IRQ_PA3_STARTUP, + WCD9XXX_IRQ_PA4_STARTUP, + WCD9306_IRQ_HPH_PA_OCPR_FAULT = WCD9XXX_IRQ_PA4_STARTUP, + WCD9XXX_IRQ_PA5_STARTUP, + WCD9XXX_IRQ_MICBIAS1_PRECHARGE, + WCD9306_IRQ_HPH_PA_OCPL_FAULT = WCD9XXX_IRQ_MICBIAS1_PRECHARGE, + WCD9XXX_IRQ_MICBIAS2_PRECHARGE, + WCD9XXX_IRQ_MICBIAS3_PRECHARGE, + /* INTR_REG 2 */ + WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, + WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, + WCD9XXX_IRQ_EAR_PA_OCPL_FAULT, + WCD9XXX_IRQ_HPH_L_PA_STARTUP, + WCD9XXX_IRQ_HPH_R_PA_STARTUP, + WCD9320_IRQ_EAR_PA_STARTUP, + WCD9306_IRQ_MBHC_JACK_SWITCH = WCD9320_IRQ_EAR_PA_STARTUP, + WCD9310_NUM_IRQS, + WCD9XXX_IRQ_RESERVED_0 = WCD9310_NUM_IRQS, + WCD9XXX_IRQ_RESERVED_1, + WCD9330_IRQ_SVASS_ERR_EXCEPTION = WCD9310_NUM_IRQS, + WCD9330_IRQ_MBHC_JACK_SWITCH, + /* INTR_REG 3 */ + WCD9XXX_IRQ_MAD_AUDIO, + WCD9XXX_IRQ_MAD_ULTRASOUND, + WCD9XXX_IRQ_MAD_BEACON, + WCD9XXX_IRQ_SPEAKER_CLIPPING, + WCD9320_IRQ_MBHC_JACK_SWITCH, + WCD9306_NUM_IRQS, + WCD9XXX_IRQ_VBAT_MONITOR_ATTACK = WCD9306_NUM_IRQS, + WCD9XXX_IRQ_VBAT_MONITOR_RELEASE, + WCD9XXX_NUM_IRQS, + /* WCD9330 INTR1_REG 3*/ + WCD9330_IRQ_SVASS_ENGINE = WCD9XXX_IRQ_MAD_AUDIO, + WCD9330_IRQ_MAD_AUDIO, + WCD9330_IRQ_MAD_ULTRASOUND, + WCD9330_IRQ_MAD_BEACON, + WCD9330_IRQ_SPEAKER1_CLIPPING, + WCD9330_IRQ_SPEAKER2_CLIPPING, + WCD9330_IRQ_VBAT_MONITOR_ATTACK, + WCD9330_IRQ_VBAT_MONITOR_RELEASE, + WCD9330_NUM_IRQS, + WCD9XXX_IRQ_RESERVED_2 = WCD9330_NUM_IRQS, +}; + +enum { + TABLA_NUM_IRQS = WCD9310_NUM_IRQS, + SITAR_NUM_IRQS = WCD9310_NUM_IRQS, + TAIKO_NUM_IRQS = WCD9XXX_NUM_IRQS, + TAPAN_NUM_IRQS = WCD9306_NUM_IRQS, + TOMTOM_NUM_IRQS = WCD9330_NUM_IRQS, +}; + +struct intr_data { + int intr_num; + bool clear_first; +}; + +struct wcd9xxx_core_resource { + struct mutex irq_lock; + struct mutex nested_irq_lock; + + enum wcd9xxx_pm_state pm_state; + struct mutex pm_lock; + /* pm_wq notifies change of pm_state */ + wait_queue_head_t pm_wq; + struct pm_qos_request pm_qos_req; + int wlock_holders; + + + /* holds the table of interrupts per codec */ + const struct intr_data *intr_table; + int intr_table_size; + unsigned int irq_base; + unsigned int irq; + u8 irq_masks_cur[WCD9XXX_MAX_IRQ_REGS]; + u8 irq_masks_cache[WCD9XXX_MAX_IRQ_REGS]; + bool irq_level_high[WCD9XXX_MAX_NUM_IRQS]; + int num_irqs; + int num_irq_regs; + u16 intr_reg[WCD9XXX_INTR_REG_MAX]; + struct regmap *wcd_core_regmap; + + /* Pointer to parent container data structure */ + void *parent; + + struct device *dev; + struct irq_domain *domain; +}; + +/* + * data structure for Slimbus and I2S channel. + * Some of fields are only used in smilbus mode + */ +struct wcd9xxx_ch { + u32 sph; /* share channel handle - slimbus only */ + u32 ch_num; /* + * vitrual channel number, such as 128 -144. + * apply for slimbus only + */ + u16 ch_h; /* chanel handle - slimbus only */ + u16 port; /* + * tabla port for RX and TX + * such as 0-9 for TX and 10 -16 for RX + * apply for both i2s and slimbus + */ + u16 shift; /* + * shift bit for RX and TX + * apply for both i2s and slimbus + */ + struct list_head list; /* + * channel link list + * apply for both i2s and slimbus + */ +}; + +struct wcd9xxx_codec_dai_data { + u32 rate; /* sample rate */ + u32 bit_width; /* sit width 16,24,32 */ + struct list_head wcd9xxx_ch_list; /* channel list */ + u16 grph; /* slimbus group handle */ + unsigned long ch_mask; + wait_queue_head_t dai_wait; + bool bus_down_in_recovery; +}; + +#define WCD9XXX_CH(xport, xshift) \ + {.port = xport, .shift = xshift} + +enum wcd9xxx_chipid_major { + TABLA_MAJOR = cpu_to_le16(0x100), + SITAR_MAJOR = cpu_to_le16(0x101), + TAIKO_MAJOR = cpu_to_le16(0x102), + TAPAN_MAJOR = cpu_to_le16(0x103), + TOMTOM_MAJOR = cpu_to_le16(0x105), + TASHA_MAJOR = cpu_to_le16(0x0), + TASHA2P0_MAJOR = cpu_to_le16(0x107), + TAVIL_MAJOR = cpu_to_le16(0x108), +}; + +enum codec_power_states { + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD_REGION_POWER_COLLAPSE_BEGIN, + WCD_REGION_POWER_DOWN, +}; + +enum wcd_power_regions { + WCD9XXX_DIG_CORE_REGION_1, + WCD9XXX_MAX_PWR_REGIONS, +}; + +struct wcd9xxx_codec_type { + u16 id_major; + u16 id_minor; + struct mfd_cell *dev; + int size; + int num_irqs; + int version; /* -1 to retrieve version from chip version register */ + enum wcd9xxx_slim_slave_addr_type slim_slave_type; + u16 i2c_chip_status; + const struct intr_data *intr_tbl; + int intr_tbl_size; + u16 intr_reg[WCD9XXX_INTR_REG_MAX]; +}; + +struct wcd9xxx_power_region { + enum codec_power_states power_state; + u16 pwr_collapse_reg_min; + u16 pwr_collapse_reg_max; +}; + +struct wcd9xxx { + struct device *dev; + struct slim_device *slim; + struct slim_device *slim_slave; + struct mutex io_lock; + struct mutex xfer_lock; + struct mutex reset_lock; + u8 version; + + int reset_gpio; + struct device_node *wcd_rst_np; + + int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *dest, bool interface_reg); + int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src, bool interface_reg); + int (*multi_reg_write)(struct wcd9xxx *wcd9xxx, const void *data, + size_t count); + int (*dev_down)(struct wcd9xxx *wcd9xxx); + int (*post_reset)(struct wcd9xxx *wcd9xxx); + + void *ssr_priv; + bool dev_up; + + u32 num_of_supplies; + struct regulator_bulk_data *supplies; + + struct wcd9xxx_core_resource core_res; + + u16 id_minor; + u16 id_major; + + /* Slimbus or I2S port */ + u32 num_rx_port; + u32 num_tx_port; + struct wcd9xxx_ch *rx_chs; + struct wcd9xxx_ch *tx_chs; + u32 mclk_rate; + enum codec_variant type; + struct regmap *regmap; + + struct wcd9xxx_codec_type *codec_type; + bool prev_pg_valid; + u8 prev_pg; + u8 avoid_cdc_rstlow; + struct wcd9xxx_power_region *wcd9xxx_pwr[WCD9XXX_MAX_PWR_REGIONS]; +}; + +struct wcd9xxx_reg_val { + unsigned short reg; /* register address */ + u8 *buf; /* buffer to be written to reg. addr */ + int bytes; /* number of bytes to be written */ +}; + +int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg); +int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg, + u8 val); +int wcd9xxx_get_logical_addresses(u8 *pgd_la, u8 *inf_la); +int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg, + int bytes, void *src); +int wcd9xxx_slim_reserve_bw(struct wcd9xxx *wcd9xxx, + u32 bw_ops, bool commit); +int wcd9xxx_set_power_state(struct wcd9xxx *wcd9xxx, enum codec_power_states, + enum wcd_power_regions); +int wcd9xxx_get_current_power_state(struct wcd9xxx *wcd9xxx, + enum wcd_power_regions); + +int wcd9xxx_page_write(struct wcd9xxx *wcd9xxx, unsigned short *reg); + +int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + unsigned int size, bool interface); + +extern int wcd9xxx_core_res_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + int num_irqs, int num_irq_regs, struct regmap *wcd_regmap); + +extern void wcd9xxx_core_res_deinit( + struct wcd9xxx_core_resource *wcd9xxx_core_res); + +extern int wcd9xxx_core_res_suspend( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + pm_message_t pmesg); + +extern int wcd9xxx_core_res_resume( + struct wcd9xxx_core_resource *wcd9xxx_core_res); + +extern int wcd9xxx_core_irq_init( + struct wcd9xxx_core_resource *wcd9xxx_core_res); + +extern int wcd9xxx_assign_irq(struct wcd9xxx_core_resource *wcd9xxx_core_res, + unsigned int irq, + unsigned int irq_base); + +extern enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void); +extern void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status); + +extern enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg( + struct wcd9xxx_core_resource *wcd9xxx_core_res, + enum wcd9xxx_pm_state o, + enum wcd9xxx_pm_state n); +static inline int __init wcd9xxx_irq_of_init(struct device_node *node, + struct device_node *parent) +{ + return 0; +} + +int wcd9xxx_init(void); +void wcd9xxx_exit(void); +#endif diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h new file mode 100644 index 000000000000..f188e850b800 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/pdata.h @@ -0,0 +1,197 @@ +/* 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. + */ + +#ifndef __MFD_WCD9XXX_PDATA_H__ + +#define __MFD_WCD9XXX_PDATA_H__ + +#include +#include + +#define MICBIAS_EXT_BYP_CAP 0x00 +#define MICBIAS_NO_EXT_BYP_CAP 0x01 + +#define SITAR_LDOH_1P95_V 0x0 +#define SITAR_LDOH_2P35_V 0x1 +#define SITAR_LDOH_2P75_V 0x2 +#define SITAR_LDOH_2P85_V 0x3 + +#define SITAR_CFILT1_SEL 0x0 +#define SITAR_CFILT2_SEL 0x1 +#define SITAR_CFILT3_SEL 0x2 + +#define WCD9XXX_LDOH_1P95_V 0x0 +#define WCD9XXX_LDOH_2P35_V 0x1 +#define WCD9XXX_LDOH_2P75_V 0x2 +#define WCD9XXX_LDOH_2P85_V 0x3 +#define WCD9XXX_LDOH_3P0_V 0x3 + +#define TABLA_LDOH_1P95_V 0x0 +#define TABLA_LDOH_2P35_V 0x1 +#define TABLA_LDOH_2P75_V 0x2 +#define TABLA_LDOH_2P85_V 0x3 + +#define TABLA_CFILT1_SEL 0x0 +#define TABLA_CFILT2_SEL 0x1 +#define TABLA_CFILT3_SEL 0x2 + +#define MAX_AMIC_CHANNEL 7 + +#define TABLA_OCP_300_MA 0x0 +#define TABLA_OCP_350_MA 0x2 +#define TABLA_OCP_365_MA 0x3 +#define TABLA_OCP_150_MA 0x4 +#define TABLA_OCP_190_MA 0x6 +#define TABLA_OCP_220_MA 0x7 + +#define TABLA_DCYCLE_255 0x0 +#define TABLA_DCYCLE_511 0x1 +#define TABLA_DCYCLE_767 0x2 +#define TABLA_DCYCLE_1023 0x3 +#define TABLA_DCYCLE_1279 0x4 +#define TABLA_DCYCLE_1535 0x5 +#define TABLA_DCYCLE_1791 0x6 +#define TABLA_DCYCLE_2047 0x7 +#define TABLA_DCYCLE_2303 0x8 +#define TABLA_DCYCLE_2559 0x9 +#define TABLA_DCYCLE_2815 0xA +#define TABLA_DCYCLE_3071 0xB +#define TABLA_DCYCLE_3327 0xC +#define TABLA_DCYCLE_3583 0xD +#define TABLA_DCYCLE_3839 0xE +#define TABLA_DCYCLE_4095 0xF + +#define WCD9XXX_MCLK_CLK_12P288MHZ 12288000 +#define WCD9XXX_MCLK_CLK_9P6HZ 9600000 + +/* Only valid for 9.6 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_600KHZ 600000 +#define WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ 2400000 +#define WCD9XXX_DMIC_SAMPLE_RATE_3P2MHZ 3200000 +#define WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ 4800000 + +/* Only valid for 12.288 MHz mclk */ +#define WCD9XXX_DMIC_SAMPLE_RATE_768KHZ 768000 +#define WCD9XXX_DMIC_SAMPLE_RATE_2P048MHZ 2048000 +#define WCD9XXX_DMIC_SAMPLE_RATE_3P072MHZ 3072000 +#define WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ 4096000 +#define WCD9XXX_DMIC_SAMPLE_RATE_6P144MHZ 6144000 + +#define WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED 0 + +#define WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED 0 + +struct wcd9xxx_amic { + /*legacy mode, txfe_enable and txfe_buff take 7 input + * each bit represent the channel / TXFE number + * and numbered as below + * bit 0 = channel 1 / TXFE1_ENABLE / TXFE1_BUFF + * bit 1 = channel 2 / TXFE2_ENABLE / TXFE2_BUFF + * ... + * bit 7 = channel 7 / TXFE7_ENABLE / TXFE7_BUFF + */ + u8 legacy_mode:MAX_AMIC_CHANNEL; + u8 txfe_enable:MAX_AMIC_CHANNEL; + u8 txfe_buff:MAX_AMIC_CHANNEL; + u8 use_pdata:MAX_AMIC_CHANNEL; +}; + +/* Each micbias can be assigned to one of three cfilters + * Vbatt_min >= .15V + ldoh_v + * ldoh_v >= .15v + cfiltx_mv + * If ldoh_v = 1.95 160 mv < cfiltx_mv < 1800 mv + * If ldoh_v = 2.35 200 mv < cfiltx_mv < 2200 mv + * If ldoh_v = 2.75 240 mv < cfiltx_mv < 2600 mv + * If ldoh_v = 2.85 250 mv < cfiltx_mv < 2700 mv + */ + +struct wcd9xxx_micbias_setting { + u8 ldoh_v; + u32 cfilt1_mv; /* in mv */ + u32 cfilt2_mv; /* in mv */ + u32 cfilt3_mv; /* in mv */ + u32 micb1_mv; + u32 micb2_mv; + u32 micb3_mv; + u32 micb4_mv; + /* Different WCD9xxx series codecs may not + * have 4 mic biases. If a codec has fewer + * mic biases, some of these properties will + * not be used. + */ + u8 bias1_cfilt_sel; + u8 bias2_cfilt_sel; + u8 bias3_cfilt_sel; + u8 bias4_cfilt_sel; + u8 bias1_cap_mode; + u8 bias2_cap_mode; + u8 bias3_cap_mode; + u8 bias4_cap_mode; + bool bias2_is_headset_only; +}; + +struct wcd9xxx_ocp_setting { + unsigned int use_pdata:1; /* 0 - use sys default as recommended */ + unsigned int num_attempts:4; /* up to 15 attempts */ + unsigned int run_time:4; /* in duty cycle */ + unsigned int wait_time:4; /* in duty cycle */ + unsigned int hph_ocp_limit:3; /* Headphone OCP current limit */ +}; + +#define WCD9XXX_MAX_REGULATOR 9 +/* + * format : TABLA__CUR_MAX + * + * from Tabla objective spec + */ + +#define WCD9XXX_CDC_VDDA_CP_CUR_MAX 500000 +#define WCD9XXX_CDC_VDDA_RX_CUR_MAX 20000 +#define WCD9XXX_CDC_VDDA_TX_CUR_MAX 20000 +#define WCD9XXX_VDDIO_CDC_CUR_MAX 5000 + +#define WCD9XXX_VDDD_CDC_D_CUR_MAX 5000 +#define WCD9XXX_VDDD_CDC_A_CUR_MAX 5000 + +#define WCD9XXX_VDD_SPKDRV_NAME "cdc-vdd-spkdrv" +#define WCD9XXX_VDD_SPKDRV2_NAME "cdc-vdd-spkdrv-2" + +struct wcd9xxx_regulator { + const char *name; + int min_uV; + int max_uV; + int optimum_uA; + bool ondemand; + struct regulator *regulator; +}; + +struct wcd9xxx_pdata { + int irq; + int irq_base; + int num_irqs; + int reset_gpio; + struct device_node *wcd_rst_np; + struct wcd9xxx_amic amic_settings; + struct slim_device slimbus_slave_device; + struct wcd9xxx_micbias_setting micbias; + struct wcd9xxx_ocp_setting ocp; + struct cdc_regulator *regulator; + int num_supplies; + u32 mclk_rate; + u32 dmic_sample_rate; + u32 mad_dmic_sample_rate; + u32 ecpp_dmic_sample_rate; + u32 dmic_clk_drv; + u16 use_pinctrl; +}; + +#endif diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-irq.h b/include/linux/mfd/wcd9xxx/wcd9xxx-irq.h new file mode 100644 index 000000000000..99ce60383cc2 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/wcd9xxx-irq.h @@ -0,0 +1,37 @@ +/* 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 + +#ifndef __MFD_WCD9XXX_IRQ_H +#define __MFD_WCD9XXX_IRQ_H +bool wcd9xxx_lock_sleep(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_unlock_sleep(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *wcd9xxx_res); +int wcd9xxx_request_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq, + irq_handler_t handler, const char *name, void *data); + +void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq, void *data); +void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq); +void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq); +void wcd9xxx_disable_irq_sync(struct wcd9xxx_core_resource *wcd9xxx_res, + int irq); + +int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res); +void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res); +int wcd9xxx_irq_drv_init(void); +void wcd9xxx_irq_drv_exit(void); +#endif diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h new file mode 100644 index 000000000000..96fdb00a2e03 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h @@ -0,0 +1,119 @@ +/* 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 __WCD9310_SLIMSLAVE_H_ +#define __WCD9310_SLIMSLAVE_H_ + +#include +#include + + +/* + * client is expected to give port ids in the range of + * 1-10 for pre Taiko Tx ports and 1-16 for Taiko + * 1-7 for pre Taiko Rx ports and 1-16 for Tako, + * we need to add offset for getting the absolute slave + * port id before configuring the HW + */ +#define TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS 10 +#define TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS 16 + +#define SLIM_MAX_TX_PORTS TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS + +#define TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS \ + TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS +#define TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS \ + TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS + +#define TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS 7 +#define TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS 13 + +#define SLIM_MAX_RX_PORTS TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS + +#define SLIM_MAX_REG_ADDR (0x180 + 4 * (SLIM_MAX_RX_PORTS)) + +#define TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID \ + TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS +#define TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID \ + TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS + +#define TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID 16 +#define TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID 31 + +#define TABLA_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID 9 +#define TAIKO_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID 15 + +/* below details are taken from SLIMBUS slave SWI */ +#define SB_PGD_PORT_BASE 0x000 + +#define SB_PGD_PORT_CFG_BYTE_ADDR(offset, port_num) \ + (SB_PGD_PORT_BASE + offset + (1 * port_num)) + +#define SB_PGD_TX_PORT_MULTI_CHANNEL_0(port_num) \ + (SB_PGD_PORT_BASE + 0x100 + 4*port_num) +#define SB_PGD_TX_PORT_MULTI_CHANNEL_0_START_PORT_ID 0 +#define SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID 7 + +#define SB_PGD_TX_PORT_MULTI_CHANNEL_1(port_num) \ + (SB_PGD_PORT_BASE + 0x101 + 4*port_num) +#define SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID 8 + +#define SB_PGD_RX_PORT_MULTI_CHANNEL_0(offset, port_num) \ + (SB_PGD_PORT_BASE + offset + (4 * port_num)) + +/* slave port water mark level + * (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes) + */ +#define SLAVE_PORT_WATER_MARK_6BYTES 0 +#define SLAVE_PORT_WATER_MARK_9BYTES 1 +#define SLAVE_PORT_WATER_MARK_12BYTES 2 +#define SLAVE_PORT_WATER_MARK_15BYTES 3 +#define SLAVE_PORT_WATER_MARK_SHIFT 1 +#define SLAVE_PORT_ENABLE 1 +#define SLAVE_PORT_DISABLE 0 +#define WATER_MARK_VAL \ + ((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \ + (SLAVE_PORT_ENABLE)) +#define BASE_CH_NUM 128 + + +int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, + u8 wcd9xxx_pgd_la, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot); + +int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx); + +int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph); +int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, + unsigned int rate, unsigned int bit_width, + u16 *grph); +int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph); +int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph); +int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx, + unsigned int *rx_ch, + unsigned int *tx_ch); +int wcd9xxx_get_slave_port(unsigned int ch_num); +int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx, + struct list_head *wcd9xxx_ch_list, u16 grph); +int wcd9xxx_rx_vport_validation(u32 port_id, + struct list_head *codec_dai_list); +int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id, + struct wcd9xxx_codec_dai_data *codec_dai, + u32 num_codec_dais); +#endif /* __WCD9310_SLIMSLAVE_H_ */ diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-utils.h b/include/linux/mfd/wcd9xxx/wcd9xxx-utils.h new file mode 100644 index 000000000000..7a13dd19e8c0 --- /dev/null +++ b/include/linux/mfd/wcd9xxx/wcd9xxx-utils.h @@ -0,0 +1,40 @@ +/* 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 __WCD9XXX_UTILS_H__ +#define __WCD9XXX_UTILS_H__ + +#include +#include +#include +#include +#include + +struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev); +int wcd9xxx_bringup(struct device *dev); +int wcd9xxx_bringdown(struct device *dev); +struct regmap *wcd9xxx_regmap_init(struct device *dev, + const struct regmap_config *config); +int wcd9xxx_reset(struct device *dev); +int wcd9xxx_reset_low(struct device *dev); +int wcd9xxx_get_codec_info(struct device *dev); + +typedef int (*codec_bringup_fn)(struct wcd9xxx *); +typedef int (*codec_bringdown_fn)(struct wcd9xxx *); +typedef int (*codec_type_fn)(struct wcd9xxx *, + struct wcd9xxx_codec_type *); + +codec_bringdown_fn wcd9xxx_bringdown_fn(int type); +codec_bringup_fn wcd9xxx_bringup_fn(int type); +codec_type_fn wcd9xxx_get_codec_info_fn(int type); + +#endif 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..26d1a4c56ebc --- /dev/null +++ b/include/linux/qdsp6v2/apr_tal.h @@ -0,0 +1,90 @@ +/* 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 +#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); + + +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; +}; + +#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..b6f4e56db7e7 --- /dev/null +++ b/include/linux/qdsp6v2/rtac.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2011, 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 __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); +void rtac_update_afe_topology(u32 port_id); +#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/linux/soundwire/soundwire.h b/include/linux/soundwire/soundwire.h new file mode 100644 index 000000000000..a60d78cbd32b --- /dev/null +++ b/include/linux/soundwire/soundwire.h @@ -0,0 +1,312 @@ +/* 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 _LINUX_SOUNDWIRE_H +#define _LINUX_SOUNDWIRE_H +#include +#include +#include + +extern struct bus_type soundwire_type; + +/* Soundwire supports max. of 8 channels per port */ +#define SWR_MAX_CHANNEL_NUM 8 +/* Soundwire supports max. of 14 ports on each device */ +#define SWR_MAX_DEV_PORT_NUM 14 +/* Maximum number of slave devices that a master can control */ +#define SWR_MAX_DEV_NUM 11 +/* Maximum number of ports on master so that it can accommodate all the port + * configurations of all devices + */ +#define SWR_MAX_MSTR_PORT_NUM (SWR_MAX_DEV_NUM * SWR_MAX_DEV_PORT_NUM) + +/* Indicates soundwire devices group information */ +enum { + SWR_GROUP_NONE = 0, + SWR_GROUP_12 = 12, + SWR_GROUP_13 = 13, + SWR_BROADCAST = 15, +}; + +/* + * struct swr_port_info - represent soundwire frame shape + * @dev_id: logical device number of the soundwire slave device + * @port_en: flag indicates whether the port is enabled + * @port_id: logical port number of the soundwire slave device + * @offset1: sample offset indicating the offset of the channel + * from the start of the frame + * @offset2: channel offset indicating offset between to channels + * @sinterval: sample interval indicates spacing from one sample + * event to the next + * @ch_en: channels in a port that need to be enabled + * @num_ch: number of channels enabled in a port + * @ch_rate: sampling rate of the channel with which data will be + * transferred + * + * Soundwire frame shape is created based on swr_port_info struct + * parameters. + */ +struct swr_port_info { + u8 dev_id; + u8 port_en; + u8 port_id; + u8 offset1; + u8 offset2; + u8 sinterval; + u8 ch_en; + u8 num_ch; + u32 ch_rate; +}; + +/* + * struct swr_params - represent transfer of data from soundwire slave + * to soundwire master + * @tid: transaction ID to track each transaction + * @dev_id: logical device number of the soundwire slave device + * @num_port: number of ports that needs to be configured + * @port_id: array of logical port numbers of the soundwire slave device + * @num_ch: array of number of channels enabled + * @ch_rate: array of sampling rate of different channels that need to + * be configured + * @ch_en: array of channels mask for all the ports + */ +struct swr_params { + u8 tid; + u8 dev_id; + u8 num_port; + u8 port_id[SWR_MAX_DEV_PORT_NUM]; + u8 num_ch[SWR_MAX_DEV_PORT_NUM]; + u32 ch_rate[SWR_MAX_DEV_PORT_NUM]; + u8 ch_en[SWR_MAX_DEV_PORT_NUM]; +}; + +/* + * struct swr_reg - struct to handle soundwire slave register read/writes + * @tid: transaction id for reg read/writes + * @dev_id: logical device number of the soundwire slave device + * @regaddr: 16 bit regaddr of soundwire slave + * @buf: value to be written/read to/from regaddr + * @len: length of the buffer buf + */ +struct swr_reg { + u8 tid; + u8 dev_id; + u32 regaddr; + u32 *buf; + u32 len; +}; + +/* + * struct swr_master - Interface to the soundwire master controller + * @dev: device interface to this driver + * @list: link with other soundwire master controllers + * @bus_num: board/SoC specific identifier for a soundwire master + * @mlock: mutex protecting master data structures + * @devices: list of devices on this master + * @port: logical port numbers of the soundwire master. This array + * can hold maximum master ports which is equal to number of slave + * devices multiplied by number of ports in each slave device + * @port_txn: table of port config transactions with transaction id + * @reg_txn: table of register transactions with transaction id + * @last_tid: size of table port_txn (can't grow beyond 256 since + * tid is 8 bits) + * @num_port: number of active ports on soundwire master + * @gr_sid: slave id used by the group for write operations + * @connect_port: callback for configuration of soundwire port(s) + * @disconnect_port: callback for disable of soundwire port(s) + * @read: callback for soundwire slave register read + * @write: callback for soundwire slave register write + * @get_logical_dev_num: callback to get soundwire slave logical + * device number + */ +struct swr_master { + struct device dev; + struct list_head list; + unsigned int bus_num; + struct mutex mlock; + struct list_head devices; + struct swr_port_info port[SWR_MAX_MSTR_PORT_NUM]; + struct swr_params **port_txn; + struct swr_reg **reg_txn; + u8 last_tid; + u8 num_port; + u8 num_dev; + u8 gr_sid; + int (*connect_port)(struct swr_master *mstr, struct swr_params *txn); + int (*disconnect_port)(struct swr_master *mstr, struct swr_params *txn); + int (*read)(struct swr_master *mstr, u8 dev_num, u16 reg_addr, + void *buf, u32 len); + int (*write)(struct swr_master *mstr, u8 dev_num, u16 reg_addr, + const void *buf); + int (*bulk_write)(struct swr_master *master, u8 dev_num, void *reg, + const void *buf, size_t len); + int (*get_logical_dev_num)(struct swr_master *mstr, u64 dev_id, + u8 *dev_num); + void (*slvdev_datapath_control)(struct swr_master *mstr, bool enable); + bool (*remove_from_group)(struct swr_master *mstr); +}; + +static inline struct swr_master *to_swr_master(struct device *dev) +{ + return dev ? container_of(dev, struct swr_master, dev) : NULL; +} + +/* + * struct swr_device - represent a soundwire slave device + * @name: indicates the name of the device, defined in devicetree + * binding under soundwire slave device node as a compatible field. + * @master: soundwire master managing the bus hosting this device + * @driver: Device's driver. Pointer to access routines + * @dev_list: list of devices on a controller + * @dev_num: logical device number of the soundwire slave device + * @dev: driver model representation of the device + * @addr: represents "ea-addr" which is unique-id of soundwire slave + * device + * @group_id: group id supported by the slave device + */ +struct swr_device { + char name[SOUNDWIRE_NAME_SIZE]; + struct swr_master *master; + struct swr_driver *driver; + struct list_head dev_list; + u8 dev_num; + struct device dev; + unsigned long addr; + u8 group_id; +}; + +static inline struct swr_device *to_swr_device(struct device *dev) +{ + return dev ? container_of(dev, struct swr_device, dev) : NULL; +} + +/* + * struct swr_driver - Manage soundwire slave device driver + * @probe: binds this driver to soundwire device + * @remove: unbinds this driver from soundwire device + * @shutdown: standard shutdown callback used during power down/halt + * @suspend: standard suspend callback used during system suspend + * @resume: standard resume callback used during system resume + * @driver: soundwire device drivers should initialize name and + * owner field of this structure + * @id_table: list of soundwire devices supported by this driver + */ +struct swr_driver { + int (*probe)(struct swr_device *swr); + int (*remove)(struct swr_device *swr); + void (*shutdown)(struct swr_device *swr); + int (*suspend)(struct swr_device *swr, pm_message_t pmesg); + int (*resume)(struct swr_device *swr); + int (*device_up)(struct swr_device *swr); + int (*device_down)(struct swr_device *swr); + int (*reset_device)(struct swr_device *swr); + struct device_driver driver; + const struct swr_device_id *id_table; +}; + +static inline struct swr_driver *to_swr_driver(struct device_driver *drv) +{ + return drv ? container_of(drv, struct swr_driver, driver) : NULL; +} + +/* + * struct swr_boardinfo - Declare board info for soundwire device bringup + * @name: name to initialize swr_device.name + * @bus_num: identifies which soundwire master parents the soundwire + * slave_device + * @addr: represents "ea-addr" of soundwire slave device + * @of_node: pointer to OpenFirmware device node + * @swr_slave: device to be registered with soundwire + */ +struct swr_boardinfo { + char name[SOUNDWIRE_NAME_SIZE]; + int bus_num; + u64 addr; + struct device_node *of_node; + struct swr_device *swr_slave; +}; + +static inline void *swr_get_ctrl_data(const struct swr_master *master) +{ + return master ? dev_get_drvdata(&master->dev) : NULL; +} + +static inline void swr_set_ctrl_data(struct swr_master *master, void *data) +{ + dev_set_drvdata(&master->dev, data); +} + +static inline void *swr_get_dev_data(const struct swr_device *dev) +{ + return dev ? dev_get_drvdata(&dev->dev) : NULL; +} + +static inline void swr_set_dev_data(struct swr_device *dev, void *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +extern int swr_startup_devices(struct swr_device *swr_dev); + +extern struct swr_device *swr_new_device(struct swr_master *master, + struct swr_boardinfo const *info); + +extern int of_register_swr_devices(struct swr_master *master); + +extern void swr_port_response(struct swr_master *mstr, u8 tid); + +extern int swr_get_logical_dev_num(struct swr_device *dev, u64 dev_id, + u8 *dev_num); + +extern int swr_read(struct swr_device *dev, u8 dev_num, u16 reg_addr, + void *buf, u32 len); + +extern int swr_write(struct swr_device *dev, u8 dev_num, u16 reg_addr, + const void *buf); + +extern int swr_bulk_write(struct swr_device *dev, u8 dev_num, void *reg_addr, + const void *buf, size_t len); + +extern int swr_connect_port(struct swr_device *dev, u8 *port_id, u8 num_port, + u8 *ch_mask, u32 *ch_rate, u8 *num_ch); + +extern int swr_disconnect_port(struct swr_device *dev, + u8 *port_id, u8 num_port); + +extern int swr_set_device_group(struct swr_device *swr_dev, u8 id); + +extern int swr_driver_register(struct swr_driver *drv); + +extern void swr_driver_unregister(struct swr_driver *drv); + +extern int swr_add_device(struct swr_master *master, + struct swr_device *swrdev); +extern void swr_remove_device(struct swr_device *swr); + +extern void swr_master_add_boarddevices(struct swr_master *master); + +extern void swr_unregister_master(struct swr_master *master); + +extern int swr_register_master(struct swr_master *master); + +extern int swr_device_up(struct swr_device *swr_dev); + +extern int swr_device_down(struct swr_device *swr_dev); + +extern int swr_reset_device(struct swr_device *swr_dev); + +extern int swr_slvdev_datapath_control(struct swr_device *swr_dev, u8 dev_num, + bool enable); +extern int swr_remove_from_group(struct swr_device *dev, u8 dev_num); + +extern void swr_remove_device(struct swr_device *swr_dev); +#endif /* _LINUX_SOUNDWIRE_H */ diff --git a/include/linux/soundwire/swr-wcd.h b/include/linux/soundwire/swr-wcd.h new file mode 100644 index 000000000000..041b901fd954 --- /dev/null +++ b/include/linux/soundwire/swr-wcd.h @@ -0,0 +1,35 @@ +/* Copyright (c) 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_SWR_WCD_H +#define _LINUX_SWR_WCD_H +#include +#include +#include +#include + +enum { + SWR_CH_MAP, + SWR_DEVICE_DOWN, + SWR_DEVICE_UP, + SWR_SUBSYS_RESTART, + SWR_SET_NUM_RX_CH, +}; + +struct swr_mstr_port { + int num_port; + u8 *port; +}; + +extern int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data); + +#endif /* _LINUX_SWR_WCD_H */ diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h new file mode 100644 index 000000000000..14f644579430 --- /dev/null +++ b/include/sound/apr_audio-v2.h @@ -0,0 +1,10683 @@ +/* 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 +#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 + +#define ADM_MATRIX_ID_COMPRESSED_AUDIO_TX 3 + +#define ADM_MATRIX_ID_LISTEN_TX 4 +/* 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 + +/* This command allows a client to open a COPP/Voice Proc the + * way as ADM_CMD_DEVICE_OPEN_V5 but supports multiple endpoint2 + * channels. + * + * @return + * #ADM_CMDRSP_DEVICE_OPEN_V6 with the resulting status and + * COPP ID. + */ +#define ADM_CMD_DEVICE_OPEN_V6 0x00010356 + +/* 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; + +/* ADM device open command payload of the + * #ADM_CMD_DEVICE_OPEN_V6 command. + */ +struct adm_cmd_device_open_v6 { + 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. + */ + + u16 dev_num_channel_eid2; +/* Number of channels the voice processor block sends + * to/receives from the endpoint2. + * Supported values: 1 to 8. + * The value is ignored for audio COPP or if endpoint_id_2 is + * set to 0xFFFF. + */ + + u16 bit_width_eid2; +/* Bit width (in bits) that the voice processor sends + * to/receives from the endpoint2. + * Supported values: 16 and 24. + * The value is ignored for audio COPP or if endpoint_id_2 is + * set to 0xFFFF. + */ + + u32 sample_rate_eid2; +/* Sampling rate at which the voice processor Tx block + * interfaces with the endpoint2. + * Supported values for Tx voice processor: >0 and <=384 kHz + * The value is ignored for audio COPP or if endpoint_id_2 is + * set to 0xFFFF. + */ + + u8 dev_channel_mapping_eid2[8]; +/* Array of channel mapping of buffers that the voice processor + * 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 the Tx voice processor. + * The values are ignored for audio COPP or if endpoint_id_2 is + * set to 0xFFFF. + */ +} __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; + +#define ASM_STREAM_CMD_REGISTER_PP_EVENTS 0x00013213 +#define ASM_STREAM_PP_EVENT 0x00013214 +#define ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE 0x13333 +#define ASM_IEC_61937_MEDIA_FMT_EVENT 0x13334 + +#define DSP_STREAM_CMD "ADSP Stream Cmd" +#define DSP_STREAM_CALLBACK "ADSP Stream Callback Event" +#define DSP_STREAM_CALLBACK_QUEUE_SIZE 1024 + +struct dsp_stream_callback_list { + struct list_head list; + struct msm_adsp_event_data event; +}; + +struct dsp_stream_callback_prtd { + uint16_t event_count; + struct list_head event_queue; + spinlock_t prtd_spin_lock; +}; + +/* 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; + +/* Returns the status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V6 command. */ +#define ADM_CMDRSP_DEVICE_OPEN_V6 0x00010357 + +/* Payload of the #ADM_CMDRSP_DEVICE_OPEN_V6 message, + * which returns the + * status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V6 command + * is the exact same as ADM_CMDRSP_DEVICE_OPEN_V5. + */ + +/* 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 +#define MAX_SIDETONE_IIR_DATA_SIZE 224 +#define MAX_NO_IIR_FILTER_STAGE 10 + +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 + */ + uint8_t iir_config[MAX_SIDETONE_IIR_DATA_SIZE]; +} __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; + +struct afe_loopback_sidetone_gain { + u16 rx_port_id; + u16 gain; +} __packed; + +struct loopback_cfg_data { + 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; + +struct afe_st_loopback_cfg_v1 { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 gain_pdata; + struct afe_loopback_sidetone_gain gain_data; + struct afe_port_param_data_v2 cfg_pdata; + struct loopback_cfg_data cfg_data; +} __packed; + +struct afe_loopback_iir_cfg_v2 { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 st_iir_enable_pdata; + struct afe_mod_enable_param st_iir_mode_enable_data; + struct afe_port_param_data_v2 st_iir_filter_config_pdata; + struct afe_sidetone_iir_filter_config_params st_iir_filter_config_data; +} __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_176P4K 176400 +#define AFE_PORT_SAMPLE_RATE_192K 192000 +#define AFE_PORT_SAMPLE_RATE_352P8K 352800 +#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 +#define AFE_GENERIC_COMPRESSED 0x8 + +/* 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 + + +/* ID of the parameter used to set the endianness value for the + * USB audio device. It should be used with + * AFE_MODULE_AUDIO_DEV_INTERFACE + */ +#define AFE_PARAM_ID_USB_AUDIO_DEV_LPCM_FMT 0x000102AA + +/* 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; + +struct afe_param_id_usb_audio_dev_lpcm_fmt { +/* Minor version used for tracking USB audio device parameter. + * Supported values: AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG + */ + u32 cfg_minor_version; +/* Endianness of actual end USB audio device */ + u32 endian; +} __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; +/* endianness of this interface */ + u32 endian; +} __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; + union { + struct afe_param_id_usb_audio_dev_params usb_dev; + struct afe_param_id_usb_audio_dev_lpcm_fmt lpcm_fmt; + }; +} __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 + * - #AFE_PORT_SAMPLE_RATE_176P4K + * - #AFE_PORT_SAMPLE_RATE_352P8K @tablebulletend + */ + + u32 bit_width; + /* < Bit width of the sample. + * @values 16, 24 + */ + + u16 data_format; + /* < Data format: linear ,compressed, generic compresssed + * @values + * - #AFE_LINEAR_PCM_DATA + * - #AFE_NON_LINEAR_DATA + * - #AFE_GENERIC_COMPRESSED + */ + + 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 COMPRESSED_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_MEDIA_FMT_GENERIC_COMPRESSED 0x00013212 + +#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_generic_compressed_fmt_blk_t { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmt_blk; + + /* + * Channel mapping array of bitstream output. + * Channel[i] mapping describes channel i inside the buffer, where + * i < num_channels. All valid used channels must be + * present at the beginning of the array. + */ + uint8_t channel_mapping[8]; + + /* + * Number of channels of the incoming bitstream. + * Supported values: 1,2,3,4,5,6,7,8 + */ + uint16_t num_channels; + + /* + * Nominal bits per sample value of the incoming bitstream. + * Supported values: 16, 32 + */ + uint16_t bits_per_sample; + + /* + * Nominal sampling rate of the incoming bitstream. + * Supported values: 8000, 11025, 16000, 22050, 24000, 32000, + * 44100, 48000, 88200, 96000, 176400, 192000, + * 352800, 384000 + */ + uint32_t sampling_rate; + +} __packed; + + +/* Command to send sample rate & channels for IEC61937 (compressed) or IEC60958 + * (pcm) streams. Both audio standards use the same format and are used for + * HDMI or SPDIF. + */ +#define ASM_DATA_CMD_IEC_60958_MEDIA_FMT 0x0001321E + +struct asm_iec_compressed_fmt_blk_t { + struct apr_hdr hdr; + + /* + * Nominal sampling rate of the incoming bitstream. + * Supported values: 8000, 11025, 16000, 22050, 24000, 32000, + * 44100, 48000, 88200, 96000, 176400, 192000, + * 352800, 384000 + */ + uint32_t sampling_rate; + + /* + * Number of channels of the incoming bitstream. + * Supported values: 1,2,3,4,5,6,7,8 + */ + uint32_t num_channels; + +} __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 + +/* Enumeration for the AAC LATM format. */ +#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_LATM 4 + +#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 +#define ASM_MEDIA_FMT_TRUEHD 0x00013215 +/* 0x0 is used for fomat ID since ADSP dynamically determines the + * format encapsulated in the IEC61937 (compressed) or IEC60958 + * (pcm) packets. + */ +#define ASM_MEDIA_FMT_IEC 0x00000000 + +/* 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_OPEN_TRANSCODE_LOOPBACK 0x00010DBA + +/* Bitmask for the stream's Performance mode. */ +#define ASM_BIT_MASK_STREAM_PERF_MODE_FLAG_IN_OPEN_TRANSCODE_LOOPBACK \ + (0x70000000UL) + +/* Bit shift for the stream's Performance mode. */ +#define ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_TRANSCODE_LOOPBACK 28 + +/* Bitmask for the decoder converter enable flag. */ +#define ASM_BIT_MASK_DECODER_CONVERTER_FLAG (0x00000078UL) + +/* Shift value for the decoder converter enable flag. */ +#define ASM_SHIFT_DECODER_CONVERTER_FLAG 3 + +/* Converter mode is None (Default). */ +#define ASM_CONVERTER_MODE_NONE 0 + +/* Converter mode is DDP-to-DD. */ +#define ASM_DDP_DD_CONVERTER_MODE 1 + +/* Identifies a special converter mode where source and sink formats + * are the same but postprocessing must applied. Therefore, Decode + * @rarrow Re-encode is necessary. + */ +#define ASM_POST_PROCESS_CONVERTER_MODE 2 + + +struct asm_stream_cmd_open_transcode_loopback_t { + struct apr_hdr hdr; + u32 mode_flags; +/* Mode Flags specifies the performance mode in which this stream + * is to be opened. + * Supported values{for bits 30 to 28}(stream_perf_mode flag) + * + * #ASM_LEGACY_STREAM_SESSION -- This mode ensures backward + * compatibility to the original behavior + * of ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK + * + * #ASM_LOW_LATENCY_STREAM_SESSION -- Opens a loopback session by using + * shortened buffers in low latency POPP + * - Recommendation: Do not enable high latency algorithms. They might + * negate the benefits of opening a low latency stream, and they + * might also suffer quality degradation from unexpected jitter. + * - This Low Latency mode is supported only for PCM In and PCM Out + * loopbacks. An error is returned if Low Latency mode is opened for + * other transcode loopback modes. + * - To configure this subfield, use + * ASM_BIT_MASK_STREAM_PERF_MODE_FLAG_IN_OPEN_TRANSCODE_LOOPBACK and + * ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_TRANSCODE_LOOPBACK. + * + * Supported values{for bits 6 to 3} (decoder-converter compatibility) + * #ASM_CONVERTER_MODE_NONE (0x0) -- Default + * #ASM_DDP_DD_CONVERTER_MODE (0x1) + * #ASM_POST_PROCESS_CONVERTER_MODE (0x2) + * 0x3-0xF -- Reserved for future use + * - Use #ASM_BIT_MASK_DECODER_CONVERTER_FLAG and + * ASM_SHIFT_DECODER_CONVERTER_FLAG to set this bit + * All other bits are reserved; clients must set them to 0. + */ + + u32 src_format_id; +/* Specifies the media format of the input audio stream. + * + * Supported values + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 + * - #ASM_MEDIA_FMT_DTS + * - #ASM_MEDIA_FMT_EAC3_DEC + * - #ASM_MEDIA_FMT_EAC3 + * - #ASM_MEDIA_FMT_AC3_DEC + * - #ASM_MEDIA_FMT_AC3 + */ + u32 sink_format_id; +/* Specifies the media format of the output stream. + * + * Supported values + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 + * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 + * - #ASM_MEDIA_FMT_DTS (not supported in Low Latency mode) + * - #ASM_MEDIA_FMT_EAC3_DEC (not supported in Low Latency mode) + * - #ASM_MEDIA_FMT_EAC3 (not supported in Low Latency mode) + * - #ASM_MEDIA_FMT_AC3_DEC (not supported in Low Latency mode) + * - #ASM_MEDIA_FMT_AC3 (not supported in Low Latency mode) + */ + + u32 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_MCH_PEAK_VOL + * - #ASM_STREAM_POSTPROC_TOPO_ID_NONE + * Topologies can be added through #ASM_CMD_ADD_TOPOLOGIES. + * This field is ignored for the Converter mode, in which no + * postprocessing is performed. + */ + + u16 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 0. Otherwise, an error is returned. + */ + + u16 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 0. Otherwise, an error is returned. + */ + + u16 bits_per_sample; +/* Number of bits per sample processed by the ASM modules. + * Supported values 16, 24 + */ + + u16 reserved; +/* This field must be set to 0. + */ +} __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_STREAM_CMD_SET_ENCDEC_PARAM_V2 0x00013218 + +struct asm_stream_cmd_set_encdec_param_v2 { + u16 service_id; + /* 0 - ASM_ENCODER_SVC; 1 - ASM_DECODER_SVC */ + + u16 reserved; + + 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; + +#define ASM_STREAM_CMD_REGISTER_ENCDEC_EVENTS 0x00013219 + +#define ASM_STREAM_CMD_ENCDEC_EVENTS 0x0001321A + +#define AVS_PARAM_ID_RTIC_SHARED_MEMORY_ADDR 0x00013237 + +struct avs_rtic_shared_mem_addr { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param_v2 encdec; + u32 shm_buf_addr_lsw; + /* Lower 32 bit of the RTIC shared memory */ + + u32 shm_buf_addr_msw; + /* Upper 32 bit of the RTIC shared memory */ + + u32 buf_size; + /* Size of buffer */ + + u16 shm_buf_mem_pool_id; + /* ADSP_MEMORY_MAP_SHMEM8_4K_POOL */ + + u16 shm_buf_num_regions; + /* number of regions to map */ + + u32 shm_buf_flag; + /* buffer property flag */ + + struct avs_shared_map_region_payload map_region; + /* memory map region*/ +} __packed; + +#define AVS_PARAM_ID_RTIC_EVENT_ACK 0x00013238 + +struct avs_param_rtic_event_ack { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param_v2 encdec; +} __packed; + +#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 + +/* Get & set params */ +#define VSS_ICOMMON_CMD_SET_PARAM_V2 0x0001133D +#define VSS_ICOMMON_CMD_GET_PARAM_V2 0x0001133E +#define VSS_ICOMMON_RSP_GET_PARAM 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; + +/* Opcode to set BT address and license for aptx decoder */ +#define APTX_DECODER_BT_ADDRESS 0x00013201 +#define APTX_CLASSIC_DEC_LICENSE_ID 0x00013202 + +struct aptx_dec_bt_addr_cfg { + uint32_t lap; + uint32_t uap; + uint32_t nap; +} __packed; + +struct aptx_dec_bt_dev_addr { + struct apr_hdr hdr; + struct asm_stream_cmd_set_encdec_param encdec; + struct aptx_dec_bt_addr_cfg bt_addr_cfg; +} __packed; + +struct asm_aptx_dec_fmt_blk_v2 { + struct apr_hdr hdr; + struct asm_data_cmd_media_fmt_update_v2 fmtblk; + u32 sample_rate; +/* Number of samples per second. + * Supported values: 44100 and 48000 Hz + */ +} __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_SESSION_EVENT_DETECTION_STATUS_V3 (0x00012B04) + +#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) +#define LSM_PARAM_ID_SWMAD_CFG (0x00012C18) +#define LSM_PARAM_ID_SWMAD_MODEL (0x00012C19) +#define LSM_PARAM_ID_SWMAD_ENABLE (0x00012C1A) +#define LSM_PARAM_ID_POLLING_ENABLE (0x00012C1B) +#define LSM_PARAM_ID_MEDIA_FMT (0x00012C1E) +#define LSM_PARAM_ID_FWK_MODE_CONFIG (0x00012C27) + +/* 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; + +/* ID of the parameter used by #AFE_MODULE_AUDIO_DEV_INTERFACE to specify + * the timing statistics of the corresponding device interface. + * Client can periodically query for the device time statistics to help adjust + * the PLL based on the drift value. The get param command must be sent to + * AFE port ID corresponding to device interface + + * This parameter ID supports following get param commands: + * #AFE_PORT_CMD_GET_PARAM_V2 and + * #AFE_PORT_CMD_GET_PARAM_V3. + */ +#define AFE_PARAM_ID_DEV_TIMING_STATS 0x000102AD + +/* Version information used to handle future additions to AFE device + * interface timing statistics (for backward compatibility). + */ +#define AFE_API_VERSION_DEV_TIMING_STATS 0x1 + +/* Enumeration for specifying a sink(Rx) device */ +#define AFE_SINK_DEVICE 0x0 + +/* Enumeration for specifying a source(Tx) device */ +#define AFE_SOURCE_DEVICE 0x1 + +/* Enumeration for specifying the drift reference is of type AV Timer */ +#define AFE_REF_TIMER_TYPE_AVTIMER 0x0 + +/* Message payload structure for the + * AFE_PARAM_ID_DEV_TIMING_STATS parameter. + */ +struct afe_param_id_dev_timing_stats { + /* Minor version used to track the version of device interface timing + * statistics. Currently, the supported version is 1. + * @values #AFE_API_VERSION_DEV_TIMING_STATS + */ + u32 minor_version; + + /* Indicates the device interface direction as either + * source (Tx) or sink (Rx). + * @values + * #AFE_SINK_DEVICE + * #AFE_SOURCE_DEVICE + */ + u16 device_direction; + + /* Reference timer for drift accumulation and time stamp information. + * @values + * #AFE_REF_TIMER_TYPE_AVTIMER @tablebulletend + */ + u16 reference_timer; + + /* + * Flag to indicate if resync is required on the client side for + * drift correction. Flag is set to TRUE for the first get_param + * response after device interface starts. This flag value can be + * used by client to identify if device interface restart has + * happened and if any re-sync is required at their end for drift + * correction. + * @values + * 0: FALSE (Resync not required) + * 1: TRUE (Resync required) @tablebulletend + */ + u32 resync_flag; + + /* Accumulated drift value in microseconds. This value is updated + * every 100th ms. + * Positive drift value indicates AV timer is running faster than device + * Negative drift value indicates AV timer is running slower than device + * @values Any valid int32 number + */ + s32 acc_drift_value; + + /* Lower 32 bits of the 64-bit absolute timestamp of reference + * timer in microseconds. + + * This timestamp corresponds to the time when the drift values + * are accumlated for every 100th ms. + * @values Any valid uint32 number + */ + u32 ref_timer_abs_ts_lsw; + + /* Upper 32 bits of the 64-bit absolute timestamp of reference + * timer in microseconds. + * This timestamp corresponds to the time when the drift values + * are accumlated for every 100th ms. + * @values Any valid uint32 number + */ + u32 ref_timer_abs_ts_msw; +} __packed; + +struct afe_av_dev_drift_get_param { + struct apr_hdr hdr; + struct afe_port_cmd_get_param_v2 get_param; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_dev_timing_stats timing_stats; +} __packed; + +struct afe_av_dev_drift_get_param_resp { + uint32_t status; + struct afe_port_param_data_v2 pdata; + struct afe_param_id_dev_timing_stats timing_stats; +} __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) + */ +}; + +/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC which allows the + * audio client choose the rendering decision that the audio DSP should use. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_MODE_CMD 0x00012F0D + +/* Indicates that rendering decision will be based on default rate + * (session clock based rendering, device driven). + * 1. The default session clock based rendering is inherently driven + * by the timing of the device. + * 2. After the initial decision is made (first buffer after a run + * command), subsequent data rendering decisions are made with + * respect to the rate at which the device is rendering, thus deriving + * its timing from the device. + * 3. While this decision making is simple, it has some inherent limitations + * (mentioned in the next section). + * 4. If this API is not set, the session clock based rendering will be assumed + * and this will ensure that the DSP is backward compatible. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT 0 + +/* Indicates that rendering decision will be based on local clock rate. + * 1. In the DSP loopback/client loopback use cases (frame based + * inputs), the incoming data into audio DSP is time-stamped at the + * local clock rate (STC). + * 2. This TS rate may match the incoming data rate or maybe different + * from the incoming data rate. + * 3. Regardless, the data will be time-stamped with local STC and + * therefore, the client is recommended to set this mode for these + * use cases. This method is inherently more robust to sequencing + * (AFE Start/Stop) and device switches, among other benefits. + * 4. This API will inform the DSP to compare every incoming buffer TS + * against local STC. + * 5. DSP will continue to honor render windows APIs, as before. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC 1 + +/* Structure for rendering decision parameter */ +struct asm_session_mtmx_strtr_param_render_mode_t { + /* Specifies the type of rendering decision the audio DSP should use. + * + * @values + * - #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT + * - #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC + */ + u32 flags; +} __packed; + +/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC which allows the + * audio client to specify the clock recovery mechanism that the audio DSP + * should use. + */ + +#define ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_CMD 0x00012F0E + +/* Indicates that default clock recovery will be used (no clock recovery). + * If the client wishes that no clock recovery be done, the client can + * choose this. This means that no attempt will made by the DSP to try and + * match the rates of the input and output audio. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_NONE 0 + +/* Indicates that independent clock recovery needs to be used. + * 1. In the DSP loopback/client loopback use cases (frame based inputs), + * the client should choose the independent clock recovery option. + * 2. This basically de-couples the audio and video from knowing each others + * clock sources and lets the audio DSP independently rate match the input + * and output rates. + * 3. After drift detection, the drift correction is achieved by either pulling + * the PLLs (if applicable) or by stream to device rate matching + * (for PCM use cases) by comparing drift with respect to STC. + * 4. For passthrough use cases, since the PLL pulling is the only option, + * a best effort will be made. + * If PLL pulling is not possible / available, the rendering will be + * done without rate matching. + */ +#define ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_AUTO 1 + +/* Payload of the #ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC parameter. + */ +struct asm_session_mtmx_strtr_param_clk_rec_t { + /* Specifies the type of clock recovery that the audio DSP should + * use for rate matching. + */ + + /* @values + * #ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_DEFAULT + * #ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_INDEPENDENT + */ + u32 flags; +} __packed; + + +/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC to + * realize smoother adjustment of audio session clock for a specified session. + * The desired audio session clock adjustment(in micro seconds) is specified + * using the command #ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2. + * Delaying/Advancing the session clock would be implemented by inserting + * interpolated/dropping audio samples in the playback path respectively. + * Also, this parameter has to be configured before the Audio Session is put + * to RUN state to avoid cold start latency/glitches in the playback. + */ + +#define ASM_SESSION_MTMX_PARAM_ADJUST_SESSION_TIME_CTL 0x00013217 + +struct asm_session_mtmx_param_adjust_session_time_ctl_t { + /* Specifies whether the module is enabled or not + * @values + * 0 -- disabled + * 1 -- enabled + */ + u32 enable; +}; + +union asm_session_mtmx_strtr_param_config { + struct asm_session_mtmx_strtr_param_window_v2_t window_param; + struct asm_session_mtmx_strtr_param_render_mode_t render_param; + struct asm_session_mtmx_strtr_param_clk_rec_t clk_rec_param; + struct asm_session_mtmx_param_adjust_session_time_ctl_t adj_time_param; +} __packed; + +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; + union asm_session_mtmx_strtr_param_config config; +} __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, + LISTEN, + COMPRESSED_PASSTHROUGH_GEN, + COMPRESSED_PASSTHROUGH_IEC61937 +}; + +#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 + +#define AUDPROC_MODULE_ID_VOICE_TX_SECNS 0x10027059 +#define AUDPROC_PARAM_IDX_SEC_PRIMARY_MIC_CH 0x10014444 + +struct admx_sec_primary_mic_ch { + uint16_t version; + uint16_t reserved; + uint16_t sec_primary_mic_ch; + uint16_t reserved1; +} __packed; + + +struct adm_set_sec_primary_ch_params { + struct adm_cmd_set_pp_params_v5 params; + struct adm_param_data_v5 data; + struct admx_sec_primary_mic_ch sec_primary_mic_ch_data; +} __packed; +#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/cpe_cmi.h b/include/sound/cpe_cmi.h new file mode 100644 index 000000000000..c145a8a64261 --- /dev/null +++ b/include/sound/cpe_cmi.h @@ -0,0 +1,492 @@ +/* + * Copyright (c) 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 __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..9f7c2f31ff0d --- /dev/null +++ b/include/sound/cpe_core.h @@ -0,0 +1,179 @@ +/* + * 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. + */ + +#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, uint32_t 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..b70dc490fb1a --- /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-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..e689e9357012 --- /dev/null +++ b/include/sound/q6adm-v2.h @@ -0,0 +1,187 @@ +/* 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_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 +#define ADM_PATH_COMPRESSED_TX 0x6 +#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_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[MAX_COPPS_PER_PORT]; + int acdb_dev_id[MAX_COPPS_PER_PORT]; + int sample_rate[MAX_COPPS_PER_PORT]; + unsigned short num_copps; + unsigned int session_id; +}; + +struct default_chmixer_param_id_coeff { + uint32_t index; + uint16_t num_output_channels; + uint16_t num_input_channels; +}; + +struct msm_pcm_channel_mixer { + int output_channel; + int input_channels[ADM_MAX_CHANNELS]; + bool enable; + int rule; + int channel_weight[ADM_MAX_CHANNELS][ADM_MAX_CHANNELS]; +}; + +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, uint32_t passthr_mode); + +int adm_connect_afe_port(int mode, int session_id, int port_id); + +void adm_ec_ref_rx_id(int port_id); + +void adm_num_ec_ref_rx_chans(int num_chans); + +void adm_ec_ref_rx_bit_width(int bit_width); + +void adm_ec_ref_rx_sampling_rate(int sampling_rate); + +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_set_mic_gain(int port_id, int copp_idx, int volume); + +int adm_send_set_multichannel_ec_primary_mic_ch(int port_id, int copp_idx, + int primary_mic_ch); + +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); +int adm_swap_speaker_channels(int port_id, int copp_idx, int sample_rate, + bool spk_swap); +int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id, + int session_type, + struct msm_pcm_channel_mixer *ch_mixer, + int channel_index); +#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..836117568d37 --- /dev/null +++ b/include/sound/q6afe-v2.h @@ -0,0 +1,371 @@ +/* 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 + +typedef int (*routing_cb)(int port); + +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_enable(u16 tx_port_id, u16 rx_port_id, bool enable); +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, u16 num_groups); +void afe_set_routing_callback(routing_cb cb); +int afe_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats, + u16 port); +#endif /* __Q6AFE_V2_H__ */ diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h new file mode 100644 index 000000000000..00b46a598280 --- /dev/null +++ b/include/sound/q6asm-v2.h @@ -0,0 +1,686 @@ +/* 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 FORMAT_APTX 0x001e +#define FORMAT_GEN_COMPR 0x001f +#define FORMAT_TRUEHD 0x0020 +#define FORMAT_IEC61937 0x0021 + +#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; + atomic_t cmd_state_pp; + /* 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, bool ts_mode); + +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_open_transcode_loopback(struct audio_client *ac, + uint16_t bits_per_sample, uint32_t source_format, + uint32_t sink_format); + +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_gen_compr( + 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_iec( + struct audio_client *ac, + uint32_t rate, uint32_t channels); + +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_stream_media_format_block_aptx_dec(struct audio_client *ac, + uint32_t sr, 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); + +/* Send aptx decoder BT address */ +int q6asm_set_aptx_dec_bt_addr(struct audio_client *ac, + struct aptx_dec_bt_addr_cfg *cfg); + +/* 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); + +int q6asm_send_stream_cmd(struct audio_client *ac, + struct msm_adsp_event_data *data); + +int q6asm_send_ion_fd(struct audio_client *ac, int fd); + +int q6asm_send_rtic_event_ack(struct audio_client *ac, + void *param, 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); + +/* Configure DSP render mode */ +int q6asm_send_mtmx_strtr_render_mode(struct audio_client *ac, + uint32_t render_mode); + +/* Configure DSP clock recovery mode */ +int q6asm_send_mtmx_strtr_clk_rec_mode(struct audio_client *ac, + uint32_t clk_rec_mode); + +/* Enable adjust session clock in DSP */ +int q6asm_send_mtmx_strtr_enable_adjust_session_clock(struct audio_client *ac, + bool enable); + +/* 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); + +/* Adjust session clock in DSP */ +int q6asm_adjust_session_clock(struct audio_client *ac, + uint32_t adjust_time_lsw, + uint32_t adjust_time_msw); +#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..26106a8db0ce --- /dev/null +++ b/include/sound/q6lsm.h @@ -0,0 +1,338 @@ +/* + * 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. + */ +#ifndef __Q6LSM_H__ +#define __Q6LSM_H__ + +#include +#include +#include +#include +#include + +#define MAX_NUM_CONFIDENCE 20 + +#define ADM_LSM_PORT_ID 0xADCB + +#define LSM_MAX_NUM_CHANNELS 8 + +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_hw_params { + u16 sample_rate; + u16 sample_size; + u32 buf_sz; + u32 period_count; + u16 num_chs; +}; + +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_hw_params hw_params; + bool use_topology; + int session_state; + bool poll_enable; + int perf_mode; + uint32_t event_mode; +}; + +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; + +struct lsm_param_poll_enable { + struct lsm_param_payload_common common; + uint32_t minor_version; + /* indicates to voice wakeup that HW MAD/SW polling is enabled or not */ + uint32_t polling_enable; +} __packed; + +struct lsm_param_fwk_mode_cfg { + struct lsm_param_payload_common common; + uint32_t minor_version; + uint32_t mode; +} __packed; + +struct lsm_param_media_fmt { + struct lsm_param_payload_common common; + uint32_t minor_version; + uint32_t sample_rate; + uint16_t num_channels; + uint16_t bit_width; + uint8_t channel_mapping[LSM_MAX_NUM_CHANNELS]; +} __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_params_opmode { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr params_hdr; + struct lsm_param_op_mode op_mode; +} __packed; + +struct lsm_cmd_set_connectport { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr params_hdr; + struct lsm_param_connect_to_port connect_to_port; +} __packed; + +struct lsm_cmd_poll_enable { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr params_hdr; + struct lsm_param_poll_enable poll_enable; +} __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_cmd_set_fwk_mode_cfg { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr params_hdr; + struct lsm_param_fwk_mode_cfg fwk_mode_cfg; +} __packed; + +struct lsm_cmd_set_media_fmt { + struct apr_hdr msg_hdr; + struct lsm_set_params_hdr params_hdr; + struct lsm_param_media_fmt media_fmt; +} __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, + uint32_t param_type); +void q6lsm_sm_set_param_data(struct lsm_client *client, + struct lsm_params_info *p_info, + size_t *offset); +int q6lsm_set_port_connected(struct lsm_client *client); +int q6lsm_set_fwk_mode_cfg(struct lsm_client *client, uint32_t event_mode); +int q6lsm_set_media_fmt_params(struct lsm_client *client); +#endif /* __Q6LSM_H__ */ diff --git a/include/uapi/Kbuild b/include/uapi/Kbuild new file mode 100644 index 000000000000..2eb179d4de95 --- /dev/null +++ b/include/uapi/Kbuild @@ -0,0 +1,6 @@ +# UAPI Header export list +# Top-level Makefile calls into asm-$(ARCH) +# List only non-arch directories below + +header-y += linux/ +header-y += sound/ diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild new file mode 100644 index 000000000000..7647f052b7f4 --- /dev/null +++ b/include/uapi/linux/Kbuild @@ -0,0 +1,19 @@ +# UAPI Header export list +header-y += avtimer.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 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..3213d00842c0 --- /dev/null +++ b/include/uapi/linux/msm_audio.h @@ -0,0 +1,475 @@ +/* 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; +}; + +#define ADSP_STREAM_PP_EVENT 0 +#define ADSP_STREAM_ENCDEC_EVENT 1 +#define ADSP_STREAM_IEC_61937_FMT_UPDATE_EVENT 2 +#define ADSP_STREAM_EVENT_MAX 3 + +struct msm_adsp_event_data { + __u32 event_type; + __u32 payload_len; + __u8 payload[0]; +}; + +#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..5a0b8603070d --- /dev/null +++ b/include/uapi/linux/msm_audio_calibration.h @@ -0,0 +1,719 @@ +#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, + AFE_SIDETONE_IIR_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 + +#define AFE_SIDETONE_IIR_CAL_TYPE AFE_SIDETONE_IIR_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; +}; + +#define MAX_SIDETONE_IIR_DATA_SIZE 224 +#define MAX_NO_IIR_FILTER_STAGE 10 + +struct audio_cal_info_sidetone_iir { + uint16_t iir_enable; + uint16_t num_biquad_stages; + uint16_t pregain; + int32_t tx_acdb_id; + int32_t rx_acdb_id; + int32_t mid; + int32_t pid; + uint8_t iir_config[MAX_SIDETONE_IIR_DATA_SIZE]; +}; +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_sidetone_iir { + struct audio_cal_type_header cal_hdr; + struct audio_cal_data cal_data; + struct audio_cal_info_sidetone_iir cal_info; +}; + +struct audio_cal_sidetone_iir { + struct audio_cal_header hdr; + struct audio_cal_type_sidetone_iir 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 new file mode 100644 index 000000000000..175fb6f63359 --- /dev/null +++ b/include/uapi/sound/Kbuild @@ -0,0 +1,8 @@ +# UAPI Header export list +header-y += lsm_params.h +header-y += audio_slimslave.h +header-y += audio_effects.h +header-y += devdep_params.h +header-y += msmcal-hwdep.h +header-y += wcd-dsp-glink.h +header-y += voice_params.h diff --git a/include/uapi/sound/audio_effects.h b/include/uapi/sound/audio_effects.h new file mode 100644 index 000000000000..7964c34f890d --- /dev/null +++ b/include/uapi/sound/audio_effects.h @@ -0,0 +1,361 @@ +#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/audio_slimslave.h b/include/uapi/sound/audio_slimslave.h new file mode 100644 index 000000000000..316a5573f5b4 --- /dev/null +++ b/include/uapi/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/uapi/sound/devdep_params.h b/include/uapi/sound/devdep_params.h new file mode 100644 index 000000000000..6697eca288fe --- /dev/null +++ b/include/uapi/sound/devdep_params.h @@ -0,0 +1,56 @@ +#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..9ca5930475ba --- /dev/null +++ b/include/uapi/sound/lsm_params.h @@ -0,0 +1,200 @@ +#ifndef _UAPI_LSM_PARAMS_H__ +#define _UAPI_LSM_PARAMS_H__ + +#define LSM_POLLING_ENABLE_SUPPORT +#define LSM_EVENT_TIMESTAMP_MODE_SUPPORT + +#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) + +#define LSM_ENDPOINT_DETECT_THRESHOLD (0) +#define LSM_OPERATION_MODE (1) +#define LSM_GAIN (2) +#define LSM_MIN_CONFIDENCE_LEVELS (3) +#define LSM_REG_SND_MODEL (4) +#define LSM_DEREG_SND_MODEL (5) +#define LSM_CUSTOM_PARAMS (6) +#define LSM_POLLING_ENABLE (7) +#define LSM_PARAMS_MAX (LSM_POLLING_ENABLE + 1) + +#define LSM_EVENT_NON_TIME_STAMP_MODE (0) +#define LSM_EVENT_TIME_STAMP_MODE (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 +}; + +/* + * 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; +}; + +/* + * Data for LSM_POLLING_ENABLE param_type + * @poll_en: Polling enable or disable + */ +struct snd_lsm_poll_enable { + bool poll_en; +}; + + +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_event_status_v3 { + __u32 timestamp_lsw; + __u32 timestamp_msw; + __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; + bool poll_enable; +}; + +/* + * 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; + uint32_t 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) +#define SNDRV_LSM_SET_PORT _IO('U', 0x0D) +#define SNDRV_LSM_SET_FWK_MODE_CONFIG _IOW('U', 0x0E, uint32_t) +#define SNDRV_LSM_EVENT_STATUS_V3 _IOW('U', 0x0F, \ + struct snd_lsm_event_status_v3) + +#endif diff --git a/include/uapi/sound/msmcal-hwdep.h b/include/uapi/sound/msmcal-hwdep.h new file mode 100644 index 000000000000..49f15de682fc --- /dev/null +++ b/include/uapi/sound/msmcal-hwdep.h @@ -0,0 +1,23 @@ +#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/include/uapi/sound/voice_params.h b/include/uapi/sound/voice_params.h new file mode 100644 index 000000000000..43e3b9d0aa49 --- /dev/null +++ b/include/uapi/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/uapi/sound/wcd-dsp-glink.h b/include/uapi/sound/wcd-dsp-glink.h new file mode 100644 index 000000000000..39d128d370a0 --- /dev/null +++ b/include/uapi/sound/wcd-dsp-glink.h @@ -0,0 +1,60 @@ +#ifndef _WCD_DSP_GLINK_H +#define _WCD_DSP_GLINK_H + +#include + +#define WDSP_CH_NAME_MAX_LEN 50 + +enum { + WDSP_REG_PKT = 1, + WDSP_CMD_PKT, + WDSP_READY_PKT, +}; +#define WDSP_READY_PKT WDSP_READY_PKT + +/* + * struct wdsp_reg_pkt - Glink channel information structure format + * @no_of_channels: Number of glink channels to open + * @payload[0]: Dynamic array contains all the glink channels information + */ +struct wdsp_reg_pkt { + __u8 no_of_channels; + __u8 payload[0]; +}; + +/* + * struct wdsp_cmd_pkt - WDSP command packet format + * @ch_name: Name of the glink channel + * @payload_size: Size of the payload + * @payload[0]: Actual data payload + */ +struct wdsp_cmd_pkt { + char ch_name[WDSP_CH_NAME_MAX_LEN]; + __u32 payload_size; + __u8 payload[0]; +}; + +/* + * struct wdsp_write_pkt - Format that userspace send the data to driver. + * @pkt_type: Type of the packet(REG or CMD PKT) + * @payload[0]: Payload is either cmd or reg pkt structure based on pkt type + */ +struct wdsp_write_pkt { + __u8 pkt_type; + __u8 payload[0]; +}; + +/* + * struct wdsp_glink_ch_cfg - Defines the glink channel configuration. + * @ch_name: Name of the glink channel + * @latency_in_us: Latency specified in micro seconds for QOS + * @no_of_intents: Number of intents prequeued + * @intents_size[0]: Dynamic array to specify size of each intent + */ +struct wdsp_glink_ch_cfg { + char name[WDSP_CH_NAME_MAX_LEN]; + __u32 latency_in_us; + __u32 no_of_intents; + __u32 intents_size[0]; +}; +#endif /* _WCD_DSP_GLINK_H */ diff --git a/sound/Makefile b/sound/Makefile new file mode 100644 index 000000000000..71c713a41d38 --- /dev/null +++ b/sound/Makefile @@ -0,0 +1,4 @@ +# MSM Machine Support + +obj-$(CONFIG_SND_SOC) += soc/msm/ +obj-$(CONFIG_SND_SOC) += soc/codecs/ diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile new file mode 100644 index 000000000000..53256649cd4e --- /dev/null +++ b/sound/soc/codecs/Makefile @@ -0,0 +1,26 @@ +snd-soc-wcd934x-objs := wcd934x.o +snd-soc-wcd9xxx-v2-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o wcdcal-hwdep.o wcd9xxx-soc-init.o +snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o +snd-soc-wsa881x-objs := wsa881x.o wsa881x-tables.o wsa881x-regmap.o wsa881x-temp-sensor.o +snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o wcd-mbhc-adc.o +snd-soc-wcd-dsp-utils-objs := wcd-dsp-utils.o +snd-soc-wcd-dsp-mgr-objs := wcd-dsp-mgr.o +snd-soc-wcd-spi-objs := wcd-spi.o + +obj-$(CONFIG_SND_SOC_WCD934X) += wcd934x/ +ifeq ($(CONFIG_COMMON_CLK_MSM), y) + obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clk.o +endif +ifeq ($(CONFIG_COMMON_CLK_QCOM), y) + obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clk-up.o +endif + +obj-$(CONFIG_SND_SOC_WCD9XXX_V2) += snd-soc-wcd9xxx-v2.o +obj-$(CONFIG_SND_SOC_WCD_CPE) += snd-soc-wcd-cpe.o +obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o +obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o +obj-$(CONFIG_SND_SOC_WCD_DSP_MGR) += snd-soc-wcd-dsp-mgr.o snd-soc-wcd-dsp-utils.o +obj-$(CONFIG_SND_SOC_WCD_SPI) += snd-soc-wcd-spi.o + +snd-soc-msm-stub-objs := msm_stub.o +obj-$(CONFIG_SND_SOC_MSM_STUB) += snd-soc-msm-stub.o diff --git a/sound/soc/codecs/audio-ext-clk-up.c b/sound/soc/codecs/audio-ext-clk-up.c new file mode 100644 index 000000000000..31c063d4b93e --- /dev/null +++ b/sound/soc/codecs/audio-ext-clk-up.c @@ -0,0 +1,626 @@ +/* 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 "../../../drivers/clk/qcom/common.h" +#include +#include +#include +#include +#include +#include "audio-ext-clk-up.h" + +enum audio_clk_mux { + AP_CLK2, + LPASS_MCLK, + LPASS_MCLK2, +}; + +struct pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *sleep; + struct pinctrl_state *active; + char __iomem *base; +}; + +struct audio_ext_ap_clk { + bool enabled; + int gpio; + struct clk_fixed_factor fact; +}; + +struct audio_ext_pmi_clk { + int gpio; + struct clk_fixed_factor fact; +}; + +struct audio_ext_ap_clk2 { + bool enabled; + struct pinctrl_info pnctrl_info; + struct clk_fixed_factor fact; +}; + +struct audio_ext_lpass_mclk { + struct pinctrl_info pnctrl_info; + struct clk_fixed_factor fact; +}; + +static struct afe_clk_set clk2_config = { + Q6AFE_LPASS_CLK_CONFIG_API_VERSION, + Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR, + Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static struct afe_clk_set lpass_default = { + Q6AFE_LPASS_CLK_CONFIG_API_VERSION, + Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR, + Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static struct afe_clk_set lpass_mclk = { + Q6AFE_LPASS_CLK_CONFIG_API_VERSION, + Q6AFE_LPASS_CLK_ID_MCLK_1, + Q6AFE_LPASS_OSR_CLK_11_P2896_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk_hw *hw) +{ + return container_of(hw, struct audio_ext_ap_clk, fact.hw); +} + +static int audio_ext_clk_prepare(struct clk_hw *hw) +{ + struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw); + + pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio); + if (gpio_is_valid(audio_clk->gpio)) + return gpio_direction_output(audio_clk->gpio, 1); + return 0; +} + +static void audio_ext_clk_unprepare(struct clk_hw *hw) +{ + struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw); + + pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio); + if (gpio_is_valid(audio_clk->gpio)) + gpio_direction_output(audio_clk->gpio, 0); +} + +static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk_hw *hw) +{ + return container_of(hw, struct audio_ext_ap_clk2, fact.hw); +} + +static int audio_ext_clk2_prepare(struct clk_hw *hw) +{ + struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw); + struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; + int ret; + + + if (!pnctrl_info->pinctrl || !pnctrl_info->active) + return 0; + + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->active); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return -EIO; + } + + clk2_config.enable = 1; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config); + if (ret < 0) { + pr_err("%s: failed to set clock, ret = %d\n", __func__, ret); + return -EINVAL; + } + + return 0; +} + +static void audio_ext_clk2_unprepare(struct clk_hw *hw) +{ + struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw); + struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; + int ret; + + if (!pnctrl_info->pinctrl || !pnctrl_info->sleep) + return; + + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) + pr_err("%s: sleep state select failed with %d\n", + __func__, ret); + + clk2_config.enable = 0; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config); + if (ret < 0) + pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret); +} + +static inline struct audio_ext_lpass_mclk *to_audio_lpass_mclk( + struct clk_hw *hw) +{ + return container_of(hw, struct audio_ext_lpass_mclk, fact.hw); +} + +static int audio_ext_lpass_mclk_prepare(struct clk_hw *hw) +{ + struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw); + struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info; + int ret; + + lpass_mclk.enable = 1; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX, + &lpass_mclk); + if (ret < 0) { + pr_err("%s afe_set_digital_codec_core_clock failed\n", + __func__); + return ret; + } + + if (pnctrl_info->pinctrl) { + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->active); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return -EIO; + } + } + + if (pnctrl_info->base) + iowrite32(1, pnctrl_info->base); + return 0; +} + +static void audio_ext_lpass_mclk_unprepare(struct clk_hw *hw) +{ + struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw); + struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info; + int ret; + + if (pnctrl_info->pinctrl) { + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return; + } + } + + lpass_mclk.enable = 0; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX, + &lpass_mclk); + if (ret < 0) + pr_err("%s: afe_set_digital_codec_core_clock failed, ret = %d\n", + __func__, ret); + if (pnctrl_info->base) + iowrite32(0, pnctrl_info->base); +} + +static int audio_ext_lpass_mclk2_prepare(struct clk_hw *hw) +{ + struct audio_ext_lpass_mclk *audio_lpass_mclk2 = + to_audio_lpass_mclk(hw); + struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info; + int ret; + + if (pnctrl_info->pinctrl) { + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->active); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return -EIO; + } + } + + lpass_default.enable = 1; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default); + if (ret < 0) { + pr_err("%s: failed to set clock, ret = %d\n", __func__, ret); + return -EINVAL; + } + + return 0; +} + +static void audio_ext_lpass_mclk2_unprepare(struct clk_hw *hw) +{ + struct audio_ext_lpass_mclk *audio_lpass_mclk2 = + to_audio_lpass_mclk(hw); + struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info; + int ret; + + if (pnctrl_info->pinctrl) { + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) + pr_err("%s: sleep state select failed with %d\n", + __func__, ret); + } + + lpass_default.enable = 0; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default); + if (ret < 0) + pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret); +} + +static const struct clk_ops audio_ext_ap_clk_ops = { + .prepare = audio_ext_clk_prepare, + .unprepare = audio_ext_clk_unprepare, +}; + +static const struct clk_ops audio_ext_ap_clk2_ops = { + .prepare = audio_ext_clk2_prepare, + .unprepare = audio_ext_clk2_unprepare, +}; + +static const struct clk_ops audio_ext_lpass_mclk_ops = { + .prepare = audio_ext_lpass_mclk_prepare, + .unprepare = audio_ext_lpass_mclk_unprepare, +}; + +static const struct clk_ops audio_ext_lpass_mclk2_ops = { + .prepare = audio_ext_lpass_mclk2_prepare, + .unprepare = audio_ext_lpass_mclk2_unprepare, +}; + +static struct audio_ext_pmi_clk audio_pmi_clk = { + .gpio = -EINVAL, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_ext_pmi_clk", + .parent_names = (const char *[]){ "div_clk1" }, + .num_parents = 1, + .ops = &clk_dummy_ops, + }, + }, +}; + +static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = { + .gpio = -EINVAL, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_ext_pmi_lnbb_clk", + .parent_names = (const char *[]){ "ln_bb_clk2" }, + .num_parents = 1, + .ops = &clk_dummy_ops, + }, + }, +}; + +static struct audio_ext_ap_clk audio_ap_clk = { + .gpio = -EINVAL, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_ap_clk", + .ops = &audio_ext_ap_clk_ops, + }, + }, +}; + +static struct audio_ext_ap_clk2 audio_ap_clk2 = { + .enabled = false, + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_ap_clk2", + .ops = &audio_ext_ap_clk2_ops, + }, + }, +}; + +static struct audio_ext_lpass_mclk audio_lpass_mclk = { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_lpass_mclk", + .ops = &audio_ext_lpass_mclk_ops, + }, + }, +}; + +static struct audio_ext_lpass_mclk audio_lpass_mclk2 = { + .pnctrl_info = {NULL}, + .fact = { + .mult = 1, + .div = 1, + .hw.init = &(struct clk_init_data){ + .name = "audio_lpass_mclk2", + .ops = &audio_ext_lpass_mclk2_ops, + }, + }, +}; + +static struct clk_hw *audio_msm_hws[] = { + &audio_pmi_clk.fact.hw, + &audio_ap_clk.fact.hw, + &audio_ap_clk2.fact.hw, + &audio_lpass_mclk.fact.hw, + &audio_lpass_mclk2.fact.hw, +}; + +static struct clk_hw *audio_msm_hws1[] = { + &audio_pmi_lnbb_clk.fact.hw, +}; + +static int audio_get_pinctrl(struct platform_device *pdev, + enum audio_clk_mux mux) +{ + struct device *dev = &pdev->dev; + struct pinctrl_info *pnctrl_info; + struct pinctrl *pinctrl; + int ret; + u32 reg; + + switch (mux) { + case AP_CLK2: + pnctrl_info = &audio_ap_clk2.pnctrl_info; + break; + case LPASS_MCLK: + pnctrl_info = &audio_lpass_mclk.pnctrl_info; + break; + case LPASS_MCLK2: + pnctrl_info = &audio_lpass_mclk2.pnctrl_info; + break; + default: + dev_err(dev, "%s Not a valid MUX ID: %d\n", + __func__, mux); + return -EINVAL; + } + + if (pnctrl_info->pinctrl) { + dev_dbg(dev, "%s: already requested before\n", + __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(pinctrl)) { + dev_dbg(dev, "%s: Unable to get pinctrl handle\n", + __func__); + return -EINVAL; + } + pnctrl_info->pinctrl = pinctrl; + /* get all state handles from Device Tree */ + pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep"); + if (IS_ERR(pnctrl_info->sleep)) { + dev_err(dev, "%s: could not get sleep pinstate\n", + __func__); + goto err; + } + pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active"); + if (IS_ERR(pnctrl_info->active)) { + dev_err(dev, "%s: could not get active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) { + dev_err(dev, "%s: Disable TLMM pins failed with %d\n", + __func__, ret); + goto err; + } + + ret = of_property_read_u32(dev->of_node, "qcom,mclk-clk-reg", ®); + if (ret < 0) { + dev_dbg(dev, "%s: miss mclk reg\n", __func__); + } else { + pnctrl_info->base = ioremap(reg, sizeof(u32)); + if (pnctrl_info->base == NULL) { + dev_err(dev, "%s ioremap failed\n", __func__); + goto err; + } + } + + return 0; + +err: + devm_pinctrl_put(pnctrl_info->pinctrl); + return -EINVAL; +} + +static int audio_ref_clk_probe(struct platform_device *pdev) +{ + int clk_gpio; + int ret; + u32 mclk_freq; + struct clk *audio_clk; + struct device *dev = &pdev->dev; + int i; + struct clk_onecell_data *clk_data; + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,codec-mclk-clk-freq", + &mclk_freq); + if (!ret) { + lpass_mclk.clk_freq_in_hz = mclk_freq; + + ret = audio_get_pinctrl(pdev, LPASS_MCLK); + if (ret) + dev_err(&pdev->dev, "%s: Parsing pinctrl %s failed\n", + __func__, "LPASS_MCLK"); + ret = audio_get_pinctrl(pdev, LPASS_MCLK2); + if (ret) + dev_dbg(&pdev->dev, "%s: Parsing pinctrl %s failed\n", + __func__, "LPASS_MCLK2"); + } + + clk_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,audio-ref-clk-gpio", 0); + if (clk_gpio > 0) { + ret = gpio_request(clk_gpio, "EXT_CLK"); + if (ret) { + dev_err(&pdev->dev, + "Request ext clk gpio failed %d, err:%d\n", + clk_gpio, ret); + goto err; + } + if (of_property_read_bool(pdev->dev.of_node, + "qcom,node_has_rpm_clock")) { + audio_pmi_clk.gpio = clk_gpio; + } else + audio_ap_clk.gpio = clk_gpio; + + } + + ret = audio_get_pinctrl(pdev, AP_CLK2); + if (ret) + dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n", + __func__); + + clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL); + if (!clk_data) + goto err_gpio; + + + clk_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,audio-ref-clk-gpio", 0); + if (clk_gpio > 0) { + clk_data->clk_num = ARRAY_SIZE(audio_msm_hws); + clk_data->clks = devm_kzalloc(&pdev->dev, + clk_data->clk_num * + sizeof(struct clk *), + GFP_KERNEL); + if (!clk_data->clks) + goto err_clk; + + for (i = 0; i < ARRAY_SIZE(audio_msm_hws); i++) { + audio_clk = devm_clk_register(dev, audio_msm_hws[i]); + if (IS_ERR(audio_clk)) { + dev_err(&pdev->dev, + "%s: ref clock: %d register failed\n", + __func__, i); + return PTR_ERR(audio_clk); + } + clk_data->clks[i] = audio_clk; + } + } else { + clk_data->clk_num = ARRAY_SIZE(audio_msm_hws1); + clk_data->clks = devm_kzalloc(&pdev->dev, + clk_data->clk_num * + sizeof(struct clk *), + GFP_KERNEL); + if (!clk_data->clks) + goto err_clk; + + for (i = 0; i < ARRAY_SIZE(audio_msm_hws1); i++) { + audio_clk = devm_clk_register(dev, audio_msm_hws1[i]); + if (IS_ERR(audio_clk)) { + dev_err(&pdev->dev, + "%s: ref clock: %d register failed\n", + __func__, i); + return PTR_ERR(audio_clk); + } + clk_data->clks[i] = audio_clk; + } + } + + ret = of_clk_add_provider(pdev->dev.of_node, + of_clk_src_onecell_get, clk_data); + if (ret) { + dev_err(&pdev->dev, "%s: audio ref clock register failed\n", + __func__); + goto err_gpio; + } + + return 0; + +err_clk: + if (clk_data) + devm_kfree(&pdev->dev, clk_data->clks); + devm_kfree(&pdev->dev, clk_data); +err_gpio: + gpio_free(clk_gpio); + +err: + return ret; +} + +static int audio_ref_clk_remove(struct platform_device *pdev) +{ + struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info; + + if (audio_pmi_clk.gpio > 0) + gpio_free(audio_pmi_clk.gpio); + else if (audio_ap_clk.gpio > 0) + gpio_free(audio_ap_clk.gpio); + + if (pnctrl_info->pinctrl) { + devm_pinctrl_put(pnctrl_info->pinctrl); + pnctrl_info->pinctrl = NULL; + } + + return 0; +} + +static const struct of_device_id audio_ref_clk_match[] = { + {.compatible = "qcom,audio-ref-clk"}, + {} +}; +MODULE_DEVICE_TABLE(of, audio_ref_clk_match); + +static struct platform_driver audio_ref_clk_driver = { + .driver = { + .name = "audio-ref-clk", + .owner = THIS_MODULE, + .of_match_table = audio_ref_clk_match, + }, + .probe = audio_ref_clk_probe, + .remove = audio_ref_clk_remove, +}; + +int audio_ref_clk_platform_init(void) +{ + return platform_driver_register(&audio_ref_clk_driver); +} + +void audio_ref_clk_platform_exit(void) +{ + platform_driver_unregister(&audio_ref_clk_driver); +} + +MODULE_DESCRIPTION("Audio Ref Up Clock module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/audio-ext-clk-up.h b/sound/soc/codecs/audio-ext-clk-up.h new file mode 100644 index 000000000000..8a0232e11d0f --- /dev/null +++ b/sound/soc/codecs/audio-ext-clk-up.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 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_EXT_CLK_UP_H_ +#define __AUDIO_EXT_CLK_UP_H_ + +int audio_ref_clk_platform_init(void); +void audio_ref_clk_platform_exit(void); + +#endif diff --git a/sound/soc/codecs/audio-ext-clk.c b/sound/soc/codecs/audio-ext-clk.c new file mode 100644 index 000000000000..72f16f5848dd --- /dev/null +++ b/sound/soc/codecs/audio-ext-clk.c @@ -0,0 +1,348 @@ +/* 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 +#include +#include +#include +#include "audio-ext-clk-up.h" + +struct pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *sleep; + struct pinctrl_state *active; +}; + +struct audio_ext_ap_clk { + bool enabled; + int gpio; + struct clk c; +}; + +struct audio_ext_pmi_clk { + int gpio; + struct clk c; +}; + +struct audio_ext_ap_clk2 { + bool enabled; + struct pinctrl_info pnctrl_info; + struct clk c; +}; + +static struct afe_clk_set clk2_config = { + Q6AFE_LPASS_CLK_CONFIG_API_VERSION, + Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR, + Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, +}; + +static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk *clk) +{ + return container_of(clk, struct audio_ext_ap_clk, c); +} + +static int audio_ext_clk_prepare(struct clk *clk) +{ + struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk); + + pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio); + if (gpio_is_valid(audio_clk->gpio)) + return gpio_direction_output(audio_clk->gpio, 1); + return 0; +} + +static void audio_ext_clk_unprepare(struct clk *clk) +{ + struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk); + + pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio); + if (gpio_is_valid(audio_clk->gpio)) + gpio_direction_output(audio_clk->gpio, 0); +} + +static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk *clk) +{ + return container_of(clk, struct audio_ext_ap_clk2, c); +} + +static int audio_ext_clk2_prepare(struct clk *clk) +{ + struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk); + struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; + int ret; + + + if (!pnctrl_info->pinctrl || !pnctrl_info->active) + return 0; + + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->active); + if (ret) { + pr_err("%s: active state select failed with %d\n", + __func__, ret); + return -EIO; + } + + clk2_config.enable = 1; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config); + if (ret < 0) { + pr_err("%s: failed to set clock, ret = %d\n", __func__, ret); + return -EINVAL; + } + + return 0; +} + +static void audio_ext_clk2_unprepare(struct clk *clk) +{ + struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk); + struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info; + int ret; + + if (!pnctrl_info->pinctrl || !pnctrl_info->sleep) + return; + + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) + pr_err("%s: sleep state select failed with %d\n", + __func__, ret); + + clk2_config.enable = 0; + ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config); + if (ret < 0) + pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret); +} + +static const struct clk_ops audio_ext_ap_clk_ops = { + .prepare = audio_ext_clk_prepare, + .unprepare = audio_ext_clk_unprepare, +}; + +static const struct clk_ops audio_ext_ap_clk2_ops = { + .prepare = audio_ext_clk2_prepare, + .unprepare = audio_ext_clk2_unprepare, +}; + +static struct audio_ext_pmi_clk audio_pmi_clk = { + .gpio = -EINVAL, + .c = { + .dbg_name = "audio_ext_pmi_clk", + .ops = &clk_ops_dummy, + CLK_INIT(audio_pmi_clk.c), + }, +}; + +static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = { + .gpio = -EINVAL, + .c = { + .dbg_name = "audio_ext_pmi_lnbb_clk", + .ops = &clk_ops_dummy, + CLK_INIT(audio_pmi_lnbb_clk.c), + }, +}; + +static struct audio_ext_ap_clk audio_ap_clk = { + .gpio = -EINVAL, + .c = { + .dbg_name = "audio_ext_ap_clk", + .ops = &audio_ext_ap_clk_ops, + CLK_INIT(audio_ap_clk.c), + }, +}; + +static struct audio_ext_ap_clk2 audio_ap_clk2 = { + .c = { + .dbg_name = "audio_ext_ap_clk2", + .ops = &audio_ext_ap_clk2_ops, + CLK_INIT(audio_ap_clk2.c), + }, +}; + +static struct clk_lookup audio_ref_clock[] = { + CLK_LIST(audio_ap_clk), + CLK_LIST(audio_pmi_clk), + CLK_LIST(audio_pmi_lnbb_clk), + CLK_LIST(audio_ap_clk2), +}; + +static int audio_get_pinctrl(struct platform_device *pdev) +{ + struct pinctrl_info *pnctrl_info; + struct pinctrl *pinctrl; + int ret; + + pnctrl_info = &audio_ap_clk2.pnctrl_info; + + if (pnctrl_info->pinctrl) { + dev_dbg(&pdev->dev, "%s: already requested before\n", + __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n", + __func__); + return -EINVAL; + } + pnctrl_info->pinctrl = pinctrl; + /* get all state handles from Device Tree */ + pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep"); + if (IS_ERR(pnctrl_info->sleep)) { + dev_err(&pdev->dev, "%s: could not get sleep pinstate\n", + __func__); + goto err; + } + pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active"); + if (IS_ERR(pnctrl_info->active)) { + dev_err(&pdev->dev, "%s: could not get active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pnctrl_info->pinctrl, + pnctrl_info->sleep); + if (ret) { + dev_err(&pdev->dev, "%s: Disable TLMM pins failed with %d\n", + __func__, ret); + goto err; + } + return 0; + +err: + devm_pinctrl_put(pnctrl_info->pinctrl); + return -EINVAL; +} + +static int audio_ref_clk_probe(struct platform_device *pdev) +{ + int clk_gpio; + int ret; + struct clk *audio_clk; + + clk_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,audio-ref-clk-gpio", 0); + if (clk_gpio > 0) { + ret = gpio_request(clk_gpio, "EXT_CLK"); + if (ret) { + dev_err(&pdev->dev, + "Request ext clk gpio failed %d, err:%d\n", + clk_gpio, ret); + goto err; + } + if (of_property_read_bool(pdev->dev.of_node, + "qcom,node_has_rpm_clock")) { + audio_clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(audio_clk)) { + dev_err(&pdev->dev, "Failed to get RPM div clk\n"); + ret = PTR_ERR(audio_clk); + goto err_gpio; + } + audio_pmi_clk.c.parent = audio_clk; + audio_pmi_clk.gpio = clk_gpio; + } else + audio_ap_clk.gpio = clk_gpio; + + } else { + if (of_property_read_bool(pdev->dev.of_node, + "qcom,node_has_rpm_clock")) { + audio_clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(audio_clk)) { + dev_err(&pdev->dev, "Failed to get lnbbclk2\n"); + ret = PTR_ERR(audio_clk); + goto err; + } + audio_pmi_lnbb_clk.c.parent = audio_clk; + audio_pmi_lnbb_clk.gpio = -EINVAL; + } + } + + ret = audio_get_pinctrl(pdev); + if (ret) + dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n", + __func__); + + ret = of_msm_clock_register(pdev->dev.of_node, audio_ref_clock, + ARRAY_SIZE(audio_ref_clock)); + if (ret) { + dev_err(&pdev->dev, "%s: audio ref clock register failed\n", + __func__); + goto err_gpio; + } + + return 0; + +err_gpio: + gpio_free(clk_gpio); + +err: + return ret; +} + +static int audio_ref_clk_remove(struct platform_device *pdev) +{ + struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info; + + if (audio_pmi_clk.gpio > 0) + gpio_free(audio_pmi_clk.gpio); + else if (audio_ap_clk.gpio > 0) + gpio_free(audio_ap_clk.gpio); + + if (pnctrl_info->pinctrl) { + devm_pinctrl_put(pnctrl_info->pinctrl); + pnctrl_info->pinctrl = NULL; + } + + return 0; +} + +static const struct of_device_id audio_ref_clk_match[] = { + {.compatible = "qcom,audio-ref-clk"}, + {} +}; +MODULE_DEVICE_TABLE(of, audio_ref_clk_match); + +static struct platform_driver audio_ref_clk_driver = { + .driver = { + .name = "audio-ref-clk", + .owner = THIS_MODULE, + .of_match_table = audio_ref_clk_match, + }, + .probe = audio_ref_clk_probe, + .remove = audio_ref_clk_remove, +}; + +int audio_ref_clk_platform_init(void) +{ + return platform_driver_register(&audio_ref_clk_driver); +} + +void audio_ref_clk_platform_exit(void) +{ + platform_driver_unregister(&audio_ref_clk_driver); +} + +MODULE_DESCRIPTION("Audio Ref Clock module platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm_stub.c b/sound/soc/codecs/msm_stub.c new file mode 100644 index 000000000000..68e55ae1da16 --- /dev/null +++ b/sound/soc/codecs/msm_stub.c @@ -0,0 +1,88 @@ +/* Copyright (c) 2011-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 + +/* A dummy driver useful only to advertise hardware parameters */ +static struct snd_soc_dai_driver msm_stub_dais[] = { + { + .name = "msm-stub-rx", + .playback = { /* Support maximum range */ + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + }, + { + .name = "msm-stub-tx", + .capture = { /* Support maximum range */ + .stream_name = "Record", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + }, + }, +}; + +static struct snd_soc_codec_driver soc_msm_stub = {}; + +static int msm_stub_dev_probe(struct platform_device *pdev) +{ + dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev)); + + return snd_soc_register_codec(&pdev->dev, + &soc_msm_stub, msm_stub_dais, ARRAY_SIZE(msm_stub_dais)); +} + +static int msm_stub_dev_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} +static const struct of_device_id msm_stub_codec_dt_match[] = { + { .compatible = "qcom,msm-stub-codec", }, + {} +}; + +static struct platform_driver msm_stub_driver = { + .driver = { + .name = "msm-stub-codec", + .owner = THIS_MODULE, + .of_match_table = msm_stub_codec_dt_match, + }, + .probe = msm_stub_dev_probe, + .remove = msm_stub_dev_remove, +}; + +static int __init msm_stub_init(void) +{ + return platform_driver_register(&msm_stub_driver); +} +module_init(msm_stub_init); + +static void __exit msm_stub_exit(void) +{ + platform_driver_unregister(&msm_stub_driver); +} +module_exit(msm_stub_exit); + +MODULE_DESCRIPTION("Generic MSM CODEC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/sdm660_cdc/Kconfig b/sound/soc/codecs/sdm660_cdc/Kconfig new file mode 100644 index 000000000000..2f36c393a65b --- /dev/null +++ b/sound/soc/codecs/sdm660_cdc/Kconfig @@ -0,0 +1,5 @@ + +config SND_SOC_SDM660_CDC + tristate "MSM Internal PMIC based codec" + select SND_SOC_WCD_MBHC + select SND_SOC_WCD_MBHC_LEGACY diff --git a/sound/soc/codecs/sdm660_cdc/Makefile b/sound/soc/codecs/sdm660_cdc/Makefile new file mode 100644 index 000000000000..d846fae26054 --- /dev/null +++ b/sound/soc/codecs/sdm660_cdc/Makefile @@ -0,0 +1,2 @@ +snd-soc-sdm660-cdc-objs := msm-analog-cdc.o msm-digital-cdc.o sdm660-regmap.o +obj-$(CONFIG_SND_SOC_SDM660_CDC) += snd-soc-sdm660-cdc.o sdm660-cdc-irq.o diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c new file mode 100644 index 000000000000..a8fcd347b38b --- /dev/null +++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -0,0 +1,4683 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "msm-analog-cdc.h" +#include "sdm660-cdc-irq.h" +#include "sdm660-cdc-registers.h" +#include "msm-cdc-common.h" +#include "../../msm/sdm660-common.h" +#include "../wcd-mbhc-v2-api.h" + +#define DRV_NAME "pmic_analog_codec" +#define SDM660_CDC_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_192000) +#define SDM660_CDC_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE) +#define MSM_DIG_CDC_STRING_LEN 80 +#define MSM_ANLG_CDC_VERSION_ENTRY_SIZE 32 + +#define CODEC_DT_MAX_PROP_SIZE 40 +#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 +#define BUS_DOWN 1 + +/* + * 50 Milliseconds sufficient for DSP bring up in the lpass + * after Sub System Restart + */ +#define ADSP_STATE_READY_TIMEOUT_MS 50 + +#define EAR_PMD 0 +#define EAR_PMU 1 +#define SPK_PMD 2 +#define SPK_PMU 3 + +#define MICBIAS_DEFAULT_VAL 1800000 +#define MICBIAS_MIN_VAL 1600000 +#define MICBIAS_STEP_SIZE 50000 + +#define DEFAULT_BOOST_VOLTAGE 5000 +#define MIN_BOOST_VOLTAGE 4000 +#define MAX_BOOST_VOLTAGE 5550 +#define BOOST_VOLTAGE_STEP 50 + +#define SDM660_CDC_MBHC_BTN_COARSE_ADJ 100 /* in mV */ +#define SDM660_CDC_MBHC_BTN_FINE_ADJ 12 /* in mV */ + +#define VOLTAGE_CONVERTER(value, min_value, step_size)\ + ((value - min_value)/step_size) + +enum { + BOOST_SWITCH = 0, + BOOST_ALWAYS, + BYPASS_ALWAYS, + BOOST_ON_FOREVER, +}; + +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); +static struct snd_soc_dai_driver msm_anlg_cdc_i2s_dai[]; +/* By default enable the internal speaker boost */ +static bool spkr_boost_en = true; + +static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = { + "cdc-vdd-mic-bias", +}; + +static struct wcd_mbhc_register + wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x18, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0xF0, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x0C, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x01, + 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x02, + 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x08, + 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x04, + 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0xFF, + 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFC, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, + 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", 0, 0, 0, 0), +}; + +/* Multiply gain_adj and offset by 1000 and 100 to avoid float arithmetic */ +static const struct wcd_imped_i_ref imped_i_ref[] = { + {I_h4_UA, 8, 800, 9000, 10000}, + {I_pt5_UA, 10, 100, 990, 4600}, + {I_14_UA, 17, 14, 1050, 700}, + {I_l4_UA, 10, 4, 1165, 110}, + {I_1_UA, 0, 1, 1200, 65}, +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = MSM89XX_IRQ_MBHC_HS_DET, + .mbhc_btn_press_intr = MSM89XX_IRQ_MBHC_PRESS, + .mbhc_btn_release_intr = MSM89XX_IRQ_MBHC_RELEASE, + .mbhc_hs_ins_intr = MSM89XX_IRQ_MBHC_INSREM_DET1, + .mbhc_hs_rem_intr = MSM89XX_IRQ_MBHC_INSREM_DET, + .hph_left_ocp = MSM89XX_IRQ_HPHL_OCP, + .hph_right_ocp = MSM89XX_IRQ_HPHR_OCP, +}; + +static int msm_anlg_cdc_dt_parse_vreg_info(struct device *dev, + struct sdm660_cdc_regulator *vreg, + const char *vreg_name, + bool ondemand); +static struct sdm660_cdc_pdata *msm_anlg_cdc_populate_dt_pdata( + struct device *dev); +static int msm_anlg_cdc_enable_ext_mb_source(struct wcd_mbhc *wcd_mbhc, + bool turn_on); +static void msm_anlg_cdc_trim_btn_reg(struct snd_soc_codec *codec); +static void msm_anlg_cdc_set_micb_v(struct snd_soc_codec *codec); +static void msm_anlg_cdc_set_boost_v(struct snd_soc_codec *codec); +static void msm_anlg_cdc_set_auto_zeroing(struct snd_soc_codec *codec, + bool enable); +static void msm_anlg_cdc_configure_cap(struct snd_soc_codec *codec, + bool micbias1, bool micbias2); +static bool msm_anlg_cdc_use_mb(struct snd_soc_codec *codec); + +static int get_codec_version(struct sdm660_cdc_priv *sdm660_cdc) +{ + if (sdm660_cdc->codec_version == DRAX_CDC) + return DRAX_CDC; + else if (sdm660_cdc->codec_version == DIANGU) + return DIANGU; + else if (sdm660_cdc->codec_version == CAJON_2_0) + return CAJON_2_0; + else if (sdm660_cdc->codec_version == CAJON) + return CAJON; + else if (sdm660_cdc->codec_version == CONGA) + return CONGA; + else if (sdm660_cdc->pmic_rev == TOMBAK_2_0) + return TOMBAK_2_0; + else if (sdm660_cdc->pmic_rev == TOMBAK_1_0) + return TOMBAK_1_0; + + pr_err("%s: unsupported codec version\n", __func__); + return UNSUPPORTED; +} + +static void wcd_mbhc_meas_imped(struct snd_soc_codec *codec, + s16 *impedance_l, s16 *impedance_r) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if ((sdm660_cdc->imped_det_pin == WCD_MBHC_DET_BOTH) || + (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHL)) { + /* Enable ZDET_L_MEAS_EN */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x08); + /* Wait for 2ms for measurement to complete */ + usleep_range(2000, 2100); + /* Read Left impedance value from Result1 */ + *impedance_l = snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + /* Enable ZDET_R_MEAS_EN */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x00); + } + if ((sdm660_cdc->imped_det_pin == WCD_MBHC_DET_BOTH) || + (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR)) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x04, 0x04); + /* Wait for 2ms for measurement to complete */ + usleep_range(2000, 2100); + /* Read Right impedance value from Result1 */ + *impedance_r = snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x04, 0x00); + } +} + +static void msm_anlg_cdc_set_ref_current(struct snd_soc_codec *codec, + enum wcd_curr_ref curr_ref) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: curr_ref: %d\n", __func__, curr_ref); + + if (get_codec_version(sdm660_cdc) < CAJON) + dev_dbg(codec->dev, "%s: Setting ref current not required\n", + __func__); + + sdm660_cdc->imped_i_ref = imped_i_ref[curr_ref]; + + switch (curr_ref) { + case I_h4_UA: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x01); + break; + case I_pt5_UA: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x04); + break; + case I_14_UA: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x03); + break; + case I_l4_UA: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x01); + break; + case I_1_UA: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x07, 0x00); + break; + default: + pr_debug("%s: No ref current set\n", __func__); + break; + } +} + +static bool msm_anlg_cdc_adj_ref_current(struct snd_soc_codec *codec, + s16 *impedance_l, s16 *impedance_r) +{ + int i = 2; + s16 compare_imp = 0; + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR) + compare_imp = *impedance_r; + else + compare_imp = *impedance_l; + + if (get_codec_version(sdm660_cdc) < CAJON) { + dev_dbg(codec->dev, + "%s: Reference current adjustment not required\n", + __func__); + return false; + } + + while (compare_imp < imped_i_ref[i].min_val) { + msm_anlg_cdc_set_ref_current(codec, imped_i_ref[++i].curr_ref); + wcd_mbhc_meas_imped(codec, impedance_l, impedance_r); + compare_imp = (sdm660_cdc->imped_det_pin == + WCD_MBHC_DET_HPHR) ? *impedance_r : *impedance_l; + if (i >= I_1_UA) + break; + } + return true; +} + +void msm_anlg_cdc_spk_ext_pa_cb( + int (*codec_spk_ext_pa)(struct snd_soc_codec *codec, + int enable), struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc; + + if (!codec) { + pr_err("%s: NULL codec pointer!\n", __func__); + return; + } + + sdm660_cdc = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: Enter\n", __func__); + sdm660_cdc->codec_spk_ext_pa_cb = codec_spk_ext_pa; +} + +static void msm_anlg_cdc_compute_impedance(struct snd_soc_codec *codec, s16 l, + s16 r, uint32_t *zl, uint32_t *zr, + bool high) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + uint32_t rl = 0, rr = 0; + struct wcd_imped_i_ref R = sdm660_cdc->imped_i_ref; + int codec_ver = get_codec_version(sdm660_cdc); + + switch (codec_ver) { + case TOMBAK_1_0: + case TOMBAK_2_0: + case CONGA: + if (high) { + dev_dbg(codec->dev, + "%s: This plug has high range impedance\n", + __func__); + rl = (uint32_t)(((100 * (l * 400 - 200))/96) - 230); + rr = (uint32_t)(((100 * (r * 400 - 200))/96) - 230); + } else { + dev_dbg(codec->dev, + "%s: This plug has low range impedance\n", + __func__); + rl = (uint32_t)(((1000 * (l * 2 - 1))/1165) - (13/10)); + rr = (uint32_t)(((1000 * (r * 2 - 1))/1165) - (13/10)); + } + break; + case CAJON: + case CAJON_2_0: + case DIANGU: + case DRAX_CDC: + if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHL) { + rr = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * r - 5)) - + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + rl = (uint32_t)(((10000 * (R.multiplier * (10 * l - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + } else if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR) { + rr = (uint32_t)(((10000 * (R.multiplier * (10 * r - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + rl = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * l - 5))- + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + } else if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_NONE) { + rr = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * r - 5)) - + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + rl = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * l - 5))- + (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN); + } else { + rr = (uint32_t)(((10000 * (R.multiplier * (10 * r - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + rl = (uint32_t)(((10000 * (R.multiplier * (10 * l - 5))) + - R.offset * R.gain_adj)/(R.gain_adj * 100)); + } + break; + default: + dev_dbg(codec->dev, "%s: No codec mentioned\n", __func__); + break; + } + *zl = rl; + *zr = rr; +} + +static struct firmware_cal *msm_anlg_cdc_get_hwdep_fw_cal( + struct wcd_mbhc *wcd_mbhc, + enum wcd_cal_type type) +{ + struct sdm660_cdc_priv *sdm660_cdc; + struct firmware_cal *hwdep_cal; + struct snd_soc_codec *codec = wcd_mbhc->codec; + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + sdm660_cdc = snd_soc_codec_get_drvdata(codec); + hwdep_cal = wcdcal_get_fw_cal(sdm660_cdc->fw_data, type); + if (!hwdep_cal) { + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + return NULL; + } + return hwdep_cal; +} + +static void wcd9xxx_spmi_irq_control(struct snd_soc_codec *codec, + int irq, bool enable) +{ + if (enable) + wcd9xxx_spmi_enable_irq(irq); + else + wcd9xxx_spmi_disable_irq(irq); +} + +static void msm_anlg_cdc_mbhc_clk_setup(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x08, 0x08); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x08, 0x00); +} + +static int msm_anlg_cdc_mbhc_map_btn_code_to_num(struct snd_soc_codec *codec) +{ + int btn_code; + int btn; + + btn_code = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + + switch (btn_code) { + case 0: + btn = 0; + break; + case 1: + btn = 1; + break; + case 3: + btn = 2; + break; + case 7: + btn = 3; + break; + case 15: + btn = 4; + break; + default: + btn = -EINVAL; + break; + }; + + return btn; +} + +static bool msm_anlg_cdc_spmi_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + if (lock) + return wcd9xxx_spmi_lock_sleep(); + wcd9xxx_spmi_unlock_sleep(); + return 0; +} + +static bool msm_anlg_cdc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + if (micb_num == MIC_BIAS_1) + return (snd_soc_read(mbhc->codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN) & + 0x80); + if (micb_num == MIC_BIAS_2) + return (snd_soc_read(mbhc->codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN) & + 0x80); + return false; +} + +static void msm_anlg_cdc_enable_master_bias(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, + 0x30, 0x30); + else + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, + 0x30, 0x00); +} + +static void msm_anlg_cdc_mbhc_common_micb_ctrl(struct snd_soc_codec *codec, + int event, bool enable) +{ + u16 reg; + u8 mask; + u8 val; + + switch (event) { + case MBHC_COMMON_MICB_PRECHARGE: + reg = MSM89XX_PMIC_ANALOG_MICB_1_CTL; + mask = 0x60; + val = (enable ? 0x60 : 0x00); + break; + case MBHC_COMMON_MICB_SET_VAL: + reg = MSM89XX_PMIC_ANALOG_MICB_1_VAL; + mask = 0xFF; + val = (enable ? 0xC0 : 0x00); + break; + case MBHC_COMMON_MICB_TAIL_CURR: + reg = MSM89XX_PMIC_ANALOG_MICB_1_EN; + mask = 0x04; + val = (enable ? 0x04 : 0x00); + break; + default: + dev_err(codec->dev, + "%s: Invalid event received\n", __func__); + return; + }; + snd_soc_update_bits(codec, reg, mask, val); +} + +static void msm_anlg_cdc_mbhc_internal_micbias_ctrl(struct snd_soc_codec *codec, + int micbias_num, + bool enable) +{ + if (micbias_num == 1) { + if (enable) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, + 0x10, 0x10); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, + 0x10, 0x00); + } +} + +static bool msm_anlg_cdc_mbhc_hph_pa_on_status(struct snd_soc_codec *codec) +{ + return (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN) & + 0x30) ? true : false; +} + +static void msm_anlg_cdc_mbhc_program_btn_thr(struct snd_soc_codec *codec, + s16 *btn_low, s16 *btn_high, + int num_btn, bool is_micbias) +{ + int i; + u32 course, fine, reg_val; + u16 reg_addr = MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL; + s16 *btn_voltage; + + btn_voltage = ((is_micbias) ? btn_high : btn_low); + + for (i = 0; i < num_btn; i++) { + course = (btn_voltage[i] / SDM660_CDC_MBHC_BTN_COARSE_ADJ); + fine = ((btn_voltage[i] % SDM660_CDC_MBHC_BTN_COARSE_ADJ) / + SDM660_CDC_MBHC_BTN_FINE_ADJ); + + reg_val = (course << 5) | (fine << 2); + snd_soc_update_bits(codec, reg_addr, 0xFC, reg_val); + dev_dbg(codec->dev, + "%s: course: %d fine: %d reg_addr: %x reg_val: %x\n", + __func__, course, fine, reg_addr, reg_val); + reg_addr++; + } +} + +static void msm_anlg_cdc_mbhc_calc_impedance(struct wcd_mbhc *mbhc, + uint32_t *zl, uint32_t *zr) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + s16 impedance_l, impedance_r; + s16 impedance_l_fixed; + s16 reg0, reg1, reg2, reg3, reg4; + bool high = false; + bool min_range_used = false; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + reg0 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER); + reg1 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL); + reg2 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2); + reg3 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MICB_2_EN); + reg4 = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL); + + sdm660_cdc->imped_det_pin = WCD_MBHC_DET_BOTH; + mbhc->hph_type = WCD_MBHC_HPH_NONE; + + /* disable FSM and micbias and enable pullup*/ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x80, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0xA5, 0x25); + /* + * Enable legacy electrical detection current sources + * and disable fast ramp and enable manual switching + * of extra capacitance + */ + dev_dbg(codec->dev, "%s: Setup for impedance det\n", __func__); + + msm_anlg_cdc_set_ref_current(codec, I_h4_UA); + + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, + 0x06, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, + 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, + 0x02, 0x00); + + dev_dbg(codec->dev, "%s: Start performing impedance detection\n", + __func__); + + wcd_mbhc_meas_imped(codec, &impedance_l, &impedance_r); + + if (impedance_l > 2 || impedance_r > 2) { + high = true; + if (!mbhc->mbhc_cfg->mono_stero_detection) { + /* Set ZDET_CHG to 0 to discharge ramp */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait 40ms for the discharge ramp to complete */ + usleep_range(40000, 40100); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x03, 0x00); + sdm660_cdc->imped_det_pin = (impedance_l > 2 && + impedance_r > 2) ? + WCD_MBHC_DET_NONE : + ((impedance_l > 2) ? + WCD_MBHC_DET_HPHR : + WCD_MBHC_DET_HPHL); + if (sdm660_cdc->imped_det_pin == WCD_MBHC_DET_NONE) + goto exit; + } else { + if (get_codec_version(sdm660_cdc) >= CAJON) { + if (impedance_l == 63 && impedance_r == 63) { + dev_dbg(codec->dev, + "%s: HPHL and HPHR are floating\n", + __func__); + sdm660_cdc->imped_det_pin = + WCD_MBHC_DET_NONE; + mbhc->hph_type = WCD_MBHC_HPH_NONE; + } else if (impedance_l == 63 + && impedance_r < 63) { + dev_dbg(codec->dev, + "%s: Mono HS with HPHL floating\n", + __func__); + sdm660_cdc->imped_det_pin = + WCD_MBHC_DET_HPHR; + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } else if (impedance_r == 63 && + impedance_l < 63) { + dev_dbg(codec->dev, + "%s: Mono HS with HPHR floating\n", + __func__); + sdm660_cdc->imped_det_pin = + WCD_MBHC_DET_HPHL; + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } else if (impedance_l > 3 && impedance_r > 3 && + (impedance_l == impedance_r)) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, + 0x06, 0x06); + wcd_mbhc_meas_imped(codec, &impedance_l, + &impedance_r); + if (impedance_r == impedance_l) + dev_dbg(codec->dev, + "%s: Mono Headset\n", + __func__); + sdm660_cdc->imped_det_pin = + WCD_MBHC_DET_NONE; + mbhc->hph_type = + WCD_MBHC_HPH_MONO; + } else { + dev_dbg(codec->dev, + "%s: STEREO headset is found\n", + __func__); + sdm660_cdc->imped_det_pin = + WCD_MBHC_DET_BOTH; + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } + } + } + } + + msm_anlg_cdc_set_ref_current(codec, I_pt5_UA); + msm_anlg_cdc_set_ref_current(codec, I_14_UA); + + /* Enable RAMP_L , RAMP_R & ZDET_CHG*/ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x03, 0x03); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x02); + /* wait for 50msec for the HW to apply ramp on HPHL and HPHR */ + usleep_range(50000, 50100); + /* Enable ZDET_DISCHG_CAP_CTL to add extra capacitance */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x01, 0x01); + /* wait for 5msec for the voltage to get stable */ + usleep_range(5000, 5100); + + wcd_mbhc_meas_imped(codec, &impedance_l, &impedance_r); + + min_range_used = msm_anlg_cdc_adj_ref_current(codec, + &impedance_l, &impedance_r); + if (!mbhc->mbhc_cfg->mono_stero_detection) { + /* Set ZDET_CHG to 0 to discharge ramp */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait for 40msec for the capacitor to discharge */ + usleep_range(40000, 40100); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x03, 0x00); + goto exit; + } + + /* we are setting ref current to the minimun range or the measured + * value larger than the minimum value, so min_range_used is true. + * If the headset is mono headset with either HPHL or HPHR floating + * then we have already done the mono stereo detection and do not + * need to continue further. + */ + + if (!min_range_used || + sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHL || + sdm660_cdc->imped_det_pin == WCD_MBHC_DET_HPHR) + goto exit; + + + /* Disable Set ZDET_CONN_RAMP_L and enable ZDET_CONN_FIXED_L */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x02, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, + 0x02, 0x02); + /* Set ZDET_CHG to 0 */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait for 40msec for the capacitor to discharge */ + usleep_range(40000, 40100); + + /* Set ZDET_CONN_RAMP_R to 0 */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x01, 0x00); + /* Enable ZDET_L_MEAS_EN */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x08); + /* wait for 2msec for the HW to compute left inpedance value */ + usleep_range(2000, 2100); + /* Read Left impedance value from Result1 */ + impedance_l_fixed = snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT); + /* Disable ZDET_L_MEAS_EN */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x08, 0x00); + /* + * Assume impedance_l is L1, impedance_l_fixed is L2. + * If the following condition is met, we can take this + * headset as mono one with impedance of L2. + * Otherwise, take it as stereo with impedance of L1. + * Condition: + * abs[(L2-0.5L1)/(L2+0.5L1)] < abs [(L2-L1)/(L2+L1)] + */ + if ((abs(impedance_l_fixed - impedance_l/2) * + (impedance_l_fixed + impedance_l)) >= + (abs(impedance_l_fixed - impedance_l) * + (impedance_l_fixed + impedance_l/2))) { + dev_dbg(codec->dev, + "%s: STEREO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + dev_dbg(codec->dev, + "%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + impedance_l = impedance_l_fixed; + } + /* Enable ZDET_CHG */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x02); + /* wait for 10msec for the capacitor to charge */ + usleep_range(10000, 10100); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, + 0x02, 0x00); + /* Set ZDET_CHG to 0 to discharge HPHL */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, + 0x02, 0x00); + /* wait for 40msec for the capacitor to discharge */ + usleep_range(40000, 40100); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, + 0x02, 0x00); + +exit: + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, reg4); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MICB_2_EN, reg3); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, reg1); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, reg0); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, reg2); + msm_anlg_cdc_compute_impedance(codec, impedance_l, impedance_r, + zl, zr, high); + + dev_dbg(codec->dev, "%s: RL %d ohm, RR %d ohm\n", __func__, *zl, *zr); + dev_dbg(codec->dev, "%s: Impedance detection completed\n", __func__); +} + +static int msm_anlg_cdc_dig_register_notifier(void *handle, + struct notifier_block *nblock, + bool enable) +{ + struct sdm660_cdc_priv *handle_cdc = handle; + + if (enable) + return blocking_notifier_chain_register(&handle_cdc->notifier, + nblock); + + return blocking_notifier_chain_unregister(&handle_cdc->notifier, + nblock); +} + +static int msm_anlg_cdc_mbhc_register_notifier(struct wcd_mbhc *wcd_mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct snd_soc_codec *codec = wcd_mbhc->codec; + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (enable) + return blocking_notifier_chain_register( + &sdm660_cdc->notifier_mbhc, + nblock); + + return blocking_notifier_chain_unregister(&sdm660_cdc->notifier_mbhc, + nblock); +} + +static int msm_anlg_cdc_request_irq(struct snd_soc_codec *codec, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + return wcd9xxx_spmi_request_irq(irq, handler, name, data); +} + +static int msm_anlg_cdc_free_irq(struct snd_soc_codec *codec, + int irq, void *data) +{ + return wcd9xxx_spmi_free_irq(irq, data); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .enable_mb_source = msm_anlg_cdc_enable_ext_mb_source, + .trim_btn_reg = msm_anlg_cdc_trim_btn_reg, + .compute_impedance = msm_anlg_cdc_mbhc_calc_impedance, + .set_micbias_value = msm_anlg_cdc_set_micb_v, + .set_auto_zeroing = msm_anlg_cdc_set_auto_zeroing, + .get_hwdep_fw_cal = msm_anlg_cdc_get_hwdep_fw_cal, + .set_cap_mode = msm_anlg_cdc_configure_cap, + .register_notifier = msm_anlg_cdc_mbhc_register_notifier, + .request_irq = msm_anlg_cdc_request_irq, + .irq_control = wcd9xxx_spmi_irq_control, + .free_irq = msm_anlg_cdc_free_irq, + .clk_setup = msm_anlg_cdc_mbhc_clk_setup, + .map_btn_code_to_num = msm_anlg_cdc_mbhc_map_btn_code_to_num, + .lock_sleep = msm_anlg_cdc_spmi_lock_sleep, + .micbias_enable_status = msm_anlg_cdc_micb_en_status, + .mbhc_bias = msm_anlg_cdc_enable_master_bias, + .mbhc_common_micb_ctrl = msm_anlg_cdc_mbhc_common_micb_ctrl, + .micb_internal = msm_anlg_cdc_mbhc_internal_micbias_ctrl, + .hph_pa_on_status = msm_anlg_cdc_mbhc_hph_pa_on_status, + .set_btn_thr = msm_anlg_cdc_mbhc_program_btn_thr, + .extn_use_mb = msm_anlg_cdc_use_mb, +}; + +static const uint32_t wcd_imped_val[] = {4, 8, 12, 13, 16, + 20, 24, 28, 32, + 36, 40, 44, 48}; + +static void msm_anlg_cdc_dig_notifier_call(struct snd_soc_codec *codec, + const enum dig_cdc_notify_event event) +{ + struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: notifier call event %d\n", __func__, event); + blocking_notifier_call_chain(&sdm660_cdc->notifier, + event, NULL); +} + +static void msm_anlg_cdc_notifier_call(struct snd_soc_codec *codec, + const enum wcd_notify_event event) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: notifier call event %d\n", __func__, event); + blocking_notifier_call_chain(&sdm660_cdc->notifier_mbhc, event, + &sdm660_cdc->mbhc); +} + +static void msm_anlg_cdc_boost_on(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F, 0x0F); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30); + if (get_codec_version(sdm660_cdc) < CAJON_2_0) + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82); + else + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0xA2); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0x69, 0x69); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, + 0x01, 0x01); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, + 0x88, 0x88); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x03, 0x03); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, + 0xE1, 0xE1); + if (get_codec_version(sdm660_cdc) < CAJON_2_0) { + snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x20); + /* Wait for 1ms after clock ctl enable */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0xDF, 0xDF); + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + } else { + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x40, 0x00); + snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x80, 0x80); + /* Wait for 500us after BOOST_EN to happen */ + usleep_range(500, 510); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x40, 0x40); + /* Wait for 500us after BOOST pulse_skip */ + usleep_range(500, 510); + } +} + +static void msm_anlg_cdc_boost_off(struct snd_soc_codec *codec) +{ + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0xDF, 0x5F); + snd_soc_update_bits(codec, MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x00); +} + +static void msm_anlg_cdc_bypass_on(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) < CAJON_2_0) { + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, + 0x07); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x01, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x40, 0x40); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0xDF, 0xDF); + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x20, 0x20); + } +} + +static void msm_anlg_cdc_bypass_off(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) < CAJON_2_0) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, + 0x80, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x80, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x02, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x40, 0x00); + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_BYPASS_MODE, + 0x20, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x20, 0x00); + } +} + +static void msm_anlg_cdc_boost_mode_sequence(struct snd_soc_codec *codec, + int flag) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (flag == EAR_PMU) { + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->ear_pa_boost_set) { + msm_anlg_cdc_boost_off(codec); + msm_anlg_cdc_bypass_on(codec); + } + break; + case BOOST_ALWAYS: + msm_anlg_cdc_boost_on(codec); + break; + case BYPASS_ALWAYS: + msm_anlg_cdc_bypass_on(codec); + break; + case BOOST_ON_FOREVER: + msm_anlg_cdc_boost_on(codec); + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + } else if (flag == EAR_PMD) { + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->ear_pa_boost_set) + msm_anlg_cdc_bypass_off(codec); + break; + case BOOST_ALWAYS: + msm_anlg_cdc_boost_off(codec); + /* 80ms for EAR boost to settle down */ + msleep(80); + break; + case BYPASS_ALWAYS: + /* nothing to do as bypass on always */ + break; + case BOOST_ON_FOREVER: + /* nothing to do as boost on forever */ + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + } else if (flag == SPK_PMU) { + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->spk_boost_set) { + msm_anlg_cdc_bypass_off(codec); + msm_anlg_cdc_boost_on(codec); + } + break; + case BOOST_ALWAYS: + msm_anlg_cdc_boost_on(codec); + break; + case BYPASS_ALWAYS: + msm_anlg_cdc_bypass_on(codec); + break; + case BOOST_ON_FOREVER: + msm_anlg_cdc_boost_on(codec); + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + } else if (flag == SPK_PMD) { + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->spk_boost_set) { + msm_anlg_cdc_boost_off(codec); + /* + * Add 40 ms sleep for the spk + * boost to settle down + */ + msleep(40); + } + break; + case BOOST_ALWAYS: + msm_anlg_cdc_boost_off(codec); + /* + * Add 40 ms sleep for the spk + * boost to settle down + */ + msleep(40); + break; + case BYPASS_ALWAYS: + /* nothing to do as bypass on always */ + break; + case BOOST_ON_FOREVER: + /* nothing to do as boost on forever */ + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + } +} + +static int msm_anlg_cdc_dt_parse_vreg_info(struct device *dev, + struct sdm660_cdc_regulator *vreg, const char *vreg_name, + bool ondemand) +{ + int len, ret = 0; + const __be32 *prop; + char prop_name[CODEC_DT_MAX_PROP_SIZE]; + struct device_node *regnode = NULL; + u32 prop_val; + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", + vreg_name); + regnode = of_parse_phandle(dev->of_node, prop_name, 0); + + if (!regnode) { + dev_err(dev, "Looking up %s property in node %s failed\n", + prop_name, dev->of_node->full_name); + return -ENODEV; + } + + dev_dbg(dev, "Looking up %s property in node %s\n", + prop_name, dev->of_node->full_name); + + vreg->name = vreg_name; + vreg->ondemand = ondemand; + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, + "qcom,%s-voltage", vreg_name); + prop = of_get_property(dev->of_node, prop_name, &len); + + if (!prop || (len != (2 * sizeof(__be32)))) { + dev_err(dev, "%s %s property\n", + prop ? "invalid format" : "no", prop_name); + return -EINVAL; + } + vreg->min_uv = be32_to_cpup(&prop[0]); + vreg->max_uv = be32_to_cpup(&prop[1]); + + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, + "qcom,%s-current", vreg_name); + + ret = of_property_read_u32(dev->of_node, prop_name, &prop_val); + if (ret) { + dev_err(dev, "Looking up %s property in node %s failed", + prop_name, dev->of_node->full_name); + return -EFAULT; + } + vreg->optimum_ua = prop_val; + + dev_dbg(dev, "%s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n\n", vreg->name, + vreg->min_uv, vreg->max_uv, vreg->optimum_ua, vreg->ondemand); + return 0; +} + +static void msm_anlg_cdc_dt_parse_boost_info(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + const char *prop_name = "qcom,cdc-boost-voltage"; + int boost_voltage, ret; + + ret = of_property_read_u32(codec->dev->of_node, prop_name, + &boost_voltage); + if (ret) { + dev_dbg(codec->dev, "Looking up %s property in node %s failed\n", + prop_name, codec->dev->of_node->full_name); + boost_voltage = DEFAULT_BOOST_VOLTAGE; + } + if (boost_voltage < MIN_BOOST_VOLTAGE || + boost_voltage > MAX_BOOST_VOLTAGE) { + dev_err(codec->dev, + "Incorrect boost voltage. Reverting to default\n"); + boost_voltage = DEFAULT_BOOST_VOLTAGE; + } + + sdm660_cdc_priv->boost_voltage = + VOLTAGE_CONVERTER(boost_voltage, MIN_BOOST_VOLTAGE, + BOOST_VOLTAGE_STEP); + dev_dbg(codec->dev, "Boost voltage value is: %d\n", + boost_voltage); +} + +static void msm_anlg_cdc_dt_parse_micbias_info(struct device *dev, + struct wcd_micbias_setting *micbias) +{ + const char *prop_name = "qcom,cdc-micbias-cfilt-mv"; + int ret; + + ret = of_property_read_u32(dev->of_node, prop_name, + &micbias->cfilt1_mv); + if (ret) { + dev_dbg(dev, "Looking up %s property in node %s failed", + prop_name, dev->of_node->full_name); + micbias->cfilt1_mv = MICBIAS_DEFAULT_VAL; + } +} + +static struct sdm660_cdc_pdata *msm_anlg_cdc_populate_dt_pdata( + struct device *dev) +{ + struct sdm660_cdc_pdata *pdata; + int ret, static_cnt, ond_cnt, idx, i; + const char *name = NULL; + const char *static_prop_name = "qcom,cdc-static-supplies"; + const char *ond_prop_name = "qcom,cdc-on-demand-supplies"; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + static_cnt = of_property_count_strings(dev->of_node, static_prop_name); + if (static_cnt < 0) { + dev_err(dev, "%s: Failed to get static supplies %d\n", __func__, + static_cnt); + ret = -EINVAL; + goto err; + } + + /* On-demand supply list is an optional property */ + ond_cnt = of_property_count_strings(dev->of_node, ond_prop_name); + if (ond_cnt < 0) + ond_cnt = 0; + + WARN_ON(static_cnt <= 0 || ond_cnt < 0); + if ((static_cnt + ond_cnt) > ARRAY_SIZE(pdata->regulator)) { + dev_err(dev, "%s: Num of supplies %u > max supported %zd\n", + __func__, (static_cnt + ond_cnt), + ARRAY_SIZE(pdata->regulator)); + ret = -EINVAL; + goto err; + } + + for (idx = 0; idx < static_cnt; idx++) { + ret = of_property_read_string_index(dev->of_node, + static_prop_name, idx, + &name); + if (ret) { + dev_err(dev, "%s: of read string %s idx %d error %d\n", + __func__, static_prop_name, idx, ret); + goto err; + } + + dev_dbg(dev, "%s: Found static cdc supply %s\n", __func__, + name); + ret = msm_anlg_cdc_dt_parse_vreg_info(dev, + &pdata->regulator[idx], + name, false); + if (ret) { + dev_err(dev, "%s:err parsing vreg for %s idx %d\n", + __func__, name, idx); + goto err; + } + } + + for (i = 0; i < ond_cnt; i++, idx++) { + ret = of_property_read_string_index(dev->of_node, ond_prop_name, + i, &name); + if (ret) { + dev_err(dev, "%s: err parsing on_demand for %s idx %d\n", + __func__, ond_prop_name, i); + goto err; + } + + dev_dbg(dev, "%s: Found on-demand cdc supply %s\n", __func__, + name); + ret = msm_anlg_cdc_dt_parse_vreg_info(dev, + &pdata->regulator[idx], + name, true); + if (ret) { + dev_err(dev, "%s: err parsing vreg on_demand for %s idx %d\n", + __func__, name, idx); + goto err; + } + } + msm_anlg_cdc_dt_parse_micbias_info(dev, &pdata->micbias); + + return pdata; +err: + devm_kfree(dev, pdata); + dev_err(dev, "%s: Failed to populate DT data ret = %d\n", + __func__, ret); + return NULL; +} + +static int msm_anlg_cdc_codec_enable_on_demand_supply( + struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + struct on_demand_supply *supply; + + if (w->shift >= ON_DEMAND_SUPPLIES_MAX) { + dev_err(codec->dev, "%s: error index > MAX Demand supplies", + __func__); + ret = -EINVAL; + goto out; + } + dev_dbg(codec->dev, "%s: supply: %s event: %d ref: %d\n", + __func__, on_demand_supply_name[w->shift], event, + atomic_read(&sdm660_cdc->on_demand_list[w->shift].ref)); + + supply = &sdm660_cdc->on_demand_list[w->shift]; + WARN_ONCE(!supply->supply, "%s isn't defined\n", + on_demand_supply_name[w->shift]); + if (!supply->supply) { + dev_err(codec->dev, "%s: err supply not present ond for %d", + __func__, w->shift); + goto out; + } + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (atomic_inc_return(&supply->ref) == 1) { + ret = regulator_set_voltage(supply->supply, + supply->min_uv, + supply->max_uv); + if (ret) { + dev_err(codec->dev, + "Setting regulator voltage(en) for micbias with err = %d\n", + ret); + goto out; + } + ret = regulator_set_load(supply->supply, + supply->optimum_ua); + if (ret < 0) { + dev_err(codec->dev, + "Setting regulator optimum mode(en) failed for micbias with err = %d\n", + ret); + goto out; + } + ret = regulator_enable(supply->supply); + } + if (ret) + dev_err(codec->dev, "%s: Failed to enable %s\n", + __func__, + on_demand_supply_name[w->shift]); + break; + case SND_SOC_DAPM_POST_PMD: + if (atomic_read(&supply->ref) == 0) { + dev_dbg(codec->dev, "%s: %s supply has been disabled.\n", + __func__, on_demand_supply_name[w->shift]); + goto out; + } + if (atomic_dec_return(&supply->ref) == 0) { + ret = regulator_disable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to disable %s\n", + __func__, + on_demand_supply_name[w->shift]); + ret = regulator_set_voltage(supply->supply, + 0, + supply->max_uv); + if (ret) { + dev_err(codec->dev, + "Setting regulator voltage(dis) failed for micbias with err = %d\n", + ret); + goto out; + } + ret = regulator_set_load(supply->supply, 0); + if (ret < 0) + dev_err(codec->dev, + "Setting regulator optimum mode(dis) failed for micbias with err = %d\n", + ret); + } + break; + default: + break; + } +out: + return ret; +} + +static int msm_anlg_cdc_codec_enable_clock_block(struct snd_soc_codec *codec, + int enable) +{ + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + if (enable) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30, 0x30); + msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_CLK_ON); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x0C); + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x00); + } + return 0; +} + +static int msm_anlg_cdc_codec_enable_charge_pump(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm_anlg_cdc_codec_enable_clock_block(codec, 1); + if (!(strcmp(w->name, "EAR CP"))) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x80); + msm_anlg_cdc_boost_mode_sequence(codec, EAR_PMU); + } else if (get_codec_version(sdm660_cdc) >= DIANGU) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0xC0, 0xC0); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* Wait for 1ms post powerup of chargepump */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + break; + case SND_SOC_DAPM_POST_PMD: + /* Wait for 1ms post powerdown of chargepump */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + if (!(strcmp(w->name, "EAR CP"))) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x00); + if (sdm660_cdc->boost_option != BOOST_ALWAYS) { + dev_dbg(codec->dev, + "%s: boost_option:%d, tear down ear\n", + __func__, sdm660_cdc->boost_option); + msm_anlg_cdc_boost_mode_sequence(codec, + EAR_PMD); + } + /* + * Reset pa select bit from ear to hph after ear pa + * is disabled and HPH DAC disable to reduce ear + * turn off pop and avoid HPH pop in concurrency + */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x80, 0x00); + } else { + if (get_codec_version(sdm660_cdc) < DIANGU) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x40, 0x00); + if (sdm660_cdc->rx_bias_count == 0) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x00); + dev_dbg(codec->dev, "%s: rx_bias_count = %d\n", + __func__, sdm660_cdc->rx_bias_count); + } + break; + } + return 0; +} + +static int msm_anlg_cdc_ear_pa_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = + (sdm660_cdc->ear_pa_boost_set ? 1 : 0); + dev_dbg(codec->dev, "%s: sdm660_cdc->ear_pa_boost_set = %d\n", + __func__, sdm660_cdc->ear_pa_boost_set); + return 0; +} + +static int msm_anlg_cdc_ear_pa_boost_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + sdm660_cdc->ear_pa_boost_set = + (ucontrol->value.integer.value[0] ? true : false); + return 0; +} + +static int msm_anlg_cdc_loopback_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return pdata->lb_mode; +} + +static int msm_anlg_cdc_loopback_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + pdata->lb_mode = false; + break; + case 1: + pdata->lb_mode = true; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int msm_anlg_cdc_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) >= DIANGU) { + ear_pa_gain = snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC); + ear_pa_gain = (ear_pa_gain >> 1) & 0x3; + + if (ear_pa_gain == 0x00) { + ucontrol->value.integer.value[0] = 3; + } else if (ear_pa_gain == 0x01) { + ucontrol->value.integer.value[1] = 2; + } else if (ear_pa_gain == 0x02) { + ucontrol->value.integer.value[2] = 1; + } else if (ear_pa_gain == 0x03) { + ucontrol->value.integer.value[3] = 0; + } else { + dev_err(codec->dev, + "%s: ERROR: Unsupported Ear Gain = 0x%x\n", + __func__, ear_pa_gain); + return -EINVAL; + } + } else { + ear_pa_gain = snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL); + ear_pa_gain = (ear_pa_gain >> 5) & 0x1; + if (ear_pa_gain == 0x00) { + ucontrol->value.integer.value[0] = 0; + } else if (ear_pa_gain == 0x01) { + ucontrol->value.integer.value[0] = 3; + } else { + dev_err(codec->dev, + "%s: ERROR: Unsupported Ear Gain = 0x%x\n", + __func__, ear_pa_gain); + return -EINVAL; + } + } + ucontrol->value.integer.value[0] = ear_pa_gain; + dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__, ear_pa_gain); + return 0; +} + +static int msm_anlg_cdc_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (get_codec_version(sdm660_cdc) >= DIANGU) { + switch (ucontrol->value.integer.value[0]) { + case 0: + ear_pa_gain = 0x06; + break; + case 1: + ear_pa_gain = 0x04; + break; + case 2: + ear_pa_gain = 0x02; + break; + case 3: + ear_pa_gain = 0x00; + break; + default: + return -EINVAL; + } + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x06, ear_pa_gain); + } else { + switch (ucontrol->value.integer.value[0]) { + case 0: + ear_pa_gain = 0x00; + break; + case 3: + ear_pa_gain = 0x20; + break; + case 1: + case 2: + default: + return -EINVAL; + } + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, ear_pa_gain); + } + return 0; +} + +static int msm_anlg_cdc_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (sdm660_cdc->hph_mode == NORMAL_MODE) { + ucontrol->value.integer.value[0] = 0; + } else if (sdm660_cdc->hph_mode == HD2_MODE) { + ucontrol->value.integer.value[0] = 1; + } else { + dev_err(codec->dev, "%s: ERROR: Default HPH Mode= %d\n", + __func__, sdm660_cdc->hph_mode); + } + + dev_dbg(codec->dev, "%s: sdm660_cdc->hph_mode = %d\n", __func__, + sdm660_cdc->hph_mode); + return 0; +} + +static int msm_anlg_cdc_hph_mode_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + sdm660_cdc->hph_mode = NORMAL_MODE; + break; + case 1: + if (get_codec_version(sdm660_cdc) >= DIANGU) + sdm660_cdc->hph_mode = HD2_MODE; + break; + default: + sdm660_cdc->hph_mode = NORMAL_MODE; + break; + } + dev_dbg(codec->dev, "%s: sdm660_cdc->hph_mode_set = %d\n", + __func__, sdm660_cdc->hph_mode); + return 0; +} + +static int msm_anlg_cdc_boost_option_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (sdm660_cdc->boost_option == BOOST_SWITCH) { + ucontrol->value.integer.value[0] = 0; + } else if (sdm660_cdc->boost_option == BOOST_ALWAYS) { + ucontrol->value.integer.value[0] = 1; + } else if (sdm660_cdc->boost_option == BYPASS_ALWAYS) { + ucontrol->value.integer.value[0] = 2; + } else if (sdm660_cdc->boost_option == BOOST_ON_FOREVER) { + ucontrol->value.integer.value[0] = 3; + } else { + dev_err(codec->dev, "%s: ERROR: Unsupported Boost option= %d\n", + __func__, sdm660_cdc->boost_option); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: sdm660_cdc->boost_option = %d\n", __func__, + sdm660_cdc->boost_option); + return 0; +} + +static int msm_anlg_cdc_boost_option_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + sdm660_cdc->boost_option = BOOST_SWITCH; + break; + case 1: + sdm660_cdc->boost_option = BOOST_ALWAYS; + break; + case 2: + sdm660_cdc->boost_option = BYPASS_ALWAYS; + msm_anlg_cdc_bypass_on(codec); + break; + case 3: + sdm660_cdc->boost_option = BOOST_ON_FOREVER; + msm_anlg_cdc_boost_on(codec); + break; + default: + pr_err("%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: sdm660_cdc->boost_option_set = %d\n", + __func__, sdm660_cdc->boost_option); + return 0; +} + +static int msm_anlg_cdc_spk_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (sdm660_cdc->spk_boost_set == false) { + ucontrol->value.integer.value[0] = 0; + } else if (sdm660_cdc->spk_boost_set == true) { + ucontrol->value.integer.value[0] = 1; + } else { + dev_err(codec->dev, "%s: ERROR: Unsupported Speaker Boost = %d\n", + __func__, sdm660_cdc->spk_boost_set); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: sdm660_cdc->spk_boost_set = %d\n", __func__, + sdm660_cdc->spk_boost_set); + return 0; +} + +static int msm_anlg_cdc_spk_boost_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + sdm660_cdc->spk_boost_set = false; + break; + case 1: + sdm660_cdc->spk_boost_set = true; + break; + default: + return -EINVAL; + } + dev_dbg(codec->dev, "%s: sdm660_cdc->spk_boost_set = %d\n", + __func__, sdm660_cdc->spk_boost_set); + return 0; +} + +static int msm_anlg_cdc_ext_spk_boost_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (sdm660_cdc->ext_spk_boost_set == false) + ucontrol->value.integer.value[0] = 0; + else + ucontrol->value.integer.value[0] = 1; + + dev_dbg(codec->dev, "%s: sdm660_cdc->ext_spk_boost_set = %d\n", + __func__, sdm660_cdc->ext_spk_boost_set); + return 0; +} + +static int msm_anlg_cdc_ext_spk_boost_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + switch (ucontrol->value.integer.value[0]) { + case 0: + sdm660_cdc->ext_spk_boost_set = false; + break; + case 1: + sdm660_cdc->ext_spk_boost_set = true; + break; + default: + return -EINVAL; + } + dev_dbg(codec->dev, "%s: sdm660_cdc->spk_boost_set = %d\n", + __func__, sdm660_cdc->spk_boost_set); + return 0; +} + + +static const char * const msm_anlg_cdc_loopback_mode_ctrl_text[] = { + "DISABLE", "ENABLE"}; +static const struct soc_enum msm_anlg_cdc_loopback_mode_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_loopback_mode_ctrl_text), +}; + +static const char * const msm_anlg_cdc_ear_pa_boost_ctrl_text[] = { + "DISABLE", "ENABLE"}; +static const struct soc_enum msm_anlg_cdc_ear_pa_boost_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_ear_pa_boost_ctrl_text), +}; + +static const char * const msm_anlg_cdc_ear_pa_gain_text[] = { + "POS_1P5_DB", "POS_3_DB", "POS_4P5_DB", "POS_6_DB"}; +static const struct soc_enum msm_anlg_cdc_ear_pa_gain_enum[] = { + SOC_ENUM_SINGLE_EXT(4, msm_anlg_cdc_ear_pa_gain_text), +}; + +static const char * const msm_anlg_cdc_boost_option_ctrl_text[] = { + "BOOST_SWITCH", "BOOST_ALWAYS", "BYPASS_ALWAYS", + "BOOST_ON_FOREVER"}; +static const struct soc_enum msm_anlg_cdc_boost_option_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(4, msm_anlg_cdc_boost_option_ctrl_text), +}; +static const char * const msm_anlg_cdc_spk_boost_ctrl_text[] = { + "DISABLE", "ENABLE"}; +static const struct soc_enum msm_anlg_cdc_spk_boost_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_spk_boost_ctrl_text), +}; + +static const char * const msm_anlg_cdc_ext_spk_boost_ctrl_text[] = { + "DISABLE", "ENABLE"}; +static const struct soc_enum msm_anlg_cdc_ext_spk_boost_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_ext_spk_boost_ctrl_text), +}; + +static const char * const msm_anlg_cdc_hph_mode_ctrl_text[] = { + "NORMAL", "HD2"}; +static const struct soc_enum msm_anlg_cdc_hph_mode_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(msm_anlg_cdc_hph_mode_ctrl_text), + msm_anlg_cdc_hph_mode_ctrl_text), +}; + +/*cut of frequency for high pass filter*/ +static const char * const cf_text[] = { + "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz" +}; + + +static const struct snd_kcontrol_new msm_anlg_cdc_snd_controls[] = { + + SOC_ENUM_EXT("RX HPH Mode", msm_anlg_cdc_hph_mode_ctl_enum[0], + msm_anlg_cdc_hph_mode_get, msm_anlg_cdc_hph_mode_set), + + SOC_ENUM_EXT("Boost Option", msm_anlg_cdc_boost_option_ctl_enum[0], + msm_anlg_cdc_boost_option_get, msm_anlg_cdc_boost_option_set), + + SOC_ENUM_EXT("EAR PA Boost", msm_anlg_cdc_ear_pa_boost_ctl_enum[0], + msm_anlg_cdc_ear_pa_boost_get, msm_anlg_cdc_ear_pa_boost_set), + + SOC_ENUM_EXT("EAR PA Gain", msm_anlg_cdc_ear_pa_gain_enum[0], + msm_anlg_cdc_pa_gain_get, msm_anlg_cdc_pa_gain_put), + + SOC_ENUM_EXT("Speaker Boost", msm_anlg_cdc_spk_boost_ctl_enum[0], + msm_anlg_cdc_spk_boost_get, msm_anlg_cdc_spk_boost_set), + + SOC_ENUM_EXT("Ext Spk Boost", msm_anlg_cdc_ext_spk_boost_ctl_enum[0], + msm_anlg_cdc_ext_spk_boost_get, msm_anlg_cdc_ext_spk_boost_set), + + SOC_ENUM_EXT("LOOPBACK Mode", msm_anlg_cdc_loopback_mode_ctl_enum[0], + msm_anlg_cdc_loopback_mode_get, msm_anlg_cdc_loopback_mode_put), + SOC_SINGLE_TLV("ADC1 Volume", MSM89XX_PMIC_ANALOG_TX_1_EN, 3, + 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", MSM89XX_PMIC_ANALOG_TX_2_EN, 3, + 8, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", MSM89XX_PMIC_ANALOG_TX_3_EN, 3, + 8, 0, analog_gain), + + +}; + +static int tombak_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *priv = snd_soc_codec_get_drvdata(codec); + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + + hphr = mc->shift; + ret = wcd_mbhc_get_impedance(&priv->mbhc, &zl, &zr); + if (ret) + dev_dbg(codec->dev, "%s: Failed to get mbhc imped", __func__); + dev_dbg(codec->dev, "%s: zl %u, zr %u\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + tombak_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + tombak_hph_impedance_get, NULL), +}; + +static int tombak_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct sdm660_cdc_priv *priv = snd_soc_codec_get_drvdata(codec); + struct wcd_mbhc *mbhc; + + if (!priv) { + dev_err(codec->dev, + "%s: sdm660_cdc-wcd private data is NULL\n", + __func__); + return -EINVAL; + } + + mbhc = &priv->mbhc; + if (!mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized\n", __func__); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + tombak_get_hph_type, NULL), +}; + +static const char * const rdac2_mux_text[] = { + "ZERO", "RX2", "RX1" +}; + +static const struct soc_enum rdac2_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL, + 0, 3, rdac2_mux_text); + +static const char * const adc2_mux_text[] = { + "ZERO", "INP2", "INP3" +}; + +static const char * const ext_spk_text[] = { + "Off", "On" +}; + +static const char * const wsa_spk_text[] = { + "ZERO", "WSA" +}; + +static const struct soc_enum adc2_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(adc2_mux_text), adc2_mux_text); + +static const struct soc_enum ext_spk_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(ext_spk_text), ext_spk_text); + +static const struct soc_enum wsa_spk_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, + ARRAY_SIZE(wsa_spk_text), wsa_spk_text); + + + +static const struct snd_kcontrol_new ext_spk_mux = + SOC_DAPM_ENUM("Ext Spk Switch Mux", ext_spk_enum); + + + +static const struct snd_kcontrol_new tx_adc2_mux = + SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum); + + +static const struct snd_kcontrol_new rdac2_mux = + SOC_DAPM_ENUM("RDAC2 MUX Mux", rdac2_mux_enum); + +static const char * const ear_text[] = { + "ZERO", "Switch", +}; + +static const struct soc_enum ear_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(ear_text), ear_text); + +static const struct snd_kcontrol_new ear_pa_mux[] = { + SOC_DAPM_ENUM("EAR_S", ear_enum) +}; + +static const struct snd_kcontrol_new wsa_spk_mux[] = { + SOC_DAPM_ENUM("WSA Spk Switch", wsa_spk_enum) +}; + + + +static const char * const hph_text[] = { + "ZERO", "Switch", +}; + +static const struct soc_enum hph_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(hph_text), hph_text); + +static const struct snd_kcontrol_new hphl_mux[] = { + SOC_DAPM_ENUM("HPHL", hph_enum) +}; + +static const struct snd_kcontrol_new hphr_mux[] = { + SOC_DAPM_ENUM("HPHR", hph_enum) +}; + +static const struct snd_kcontrol_new spkr_mux[] = { + SOC_DAPM_ENUM("SPK", hph_enum) +}; + +static const char * const lo_text[] = { + "ZERO", "Switch", +}; + +static const struct soc_enum lo_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(hph_text), hph_text); + +static const struct snd_kcontrol_new lo_mux[] = { + SOC_DAPM_ENUM("LINE_OUT", lo_enum) +}; + +static void msm_anlg_cdc_codec_enable_adc_block(struct snd_soc_codec *codec, + int enable) +{ + struct sdm660_cdc_priv *wcd8x16 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d\n", __func__, enable); + + if (enable) { + wcd8x16->adc_count++; + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x10, 0x10); + } else { + wcd8x16->adc_count--; + if (!wcd8x16->adc_count) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x10, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, + 0x20, 0x0); + } + } +} + +static int msm_anlg_cdc_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 adc_reg; + u8 init_bit_shift; + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + adc_reg = MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2; + + if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN) + init_bit_shift = 5; + else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) || + (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN)) + init_bit_shift = 4; + else { + dev_err(codec->dev, "%s: Error, invalid adc register\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm_anlg_cdc_codec_enable_adc_block(codec, 1); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x02, 0x02); + /* + * Add delay of 10 ms to give sufficient time for the voltage + * to shoot up and settle so that the txfe init does not + * happen when the input voltage is changing too much. + */ + usleep_range(10000, 10010); + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, + 1 << init_bit_shift); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, + 0x03, 0x00); + else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) || + (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN)) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, + 0x03, 0x00); + /* Wait for 1ms to allow txfe settling time */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * Add delay of 12 ms before deasserting the init + * to reduce the tx pop + */ + usleep_range(12000, 12010); + snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00); + /* Wait for 1ms to allow txfe settling time post powerup */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + break; + case SND_SOC_DAPM_POST_PMD: + msm_anlg_cdc_codec_enable_adc_block(codec, 0); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x02, 0x00); + if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, + 0x03, 0x02); + else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) || + (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN)) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, + 0x03, 0x02); + + break; + } + return 0; +} + +static int msm_anlg_cdc_codec_enable_spk_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x01); + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (!sdm660_cdc->spk_boost_set) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x10, 0x10); + break; + case BOOST_ALWAYS: + case BOOST_ON_FOREVER: + break; + case BYPASS_ALWAYS: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x10, 0x10); + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + /* Wait for 1ms after SPK_DAC CTL setting */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0xE0); + if (get_codec_version(sdm660_cdc) != TOMBAK_1_0) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x01, 0x01); + break; + case SND_SOC_DAPM_POST_PMU: + /* Wait for 1ms after SPK_VBAT_LDO Enable */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->spk_boost_set) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0xEF); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 0x10, 0x00); + break; + case BOOST_ALWAYS: + case BOOST_ON_FOREVER: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0xEF); + break; + case BYPASS_ALWAYS: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x00); + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX3_MUTE_OFF); + snd_soc_update_bits(codec, w->reg, 0x80, 0x80); + break; + case SND_SOC_DAPM_PRE_PMD: + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX3_MUTE_ON); + /* + * Add 1 ms sleep for the mute to take effect + */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x10); + if (get_codec_version(sdm660_cdc) < CAJON_2_0) + msm_anlg_cdc_boost_mode_sequence(codec, SPK_PMD); + snd_soc_update_bits(codec, w->reg, 0x80, 0x00); + switch (sdm660_cdc->boost_option) { + case BOOST_SWITCH: + if (sdm660_cdc->spk_boost_set) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0x69); + break; + case BOOST_ALWAYS: + case BOOST_ON_FOREVER: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xEF, 0x69); + break; + case BYPASS_ALWAYS: + break; + default: + dev_err(codec->dev, + "%s: invalid boost option: %d\n", __func__, + sdm660_cdc->boost_option); + break; + } + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0x00); + /* Wait for 1ms to allow setting time for spkr path disable */ + usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x00); + if (get_codec_version(sdm660_cdc) != TOMBAK_1_0) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x00); + if (get_codec_version(sdm660_cdc) >= CAJON_2_0) + msm_anlg_cdc_boost_mode_sequence(codec, SPK_PMD); + break; + } + return 0; +} + +static int msm_anlg_cdc_codec_enable_dig_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + + dev_dbg(codec->dev, "%s event %d w->name %s\n", __func__, + event, w->name); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + msm_anlg_cdc_codec_enable_clock_block(codec, 1); + snd_soc_update_bits(codec, w->reg, 0x80, 0x80); + msm_anlg_cdc_boost_mode_sequence(codec, SPK_PMU); + break; + case SND_SOC_DAPM_POST_PMD: + if (sdm660_cdc->rx_bias_count == 0) + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x80, 0x00); + } + return 0; +} + + + +static bool msm_anlg_cdc_use_mb(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) < CAJON) + return true; + else + return false; +} + +static void msm_anlg_cdc_set_auto_zeroing(struct snd_soc_codec *codec, + bool enable) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) < CONGA) { + if (enable) + /* + * Set autozeroing for special headset detection and + * buttons to work. + */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x18, 0x10); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_2_EN, + 0x18, 0x00); + + } else { + dev_dbg(codec->dev, + "%s: Auto Zeroing is not required from CONGA\n", + __func__); + } +} + +static void msm_anlg_cdc_trim_btn_reg(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + if (get_codec_version(sdm660_cdc) == TOMBAK_1_0) { + pr_debug("%s: This device needs to be trimmed\n", __func__); + /* + * Calculate the trim value for each device used + * till is comes in production by hardware team + */ + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5, 0xA5); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_TRIM_CTRL2, + 0xFF, 0x30); + } else { + dev_dbg(codec->dev, "%s: This device is trimmed at ATE\n", + __func__); + } +} + +static int msm_anlg_cdc_enable_ext_mb_source(struct wcd_mbhc *wcd_mbhc, + bool turn_on) +{ + int ret = 0; + static int count; + struct snd_soc_codec *codec = wcd_mbhc->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on, + count); + if (turn_on) { + if (!count) { + ret = snd_soc_dapm_force_enable_pin(dapm, + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(dapm); + } + count++; + } else { + if (count > 0) + count--; + if (!count) { + ret = snd_soc_dapm_disable_pin(dapm, + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(dapm); + } + } + + if (ret) + dev_err(codec->dev, "%s: Failed to %s external micbias source\n", + __func__, turn_on ? "enable" : "disabled"); + else + dev_dbg(codec->dev, "%s: %s external micbias source\n", + __func__, turn_on ? "Enabled" : "Disabled"); + + return ret; +} + +static int msm_anlg_cdc_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + u16 micb_int_reg; + char *internal1_text = "Internal1"; + char *internal2_text = "Internal2"; + char *internal3_text = "Internal3"; + char *external2_text = "External2"; + char *external_text = "External"; + bool micbias2; + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + switch (w->reg) { + case MSM89XX_PMIC_ANALOG_MICB_1_EN: + case MSM89XX_PMIC_ANALOG_MICB_2_EN: + micb_int_reg = MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS; + break; + default: + dev_err(codec->dev, + "%s: Error, invalid micbias register 0x%x\n", + __func__, w->reg); + return -EINVAL; + } + + micbias2 = (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_MICB_2_EN) & 0x80); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (strnstr(w->name, internal1_text, strlen(w->name))) { + if (get_codec_version(sdm660_cdc) >= CAJON) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, + 0x02, 0x02); + snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x80); + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { + snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x10); + snd_soc_update_bits(codec, w->reg, 0x60, 0x00); + } else if (strnstr(w->name, internal3_text, strlen(w->name))) { + snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x2); + /* + * update MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2 + * for external bias only, not for external2. + */ + } else if (!strnstr(w->name, external2_text, strlen(w->name)) && + strnstr(w->name, external_text, + strlen(w->name))) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, + 0x02, 0x02); + } + if (!strnstr(w->name, external_text, strlen(w->name))) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x05, 0x04); + if (w->reg == MSM89XX_PMIC_ANALOG_MICB_1_EN) + msm_anlg_cdc_configure_cap(codec, true, micbias2); + + break; + case SND_SOC_DAPM_POST_PMU: + if (get_codec_version(sdm660_cdc) <= TOMBAK_2_0) + /* + * Wait for 20ms post micbias enable + * for version < tombak 2.0. + */ + usleep_range(20000, 20100); + if (strnstr(w->name, internal1_text, strlen(w->name))) { + snd_soc_update_bits(codec, micb_int_reg, 0x40, 0x40); + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { + snd_soc_update_bits(codec, micb_int_reg, 0x08, 0x08); + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_ON); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0x01, 0x01); + } else if (strnstr(w->name, external2_text, strlen(w->name))) { + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_ON); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (strnstr(w->name, internal1_text, strlen(w->name))) { + snd_soc_update_bits(codec, micb_int_reg, 0xC0, 0x40); + } else if (strnstr(w->name, internal2_text, strlen(w->name))) { + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_OFF); + } else if (strnstr(w->name, internal3_text, 30)) { + snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0); + } else if (strnstr(w->name, external2_text, strlen(w->name))) { + /* + * send micbias turn off event to mbhc driver and then + * break, as no need to set MICB_1_EN register. + */ + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_MICBIAS_2_OFF); + break; + } + if (w->reg == MSM89XX_PMIC_ANALOG_MICB_1_EN) + msm_anlg_cdc_configure_cap(codec, false, micbias2); + break; + } + return 0; +} + +static void update_clkdiv(void *handle, int val) +{ + struct sdm660_cdc_priv *handle_cdc = handle; + struct snd_soc_codec *codec = handle_cdc->codec; + + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, + 0xFF, val); +} + +static int get_cdc_version(void *handle) +{ + struct sdm660_cdc_priv *sdm660_cdc = handle; + + return get_codec_version(sdm660_cdc); +} + +static int sdm660_wcd_codec_enable_vdd_spkr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (!sdm660_cdc->ext_spk_boost_set) { + dev_dbg(codec->dev, "%s: ext_boost not supported/disabled\n", + __func__); + return 0; + } + dev_dbg(codec->dev, "%s: %s %d\n", __func__, w->name, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (sdm660_cdc->spkdrv_reg) { + ret = regulator_enable(sdm660_cdc->spkdrv_reg); + if (ret) + dev_err(codec->dev, + "%s Failed to enable spkdrv reg %s\n", + __func__, MSM89XX_VDD_SPKDRV_NAME); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (sdm660_cdc->spkdrv_reg) { + ret = regulator_disable(sdm660_cdc->spkdrv_reg); + if (ret) + dev_err(codec->dev, + "%s: Failed to disable spkdrv_reg %s\n", + __func__, MSM89XX_VDD_SPKDRV_NAME); + } + break; + } + return 0; +} + + +/* The register address is the same as other codec so it can use resmgr */ +static int msm_anlg_cdc_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + sdm660_cdc->rx_bias_count++; + if (sdm660_cdc->rx_bias_count == 1) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x01, 0x01); + } + break; + case SND_SOC_DAPM_POST_PMD: + sdm660_cdc->rx_bias_count--; + if (sdm660_cdc->rx_bias_count == 0) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x01, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x80, 0x00); + } + break; + } + dev_dbg(codec->dev, "%s rx_bias_count = %d\n", + __func__, sdm660_cdc->rx_bias_count); + return 0; +} + +static uint32_t wcd_get_impedance_value(uint32_t imped) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wcd_imped_val) - 1; i++) { + if (imped >= wcd_imped_val[i] && + imped < wcd_imped_val[i + 1]) + break; + } + + pr_debug("%s: selected impedance value = %d\n", + __func__, wcd_imped_val[i]); + return wcd_imped_val[i]; +} + +static void wcd_imped_config(struct snd_soc_codec *codec, + uint32_t imped, bool set_gain) +{ + uint32_t value; + int codec_version; + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + value = wcd_get_impedance_value(imped); + + if (value < wcd_imped_val[0]) { + dev_dbg(codec->dev, + "%s, detected impedance is less than 4 Ohm\n", + __func__); + return; + } + + codec_version = get_codec_version(sdm660_cdc); + + if (set_gain) { + switch (codec_version) { + case TOMBAK_1_0: + case TOMBAK_2_0: + case CONGA: + /* + * For 32Ohm load and higher loads, Set 0x19E + * bit 5 to 1 (POS_0_DB_DI). For loads lower + * than 32Ohm (such as 16Ohm load), Set 0x19E + * bit 5 to 0 (POS_M4P5_DB_DI) + */ + if (value >= 32) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x20); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x00); + break; + case CAJON: + case CAJON_2_0: + case DIANGU: + case DRAX_CDC: + if (value >= 13) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_NCP_VCTRL, + 0x07, 0x07); + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_NCP_VCTRL, + 0x07, 0x04); + } + break; + } + } else { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x20, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_NCP_VCTRL, + 0x07, 0x04); + } + + dev_dbg(codec->dev, "%s: Exit\n", __func__); +} + +static int msm_anlg_cdc_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + uint32_t impedl, impedr; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + int ret; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + ret = wcd_mbhc_get_impedance(&sdm660_cdc->mbhc, + &impedl, &impedr); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (get_codec_version(sdm660_cdc) > CAJON) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 0x08, 0x08); + if (get_codec_version(sdm660_cdc) == CAJON || + get_codec_version(sdm660_cdc) == CAJON_2_0) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, + 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, + 0x80, 0x80); + } + if (get_codec_version(sdm660_cdc) > CAJON) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 0x08, 0x00); + if (sdm660_cdc->hph_mode == HD2_MODE) + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_PRE_RX1_INT_ON); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x02); + if (!ret) + wcd_imped_config(codec, impedl, true); + else + dev_dbg(codec->dev, "Failed to get mbhc impedance %d\n", + ret); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_imped_config(codec, impedl, false); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x00); + if (sdm660_cdc->hph_mode == HD2_MODE) + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_POST_RX1_INT_OFF); + break; + } + return 0; +} + +static int msm_anlg_cdc_lo_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x08); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x40, 0x40); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x40, 0x40); + break; + case SND_SOC_DAPM_POST_PMD: + /* Wait for 20ms before powerdown of lineout_dac */ + usleep_range(20000, 20100); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x80, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x40, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x80, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x40, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x00); + break; + } + return 0; +} + +static int msm_anlg_cdc_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (sdm660_cdc->hph_mode == HD2_MODE) + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_PRE_RX2_INT_ON); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x02); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x01); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x00); + if (sdm660_cdc->hph_mode == HD2_MODE) + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_POST_RX2_INT_OFF); + break; + } + return 0; +} + +static int msm_anlg_cdc_hph_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: %s event = %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (w->shift == 5) + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_PRE_HPHL_PA_ON); + else if (w->shift == 4) + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_PRE_HPHR_PA_ON); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x20, 0x20); + break; + + case SND_SOC_DAPM_POST_PMU: + /* Wait for 7ms to allow setting time for HPH_PA Enable */ + usleep_range(7000, 7100); + if (w->shift == 5) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x04); + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX1_MUTE_OFF); + } else if (w->shift == 4) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x04); + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX2_MUTE_OFF); + } + break; + + case SND_SOC_DAPM_PRE_PMD: + if (w->shift == 5) { + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX1_MUTE_ON); + /* Wait for 20ms after HPHL RX digital mute */ + msleep(20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x00); + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_PRE_HPHL_PA_OFF); + } else if (w->shift == 4) { + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX2_MUTE_ON); + /* Wait for 20ms after HPHR RX digital mute */ + msleep(20); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x00); + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_PRE_HPHR_PA_OFF); + } + if (get_codec_version(sdm660_cdc) >= CAJON) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, + 0xF0, 0x30); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (w->shift == 5) { + clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, + &sdm660_cdc->mbhc.hph_pa_dac_state); + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_HPHL_PA_OFF); + } else if (w->shift == 4) { + clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, + &sdm660_cdc->mbhc.hph_pa_dac_state); + msm_anlg_cdc_notifier_call(codec, + WCD_EVENT_POST_HPHR_PA_OFF); + } + /* Wait for 15ms after HPH RX teardown */ + usleep_range(15000, 15100); + break; + } + return 0; +} + +static const struct snd_soc_dapm_route audio_map[] = { + /* RDAC Connections */ + {"HPHR DAC", NULL, "RDAC2 MUX"}, + {"RDAC2 MUX", "RX1", "PDM_IN_RX1"}, + {"RDAC2 MUX", "RX2", "PDM_IN_RX2"}, + + /* WSA */ + {"WSA_SPK OUT", NULL, "WSA Spk Switch"}, + {"WSA Spk Switch", "WSA", "EAR PA"}, + + /* Earpiece (RX MIX1) */ + {"EAR", NULL, "EAR_S"}, + {"EAR_S", "Switch", "EAR PA"}, + {"EAR PA", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "HPHL DAC"}, + {"EAR PA", NULL, "HPHR DAC"}, + {"EAR PA", NULL, "EAR CP"}, + + /* Headset (RX MIX1 and RX MIX2) */ + {"HEADPHONE", NULL, "HPHL PA"}, + {"HEADPHONE", NULL, "HPHR PA"}, + + {"Ext Spk", NULL, "Ext Spk Switch"}, + {"Ext Spk Switch", "On", "HPHL PA"}, + {"Ext Spk Switch", "On", "HPHR PA"}, + + {"HPHL PA", NULL, "HPHL"}, + {"HPHR PA", NULL, "HPHR"}, + {"HPHL", "Switch", "HPHL DAC"}, + {"HPHR", "Switch", "HPHR DAC"}, + {"HPHL PA", NULL, "CP"}, + {"HPHL PA", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "CP"}, + {"HPHR PA", NULL, "RX_BIAS"}, + {"HPHL DAC", NULL, "PDM_IN_RX1"}, + + {"SPK_OUT", NULL, "SPK PA"}, + {"SPK PA", NULL, "SPK_RX_BIAS"}, + {"SPK PA", NULL, "SPK"}, + {"SPK", "Switch", "SPK DAC"}, + {"SPK DAC", NULL, "PDM_IN_RX3"}, + {"SPK DAC", NULL, "VDD_SPKDRV"}, + + /* lineout */ + {"LINEOUT", NULL, "LINEOUT PA"}, + {"LINEOUT PA", NULL, "SPK_RX_BIAS"}, + {"LINEOUT PA", NULL, "LINE_OUT"}, + {"LINE_OUT", "Switch", "LINEOUT DAC"}, + {"LINEOUT DAC", NULL, "PDM_IN_RX3"}, + + /* lineout to WSA */ + {"WSA_SPK OUT", NULL, "LINEOUT PA"}, + + {"PDM_IN_RX1", NULL, "RX1 CLK"}, + {"PDM_IN_RX2", NULL, "RX2 CLK"}, + {"PDM_IN_RX3", NULL, "RX3 CLK"}, + + {"ADC1_OUT", NULL, "ADC1"}, + {"ADC2_OUT", NULL, "ADC2"}, + {"ADC3_OUT", NULL, "ADC3"}, + + /* ADC Connections */ + {"ADC2", NULL, "ADC2 MUX"}, + {"ADC3", NULL, "ADC2 MUX"}, + {"ADC2 MUX", "INP2", "ADC2_INP2"}, + {"ADC2 MUX", "INP3", "ADC2_INP3"}, + + {"ADC1", NULL, "AMIC1"}, + {"ADC2_INP2", NULL, "AMIC2"}, + {"ADC2_INP3", NULL, "AMIC3"}, + + {"MIC BIAS Internal1", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal2", NULL, "INT_LDO_H"}, + {"MIC BIAS External", NULL, "INT_LDO_H"}, + {"MIC BIAS External2", NULL, "INT_LDO_H"}, + {"MIC BIAS Internal1", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS Internal2", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS External", NULL, "MICBIAS_REGULATOR"}, + {"MIC BIAS External2", NULL, "MICBIAS_REGULATOR"}, +}; + +static int msm_anlg_cdc_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(dai->codec); + + dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n", + __func__, + substream->name, substream->stream); + /* + * If status_mask is BUS_DOWN it means SSR is not complete. + * So return error. + */ + if (test_bit(BUS_DOWN, &sdm660_cdc->status_mask)) { + dev_err(dai->codec->dev, "Error, Device is not up post SSR\n"); + return -EINVAL; + } + return 0; +} + +static void msm_anlg_cdc_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + dev_dbg(dai->codec->dev, + "%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); +} + +int msm_anlg_cdc_mclk_enable(struct snd_soc_codec *codec, + int mclk_enable, bool dapm) +{ + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: mclk_enable = %u, dapm = %d\n", + __func__, mclk_enable, dapm); + if (mclk_enable) { + sdm660_cdc->int_mclk0_enabled = true; + msm_anlg_cdc_codec_enable_clock_block(codec, 1); + } else { + if (!sdm660_cdc->int_mclk0_enabled) { + dev_err(codec->dev, "Error, MCLK already diabled\n"); + return -EINVAL; + } + sdm660_cdc->int_mclk0_enabled = false; + msm_anlg_cdc_codec_enable_clock_block(codec, 0); + } + return 0; +} + +static int msm_anlg_cdc_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + dev_dbg(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static int msm_anlg_cdc_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + dev_dbg(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static int msm_anlg_cdc_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_slot, + unsigned int rx_num, unsigned int *rx_slot) + +{ + dev_dbg(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static int msm_anlg_cdc_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) + +{ + dev_dbg(dai->codec->dev, "%s\n", __func__); + return 0; +} + +static struct snd_soc_dai_ops msm_anlg_cdc_dai_ops = { + .startup = msm_anlg_cdc_startup, + .shutdown = msm_anlg_cdc_shutdown, + .set_sysclk = msm_anlg_cdc_set_dai_sysclk, + .set_fmt = msm_anlg_cdc_set_dai_fmt, + .set_channel_map = msm_anlg_cdc_set_channel_map, + .get_channel_map = msm_anlg_cdc_get_channel_map, +}; + +static struct snd_soc_dai_driver msm_anlg_cdc_i2s_dai[] = { + { + .name = "msm_anlg_cdc_i2s_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "PDM Playback", + .rates = SDM660_CDC_RATES, + .formats = SDM660_CDC_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 3, + }, + .ops = &msm_anlg_cdc_dai_ops, + }, + { + .name = "msm_anlg_cdc_i2s_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "PDM Capture", + .rates = SDM660_CDC_RATES, + .formats = SDM660_CDC_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &msm_anlg_cdc_dai_ops, + }, + { + .name = "msm_anlg_cdc_i2s_tx2", + .id = AIF3_SVA, + .capture = { + .stream_name = "RecordSVA", + .rates = SDM660_CDC_RATES, + .formats = SDM660_CDC_FORMATS, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &msm_anlg_cdc_dai_ops, + }, + { + .name = "msm_anlg_vifeedback", + .id = AIF2_VIFEED, + .capture = { + .stream_name = "VIfeed", + .rates = SDM660_CDC_RATES, + .formats = SDM660_CDC_FORMATS, + .rate_max = 48000, + .rate_min = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &msm_anlg_cdc_dai_ops, + }, +}; + + +static int msm_anlg_cdc_codec_enable_lo_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: %d %s\n", __func__, event, w->name); + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX3_MUTE_OFF); + break; + case SND_SOC_DAPM_POST_PMD: + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX3_MUTE_ON); + break; + } + + return 0; +} + +static int msm_anlg_cdc_codec_enable_spk_ext_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: %s event = %d\n", __func__, w->name, event); + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dev_dbg(codec->dev, + "%s: enable external speaker PA\n", __func__); + if (sdm660_cdc->codec_spk_ext_pa_cb) + sdm660_cdc->codec_spk_ext_pa_cb(codec, 1); + break; + case SND_SOC_DAPM_PRE_PMD: + dev_dbg(codec->dev, + "%s: enable external speaker PA\n", __func__); + if (sdm660_cdc->codec_spk_ext_pa_cb) + sdm660_cdc->codec_spk_ext_pa_cb(codec, 0); + break; + } + return 0; +} + +static int msm_anlg_cdc_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(codec->dev, + "%s: Sleeping 20ms after select EAR PA\n", + __func__); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x80, 0x80); + if (get_codec_version(sdm660_cdc) < CONGA) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFF, 0x2A); + if (get_codec_version(sdm660_cdc) >= DIANGU) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x08, 0x00); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x04); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x04); + } + break; + case SND_SOC_DAPM_POST_PMU: + dev_dbg(codec->dev, + "%s: Sleeping 20ms after enabling EAR PA\n", + __func__); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x40, 0x40); + /* Wait for 7ms after EAR PA enable */ + usleep_range(7000, 7100); + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX1_MUTE_OFF); + break; + case SND_SOC_DAPM_PRE_PMD: + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_RX1_MUTE_ON); + /* Wait for 20ms for RX digital mute to take effect */ + msleep(20); + if (sdm660_cdc->boost_option == BOOST_ALWAYS) { + dev_dbg(codec->dev, + "%s: boost_option:%d, tear down ear\n", + __func__, sdm660_cdc->boost_option); + msm_anlg_cdc_boost_mode_sequence(codec, EAR_PMD); + } + if (get_codec_version(sdm660_cdc) >= DIANGU) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x0); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x0); + } + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(codec->dev, + "%s: Sleeping 7ms after disabling EAR PA\n", + __func__); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0x40, 0x00); + /* Wait for 7ms after EAR PA teardown */ + usleep_range(7000, 7100); + if (get_codec_version(sdm660_cdc) < CONGA) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFF, 0x16); + if (get_codec_version(sdm660_cdc) >= DIANGU) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x08, 0x08); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget msm_anlg_cdc_dapm_widgets[] = { + SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM, + 0, 0, NULL, 0, msm_anlg_cdc_codec_enable_ear_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PA", MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 5, 0, NULL, 0, + msm_anlg_cdc_hph_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PA", MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 4, 0, NULL, 0, + msm_anlg_cdc_hph_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, + 0, 0, NULL, 0, msm_anlg_cdc_codec_enable_spk_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT PA", MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, + 5, 0, NULL, 0, msm_anlg_cdc_codec_enable_lo_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("EAR_S", SND_SOC_NOPM, 0, 0, ear_pa_mux), + SND_SOC_DAPM_MUX("SPK", SND_SOC_NOPM, 0, 0, spkr_mux), + SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0, hphl_mux), + SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0, hphr_mux), + SND_SOC_DAPM_MUX("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux), + SND_SOC_DAPM_MUX("WSA Spk Switch", SND_SOC_NOPM, 0, 0, wsa_spk_mux), + SND_SOC_DAPM_MUX("Ext Spk Switch", SND_SOC_NOPM, 0, 0, &ext_spk_mux), + SND_SOC_DAPM_MUX("LINE_OUT", SND_SOC_NOPM, 0, 0, lo_mux), + SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0, &tx_adc2_mux), + + SND_SOC_DAPM_MIXER_E("HPHL DAC", + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL, + 0, msm_anlg_cdc_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("HPHR DAC", + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL, + 0, msm_anlg_cdc_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_DAC("SPK DAC", NULL, MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, + 7, 0), + SND_SOC_DAPM_DAC_E("LINEOUT DAC", NULL, + SND_SOC_NOPM, 0, 0, msm_anlg_cdc_lo_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Ext Spk", msm_anlg_cdc_codec_enable_spk_ext_pa), + + SND_SOC_DAPM_SUPPLY("RX1 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RX2 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RX3 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 2, 0, msm_anlg_cdc_codec_enable_dig_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("CP", MSM89XX_PMIC_ANALOG_NCP_EN, 0, 0, + msm_anlg_cdc_codec_enable_charge_pump, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("EAR CP", MSM89XX_PMIC_ANALOG_NCP_EN, 4, 0, + msm_anlg_cdc_codec_enable_charge_pump, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("RX_BIAS", 1, SND_SOC_NOPM, + 0, 0, msm_anlg_cdc_codec_enable_rx_bias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("SPK_RX_BIAS", 1, SND_SOC_NOPM, 0, 0, + msm_anlg_cdc_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VDD_SPKDRV", SND_SOC_NOPM, 0, 0, + sdm660_wcd_codec_enable_vdd_spkr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM, + ON_DEMAND_MICBIAS, 0, + msm_anlg_cdc_codec_enable_on_demand_supply, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal1", + MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0, + msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal2", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 7, 0, + msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal3", + MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0, + msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, MSM89XX_PMIC_ANALOG_TX_1_EN, 7, 0, + msm_anlg_cdc_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP2", + NULL, MSM89XX_PMIC_ANALOG_TX_2_EN, 7, 0, + msm_anlg_cdc_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADC2_INP3", + NULL, MSM89XX_PMIC_ANALOG_TX_3_EN, 7, 0, + msm_anlg_cdc_codec_enable_adc, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E("MIC BIAS External", + MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0, + msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS External2", + MSM89XX_PMIC_ANALOG_MICB_2_EN, 7, 0, + msm_anlg_cdc_codec_enable_micbias, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_AIF_IN("PDM_IN_RX1", "PDM Playback", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("PDM_IN_RX2", "PDM Playback", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("PDM_IN_RX3", "PDM Playback", + 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("WSA_SPK OUT"), + SND_SOC_DAPM_OUTPUT("HEADPHONE"), + SND_SOC_DAPM_OUTPUT("SPK_OUT"), + SND_SOC_DAPM_OUTPUT("LINEOUT"), + SND_SOC_DAPM_AIF_OUT("ADC1_OUT", "PDM Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("ADC2_OUT", "PDM Capture", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("ADC3_OUT", "PDM Capture", + 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct sdm660_cdc_reg_mask_val msm_anlg_cdc_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), +}; + +static const struct sdm660_cdc_reg_mask_val + msm_anlg_cdc_reg_defaults_2_0[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x28), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x5F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x88), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static const struct sdm660_cdc_reg_mask_val conga_wcd_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x28), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x0A), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static const struct sdm660_cdc_reg_mask_val cajon_wcd_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0xA8), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0xA4), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x41), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0xFA), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static const struct sdm660_cdc_reg_mask_val cajon2p0_wcd_reg_defaults[] = { + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0xA2), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0xA8), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0xA4), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x41), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x10), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x18), + MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0xFA), + MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80), +}; + +static void msm_anlg_cdc_update_reg_defaults(struct snd_soc_codec *codec) +{ + u32 i, version; + struct sdm660_cdc_priv *sdm660_cdc = + snd_soc_codec_get_drvdata(codec); + + version = get_codec_version(sdm660_cdc); + if (version == TOMBAK_1_0) { + for (i = 0; i < ARRAY_SIZE(msm_anlg_cdc_reg_defaults); i++) + snd_soc_write(codec, msm_anlg_cdc_reg_defaults[i].reg, + msm_anlg_cdc_reg_defaults[i].val); + } else if (version == TOMBAK_2_0) { + for (i = 0; i < ARRAY_SIZE(msm_anlg_cdc_reg_defaults_2_0); i++) + snd_soc_write(codec, + msm_anlg_cdc_reg_defaults_2_0[i].reg, + msm_anlg_cdc_reg_defaults_2_0[i].val); + } else if (version == CONGA) { + for (i = 0; i < ARRAY_SIZE(conga_wcd_reg_defaults); i++) + snd_soc_write(codec, + conga_wcd_reg_defaults[i].reg, + conga_wcd_reg_defaults[i].val); + } else if (version == CAJON) { + for (i = 0; i < ARRAY_SIZE(cajon_wcd_reg_defaults); i++) + snd_soc_write(codec, + cajon_wcd_reg_defaults[i].reg, + cajon_wcd_reg_defaults[i].val); + } else if (version == CAJON_2_0 || version == DIANGU + || version == DRAX_CDC) { + for (i = 0; i < ARRAY_SIZE(cajon2p0_wcd_reg_defaults); i++) + snd_soc_write(codec, + cajon2p0_wcd_reg_defaults[i].reg, + cajon2p0_wcd_reg_defaults[i].val); + } +} + +static const struct sdm660_cdc_reg_mask_val + msm_anlg_cdc_codec_reg_init_val[] = { + + /* Initialize current threshold to 350MA + * number of wait and run cycles to 4096 + */ + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xFF, 0x12}, + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0xFF, 0xFF}, +}; + +static void msm_anlg_cdc_codec_init_cache(struct snd_soc_codec *codec) +{ + u32 i; + + regcache_cache_only(codec->component.regmap, true); + /* update cache with POR values */ + for (i = 0; i < ARRAY_SIZE(msm89xx_pmic_cdc_defaults); i++) + snd_soc_write(codec, msm89xx_pmic_cdc_defaults[i].reg, + msm89xx_pmic_cdc_defaults[i].def); + regcache_cache_only(codec->component.regmap, false); +} + +static void msm_anlg_cdc_codec_init_reg(struct snd_soc_codec *codec) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(msm_anlg_cdc_codec_reg_init_val); i++) + snd_soc_update_bits(codec, + msm_anlg_cdc_codec_reg_init_val[i].reg, + msm_anlg_cdc_codec_reg_init_val[i].mask, + msm_anlg_cdc_codec_reg_init_val[i].val); +} + +static int msm_anlg_cdc_bringup(struct snd_soc_codec *codec) +{ + snd_soc_write(codec, + MSM89XX_PMIC_DIGITAL_SEC_ACCESS, + 0xA5); + snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x01); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x01); + snd_soc_write(codec, + MSM89XX_PMIC_DIGITAL_SEC_ACCESS, + 0xA5); + snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_SEC_ACCESS, + 0xA5); + snd_soc_write(codec, MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00); + + return 0; +} + +static struct regulator *msm_anlg_cdc_find_regulator( + const struct sdm660_cdc_priv *sdm660_cdc, + const char *name) +{ + int i; + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (sdm660_cdc->supplies[i].supply && + !strcmp(sdm660_cdc->supplies[i].supply, name)) + return sdm660_cdc->supplies[i].consumer; + } + + dev_err(sdm660_cdc->dev, "Error: regulator not found:%s\n" + , name); + return NULL; +} + +static void msm_anlg_cdc_update_micbias_regulator( + const struct sdm660_cdc_priv *sdm660_cdc, + const char *name, + struct on_demand_supply *micbias_supply) +{ + int i; + struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data; + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (sdm660_cdc->supplies[i].supply && + !strcmp(sdm660_cdc->supplies[i].supply, name)) { + micbias_supply->supply = + sdm660_cdc->supplies[i].consumer; + micbias_supply->min_uv = pdata->regulator[i].min_uv; + micbias_supply->max_uv = pdata->regulator[i].max_uv; + micbias_supply->optimum_ua = + pdata->regulator[i].optimum_ua; + return; + } + } + + dev_err(sdm660_cdc->dev, "Error: regulator not found:%s\n", name); +} + +static int msm_anlg_cdc_device_down(struct snd_soc_codec *codec) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + unsigned int tx_1_en; + unsigned int tx_2_en; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + dev_dbg(codec->dev, "%s: device down!\n", __func__); + + tx_1_en = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_TX_1_EN); + tx_2_en = snd_soc_read(codec, MSM89XX_PMIC_ANALOG_TX_2_EN); + tx_1_en = tx_1_en & 0x7f; + tx_2_en = tx_2_en & 0x7f; + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_TX_1_EN, tx_1_en); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_TX_2_EN, tx_2_en); + if (sdm660_cdc_priv->boost_option == BOOST_ON_FOREVER) { + if ((snd_soc_read(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL) + & 0x80) == 0) { + msm_anlg_cdc_dig_notifier_call(codec, + DIG_CDC_EVENT_CLK_ON); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, + 0x0C, 0x0C); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, + 0x84, 0x84); + snd_soc_update_bits(codec, + MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, + 0x1F, 0x1F); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x90, 0x90); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, + 0xFF, 0xFF); + /* Wait for 20us for boost settings to take effect */ + usleep_range(20, 21); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, + 0xFF, 0xFF); + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0xE9, 0xE9); + } + } + msm_anlg_cdc_boost_off(codec); + sdm660_cdc_priv->hph_mode = NORMAL_MODE; + + /* 40ms to allow boost to discharge */ + msleep(40); + /* Disable PA to avoid pop during codec bring up */ + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, + 0x30, 0x00); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, + 0x80, 0x00); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12); + snd_soc_write(codec, + MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x93); + + msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_SSR_DOWN); + atomic_set(&pdata->int_mclk0_enabled, false); + set_bit(BUS_DOWN, &sdm660_cdc_priv->status_mask); + snd_soc_card_change_online_state(codec->component.card, 0); + + return 0; +} + +static int msm_anlg_cdc_device_up(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: device up!\n", __func__); + + msm_anlg_cdc_dig_notifier_call(codec, DIG_CDC_EVENT_SSR_UP); + clear_bit(BUS_DOWN, &sdm660_cdc_priv->status_mask); + snd_soc_card_change_online_state(codec->component.card, 1); + /* delay is required to make sure sound card state updated */ + usleep_range(5000, 5100); + + snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_INT_EN_SET, + MSM89XX_PMIC_DIGITAL_INT_EN_SET__POR); + snd_soc_write(codec, MSM89XX_PMIC_DIGITAL_INT_EN_CLR, + MSM89XX_PMIC_DIGITAL_INT_EN_CLR__POR); + + msm_anlg_cdc_set_boost_v(codec); + msm_anlg_cdc_set_micb_v(codec); + if (sdm660_cdc_priv->boost_option == BOOST_ON_FOREVER) + msm_anlg_cdc_boost_on(codec); + else if (sdm660_cdc_priv->boost_option == BYPASS_ALWAYS) + msm_anlg_cdc_bypass_on(codec); + + return 0; +} + +static int sdm660_cdc_notifier_service_cb(struct notifier_block *nb, + unsigned long opcode, void *ptr) +{ + struct snd_soc_codec *codec; + struct sdm660_cdc_priv *sdm660_cdc_priv = + container_of(nb, struct sdm660_cdc_priv, + audio_ssr_nb); + bool adsp_ready = false; + bool timedout; + unsigned long timeout; + static bool initial_boot = true; + + codec = sdm660_cdc_priv->codec; + dev_dbg(codec->dev, "%s: Service opcode 0x%lx\n", __func__, opcode); + + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + if (initial_boot) { + initial_boot = false; + break; + } + dev_dbg(codec->dev, + "ADSP is about to power down. teardown/reset codec\n"); + msm_anlg_cdc_device_down(codec); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (initial_boot) + initial_boot = false; + dev_dbg(codec->dev, + "ADSP is about to power up. bring up codec\n"); + + if (!q6core_is_adsp_ready()) { + dev_dbg(codec->dev, + "ADSP isn't ready\n"); + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + while (!(timedout = time_after(jiffies, timeout))) { + if (!q6core_is_adsp_ready()) { + dev_dbg(codec->dev, + "ADSP isn't ready\n"); + } else { + dev_dbg(codec->dev, + "ADSP is ready\n"); + adsp_ready = true; + goto powerup; + } + } + } else { + adsp_ready = true; + dev_dbg(codec->dev, "%s: DSP is ready\n", __func__); + } +powerup: + if (adsp_ready) + msm_anlg_cdc_device_up(codec); + break; + default: + break; + } + return NOTIFY_OK; +} + +int msm_anlg_cdc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + + return wcd_mbhc_start(&sdm660_cdc_priv->mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(msm_anlg_cdc_hs_detect); + +void msm_anlg_cdc_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + + wcd_mbhc_stop(&sdm660_cdc_priv->mbhc); +} +EXPORT_SYMBOL(msm_anlg_cdc_hs_detect_exit); + +void msm_anlg_cdc_update_int_spk_boost(bool enable) +{ + pr_debug("%s: enable = %d\n", __func__, enable); + spkr_boost_en = enable; +} +EXPORT_SYMBOL(msm_anlg_cdc_update_int_spk_boost); + +static void msm_anlg_cdc_set_micb_v(struct snd_soc_codec *codec) +{ + + struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec); + struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data; + u8 reg_val; + + reg_val = VOLTAGE_CONVERTER(pdata->micbias.cfilt1_mv, MICBIAS_MIN_VAL, + MICBIAS_STEP_SIZE); + dev_dbg(codec->dev, "cfilt1_mv %d reg_val %x\n", + (u32)pdata->micbias.cfilt1_mv, reg_val); + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_VAL, + 0xF8, (reg_val << 3)); +} + +static void msm_anlg_cdc_set_boost_v(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + snd_soc_codec_get_drvdata(codec); + + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE, + 0x1F, sdm660_cdc_priv->boost_voltage); +} + +static void msm_anlg_cdc_configure_cap(struct snd_soc_codec *codec, + bool micbias1, bool micbias2) +{ + + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + + pr_debug("\n %s: micbias1 %x micbias2 = %d\n", __func__, micbias1, + micbias2); + if (micbias1 && micbias2) { + if ((pdata->micbias1_cap_mode + == MICBIAS_EXT_BYP_CAP) || + (pdata->micbias2_cap_mode + == MICBIAS_EXT_BYP_CAP)) + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (MICBIAS_EXT_BYP_CAP << 6)); + else + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (MICBIAS_NO_EXT_BYP_CAP << 6)); + } else if (micbias2) { + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (pdata->micbias2_cap_mode << 6)); + } else if (micbias1) { + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, (pdata->micbias1_cap_mode << 6)); + } else { + snd_soc_update_bits(codec, MSM89XX_PMIC_ANALOG_MICB_1_EN, + 0x40, 0x00); + } +} + +static ssize_t msm_anlg_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv; + char buffer[MSM_ANLG_CDC_VERSION_ENTRY_SIZE]; + int len = 0; + + sdm660_cdc_priv = (struct sdm660_cdc_priv *) entry->private_data; + if (!sdm660_cdc_priv) { + pr_err("%s: sdm660_cdc_priv is null\n", __func__); + return -EINVAL; + } + + switch (get_codec_version(sdm660_cdc_priv)) { + case DRAX_CDC: + len = snprintf(buffer, sizeof(buffer), "DRAX-CDC_1_0\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops msm_anlg_codec_info_ops = { + .read = msm_anlg_codec_version_read, +}; + +/* + * msm_anlg_codec_info_create_codec_entry - creates pmic_analog module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates pmic_analog module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int msm_anlg_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct sdm660_cdc_priv *sdm660_cdc_priv; + struct snd_soc_card *card; + int ret; + + if (!codec_root || !codec) + return -EINVAL; + + sdm660_cdc_priv = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + sdm660_cdc_priv->entry = snd_register_module_info(codec_root->module, + "spmi0-03", + codec_root); + if (!sdm660_cdc_priv->entry) { + dev_dbg(codec->dev, "%s: failed to create pmic_analog entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + sdm660_cdc_priv->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create pmic_analog version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = sdm660_cdc_priv; + version_entry->size = MSM_ANLG_CDC_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &msm_anlg_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + sdm660_cdc_priv->version_entry = version_entry; + + sdm660_cdc_priv->audio_ssr_nb.notifier_call = + sdm660_cdc_notifier_service_cb; + ret = audio_notifier_register("pmic_analog_cdc", + AUDIO_NOTIFIER_ADSP_DOMAIN, + &sdm660_cdc_priv->audio_ssr_nb); + if (ret < 0) { + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + return ret; + } + return 0; +} +EXPORT_SYMBOL(msm_anlg_codec_info_create_codec_entry); + +static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int ret; + + sdm660_cdc = dev_get_drvdata(codec->dev); + sdm660_cdc->codec = codec; + + /* codec resmgr module init */ + sdm660_cdc->spkdrv_reg = + msm_anlg_cdc_find_regulator(sdm660_cdc, + MSM89XX_VDD_SPKDRV_NAME); + sdm660_cdc->pmic_rev = + snd_soc_read(codec, + MSM89XX_PMIC_DIGITAL_REVISION1); + sdm660_cdc->codec_version = + snd_soc_read(codec, + MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE); + sdm660_cdc->analog_major_rev = + snd_soc_read(codec, + MSM89XX_PMIC_ANALOG_REVISION4); + + if (sdm660_cdc->codec_version == CONGA) { + dev_dbg(codec->dev, "%s :Conga REV: %d\n", __func__, + sdm660_cdc->codec_version); + sdm660_cdc->ext_spk_boost_set = true; + } else { + dev_dbg(codec->dev, "%s :PMIC REV: %d\n", __func__, + sdm660_cdc->pmic_rev); + if (sdm660_cdc->pmic_rev == TOMBAK_1_0 && + sdm660_cdc->codec_version == CAJON_2_0) { + if (sdm660_cdc->analog_major_rev == 0x02) { + sdm660_cdc->codec_version = DRAX_CDC; + dev_dbg(codec->dev, + "%s : Drax codec detected\n", __func__); + } else { + sdm660_cdc->codec_version = DIANGU; + dev_dbg(codec->dev, "%s : Diangu detected\n", + __func__); + } + } else if (sdm660_cdc->pmic_rev == TOMBAK_1_0 && + (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_NCP_FBCTRL) + & 0x80)) { + sdm660_cdc->codec_version = CAJON; + dev_dbg(codec->dev, "%s : Cajon detected\n", __func__); + } else if (sdm660_cdc->pmic_rev == TOMBAK_2_0 && + (snd_soc_read(codec, MSM89XX_PMIC_ANALOG_NCP_FBCTRL) + & 0x80)) { + sdm660_cdc->codec_version = CAJON_2_0; + dev_dbg(codec->dev, "%s : Cajon 2.0 detected\n", + __func__); + } + } + /* + * set to default boost option BOOST_SWITCH, user mixer path can change + * it to BOOST_ALWAYS or BOOST_BYPASS based on solution chosen. + */ + sdm660_cdc->boost_option = BOOST_SWITCH; + sdm660_cdc->hph_mode = NORMAL_MODE; + + msm_anlg_cdc_dt_parse_boost_info(codec); + msm_anlg_cdc_set_boost_v(codec); + + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + msm_anlg_cdc_bringup(codec); + msm_anlg_cdc_codec_init_cache(codec); + msm_anlg_cdc_codec_init_reg(codec); + msm_anlg_cdc_update_reg_defaults(codec); + + wcd9xxx_spmi_set_codec(codec); + + msm_anlg_cdc_update_micbias_regulator( + sdm660_cdc, + on_demand_supply_name[ON_DEMAND_MICBIAS], + &sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS]); + atomic_set(&sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS].ref, + 0); + + sdm660_cdc->fw_data = devm_kzalloc(codec->dev, + sizeof(*(sdm660_cdc->fw_data)), + GFP_KERNEL); + if (!sdm660_cdc->fw_data) + return -ENOMEM; + + set_bit(WCD9XXX_MBHC_CAL, sdm660_cdc->fw_data->cal_bit); + ret = wcd_cal_create_hwdep(sdm660_cdc->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + return ret; + } + + wcd_mbhc_init(&sdm660_cdc->mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, true); + + sdm660_cdc->int_mclk0_enabled = false; + /*Update speaker boost configuration*/ + sdm660_cdc->spk_boost_set = spkr_boost_en; + pr_debug("%s: speaker boost configured = %d\n", + __func__, sdm660_cdc->spk_boost_set); + + /* Set initial MICBIAS voltage level */ + msm_anlg_cdc_set_micb_v(codec); + + /* Set initial cap mode */ + msm_anlg_cdc_configure_cap(codec, false, false); + + snd_soc_dapm_ignore_suspend(dapm, "PDM Playback"); + snd_soc_dapm_ignore_suspend(dapm, "PDM Capture"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int msm_anlg_cdc_soc_remove(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc_priv = + dev_get_drvdata(codec->dev); + + sdm660_cdc_priv->spkdrv_reg = NULL; + sdm660_cdc_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL; + atomic_set(&sdm660_cdc_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, + 0); + wcd_mbhc_deinit(&sdm660_cdc_priv->mbhc); + + return 0; +} + +static int msm_anlg_cdc_enable_static_supplies_to_optimum( + struct sdm660_cdc_priv *sdm660_cdc, + struct sdm660_cdc_pdata *pdata) +{ + int i; + int ret = 0; + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (pdata->regulator[i].ondemand) + continue; + if (regulator_count_voltages( + sdm660_cdc->supplies[i].consumer) <= 0) + continue; + + ret = regulator_set_voltage( + sdm660_cdc->supplies[i].consumer, + pdata->regulator[i].min_uv, + pdata->regulator[i].max_uv); + if (ret) { + dev_err(sdm660_cdc->dev, + "Setting volt failed for regulator %s err %d\n", + sdm660_cdc->supplies[i].supply, ret); + } + + ret = regulator_set_load(sdm660_cdc->supplies[i].consumer, + pdata->regulator[i].optimum_ua); + dev_dbg(sdm660_cdc->dev, "Regulator %s set optimum mode\n", + sdm660_cdc->supplies[i].supply); + } + + return ret; +} + +static int msm_anlg_cdc_disable_static_supplies_to_optimum( + struct sdm660_cdc_priv *sdm660_cdc, + struct sdm660_cdc_pdata *pdata) +{ + int i; + int ret = 0; + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (pdata->regulator[i].ondemand) + continue; + if (regulator_count_voltages( + sdm660_cdc->supplies[i].consumer) <= 0) + continue; + regulator_set_voltage(sdm660_cdc->supplies[i].consumer, 0, + pdata->regulator[i].max_uv); + regulator_set_load(sdm660_cdc->supplies[i].consumer, 0); + dev_dbg(sdm660_cdc->dev, "Regulator %s set optimum mode\n", + sdm660_cdc->supplies[i].supply); + } + + return ret; +} + +static int msm_anlg_cdc_suspend(struct snd_soc_codec *codec) +{ + struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec); + struct sdm660_cdc_pdata *sdm660_cdc_pdata = + sdm660_cdc->dev->platform_data; + + msm_anlg_cdc_disable_static_supplies_to_optimum(sdm660_cdc, + sdm660_cdc_pdata); + return 0; +} + +static int msm_anlg_cdc_resume(struct snd_soc_codec *codec) +{ + struct msm_asoc_mach_data *pdata = NULL; + struct sdm660_cdc_priv *sdm660_cdc = snd_soc_codec_get_drvdata(codec); + struct sdm660_cdc_pdata *sdm660_cdc_pdata = + sdm660_cdc->dev->platform_data; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + msm_anlg_cdc_enable_static_supplies_to_optimum(sdm660_cdc, + sdm660_cdc_pdata); + return 0; +} + +static struct regmap *msm_anlg_get_regmap(struct device *dev) +{ + return dev_get_regmap(dev->parent, NULL); +} + +static struct snd_soc_codec_driver soc_codec_dev_sdm660_cdc = { + .probe = msm_anlg_cdc_soc_probe, + .remove = msm_anlg_cdc_soc_remove, + .suspend = msm_anlg_cdc_suspend, + .resume = msm_anlg_cdc_resume, + .reg_word_size = 1, + .controls = msm_anlg_cdc_snd_controls, + .num_controls = ARRAY_SIZE(msm_anlg_cdc_snd_controls), + .dapm_widgets = msm_anlg_cdc_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm_anlg_cdc_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + .get_regmap = msm_anlg_get_regmap, +}; + +static int msm_anlg_cdc_init_supplies(struct sdm660_cdc_priv *sdm660_cdc, + struct sdm660_cdc_pdata *pdata) +{ + int ret; + int i; + + sdm660_cdc->supplies = devm_kzalloc(sdm660_cdc->dev, + sizeof(struct regulator_bulk_data) * + ARRAY_SIZE(pdata->regulator), + GFP_KERNEL); + if (!sdm660_cdc->supplies) { + ret = -ENOMEM; + goto err; + } + + sdm660_cdc->num_of_supplies = 0; + if (ARRAY_SIZE(pdata->regulator) > MAX_REGULATOR) { + dev_err(sdm660_cdc->dev, "%s: Array Size out of bound\n", + __func__); + ret = -EINVAL; + goto err; + } + + for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) { + if (pdata->regulator[i].name) { + sdm660_cdc->supplies[i].supply = + pdata->regulator[i].name; + sdm660_cdc->num_of_supplies++; + } + } + + ret = devm_regulator_bulk_get(sdm660_cdc->dev, + sdm660_cdc->num_of_supplies, + sdm660_cdc->supplies); + if (ret != 0) { + dev_err(sdm660_cdc->dev, + "Failed to get supplies: err = %d\n", + ret); + goto err_supplies; + } + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (regulator_count_voltages( + sdm660_cdc->supplies[i].consumer) <= 0) + continue; + if (pdata->regulator[i].ondemand) { + ret = regulator_set_voltage( + sdm660_cdc->supplies[i].consumer, + 0, pdata->regulator[i].max_uv); + if (ret) { + dev_err(sdm660_cdc->dev, + "Setting regulator voltage failed for regulator %s err = %d\n", + sdm660_cdc->supplies[i].supply, ret); + goto err_supplies; + } + ret = regulator_set_load( + sdm660_cdc->supplies[i].consumer, 0); + if (ret < 0) { + dev_err(sdm660_cdc->dev, + "Setting regulator optimum mode failed for regulator %s err = %d\n", + sdm660_cdc->supplies[i].supply, ret); + goto err_supplies; + } else { + ret = 0; + continue; + } + } + ret = regulator_set_voltage(sdm660_cdc->supplies[i].consumer, + pdata->regulator[i].min_uv, + pdata->regulator[i].max_uv); + if (ret) { + dev_err(sdm660_cdc->dev, + "Setting regulator voltage failed for regulator %s err = %d\n", + sdm660_cdc->supplies[i].supply, ret); + goto err_supplies; + } + ret = regulator_set_load(sdm660_cdc->supplies[i].consumer, + pdata->regulator[i].optimum_ua); + if (ret < 0) { + dev_err(sdm660_cdc->dev, + "Setting regulator optimum mode failed for regulator %s err = %d\n", + sdm660_cdc->supplies[i].supply, ret); + goto err_supplies; + } else { + ret = 0; + } + } + + return ret; + +err_supplies: + kfree(sdm660_cdc->supplies); +err: + return ret; +} + +static int msm_anlg_cdc_enable_static_supplies( + struct sdm660_cdc_priv *sdm660_cdc, + struct sdm660_cdc_pdata *pdata) +{ + int i; + int ret = 0; + + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (pdata->regulator[i].ondemand) + continue; + ret = regulator_enable(sdm660_cdc->supplies[i].consumer); + if (ret) { + dev_err(sdm660_cdc->dev, "Failed to enable %s\n", + sdm660_cdc->supplies[i].supply); + break; + } + dev_dbg(sdm660_cdc->dev, "Enabled regulator %s\n", + sdm660_cdc->supplies[i].supply); + } + + while (ret && --i) + if (!pdata->regulator[i].ondemand) + regulator_disable(sdm660_cdc->supplies[i].consumer); + return ret; +} + +static void msm_anlg_cdc_disable_supplies(struct sdm660_cdc_priv *sdm660_cdc, + struct sdm660_cdc_pdata *pdata) +{ + int i; + + regulator_bulk_disable(sdm660_cdc->num_of_supplies, + sdm660_cdc->supplies); + for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { + if (regulator_count_voltages( + sdm660_cdc->supplies[i].consumer) <= 0) + continue; + regulator_set_voltage(sdm660_cdc->supplies[i].consumer, 0, + pdata->regulator[i].max_uv); + regulator_set_load(sdm660_cdc->supplies[i].consumer, 0); + } + regulator_bulk_free(sdm660_cdc->num_of_supplies, + sdm660_cdc->supplies); + kfree(sdm660_cdc->supplies); +} + +static const struct of_device_id sdm660_codec_of_match[] = { + { .compatible = "qcom,pmic-analog-codec", }, + {}, +}; + +static void msm_anlg_add_child_devices(struct work_struct *work) +{ + struct sdm660_cdc_priv *pdata; + struct platform_device *pdev; + struct device_node *node; + struct msm_dig_ctrl_data *dig_ctrl_data = NULL, *temp; + int ret, ctrl_num = 0; + struct msm_dig_ctrl_platform_data *platdata; + char plat_dev_name[MSM_DIG_CDC_STRING_LEN]; + + pdata = container_of(work, struct sdm660_cdc_priv, + msm_anlg_add_child_devices_work); + if (!pdata) { + pr_err("%s: Memory for pdata does not exist\n", + __func__); + return; + } + if (!pdata->dev->of_node) { + dev_err(pdata->dev, + "%s: DT node for pdata does not exist\n", __func__); + return; + } + + platdata = &pdata->dig_plat_data; + + for_each_child_of_node(pdata->dev->of_node, node) { + if (!strcmp(node->name, "msm-dig-codec")) + strlcpy(plat_dev_name, "msm_digital_codec", + (MSM_DIG_CDC_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(pdata->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = pdata->dev; + pdev->dev.of_node = node; + + if (!strcmp(node->name, "msm-dig-codec")) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (!strcmp(node->name, "msm-dig-codec")) { + temp = krealloc(dig_ctrl_data, + (ctrl_num + 1) * sizeof( + struct msm_dig_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto err; + } + dig_ctrl_data = temp; + dig_ctrl_data[ctrl_num].dig_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added digital codec device(s)\n", + __func__); + pdata->dig_ctrl_data = dig_ctrl_data; + } + } + + return; +fail_pdev_add: + platform_device_put(pdev); +err: + return; +} + +static int msm_anlg_cdc_probe(struct platform_device *pdev) +{ + int ret = 0; + struct sdm660_cdc_priv *sdm660_cdc = NULL; + struct sdm660_cdc_pdata *pdata; + int adsp_state; + + adsp_state = apr_get_subsys_state(); + if (adsp_state != APR_SUBSYS_LOADED) { + dev_err(&pdev->dev, "Adsp is not loaded yet %d\n", + adsp_state); + return -EPROBE_DEFER; + } + device_init_wakeup(&pdev->dev, true); + + if (pdev->dev.of_node) { + dev_dbg(&pdev->dev, "%s:Platform data from device tree\n", + __func__); + pdata = msm_anlg_cdc_populate_dt_pdata(&pdev->dev); + pdev->dev.platform_data = pdata; + } else { + dev_dbg(&pdev->dev, "%s:Platform data from board file\n", + __func__); + pdata = pdev->dev.platform_data; + } + if (pdata == NULL) { + dev_err(&pdev->dev, "%s:Platform data failed to populate\n", + __func__); + goto rtn; + } + sdm660_cdc = devm_kzalloc(&pdev->dev, sizeof(struct sdm660_cdc_priv), + GFP_KERNEL); + if (sdm660_cdc == NULL) { + ret = -ENOMEM; + goto rtn; + } + + sdm660_cdc->dev = &pdev->dev; + ret = msm_anlg_cdc_init_supplies(sdm660_cdc, pdata); + if (ret) { + dev_err(&pdev->dev, "%s: Fail to enable Codec supplies\n", + __func__); + goto rtn; + } + ret = msm_anlg_cdc_enable_static_supplies(sdm660_cdc, pdata); + if (ret) { + dev_err(&pdev->dev, + "%s: Fail to enable Codec pre-reset supplies\n", + __func__); + goto rtn; + } + /* Allow supplies to be ready */ + usleep_range(5, 6); + + wcd9xxx_spmi_set_dev(pdev, 0); + wcd9xxx_spmi_set_dev(pdev, 1); + if (wcd9xxx_spmi_irq_init()) { + dev_err(&pdev->dev, + "%s: irq initialization failed\n", __func__); + } else { + dev_dbg(&pdev->dev, + "%s: irq initialization passed\n", __func__); + } + dev_set_drvdata(&pdev->dev, sdm660_cdc); + + ret = snd_soc_register_codec(&pdev->dev, + &soc_codec_dev_sdm660_cdc, + msm_anlg_cdc_i2s_dai, + ARRAY_SIZE(msm_anlg_cdc_i2s_dai)); + if (ret) { + dev_err(&pdev->dev, + "%s:snd_soc_register_codec failed with error %d\n", + __func__, ret); + goto err_supplies; + } + BLOCKING_INIT_NOTIFIER_HEAD(&sdm660_cdc->notifier); + BLOCKING_INIT_NOTIFIER_HEAD(&sdm660_cdc->notifier_mbhc); + + sdm660_cdc->dig_plat_data.handle = (void *) sdm660_cdc; + sdm660_cdc->dig_plat_data.update_clkdiv = update_clkdiv; + sdm660_cdc->dig_plat_data.get_cdc_version = get_cdc_version; + sdm660_cdc->dig_plat_data.register_notifier = + msm_anlg_cdc_dig_register_notifier; + INIT_WORK(&sdm660_cdc->msm_anlg_add_child_devices_work, + msm_anlg_add_child_devices); + schedule_work(&sdm660_cdc->msm_anlg_add_child_devices_work); + + return ret; +err_supplies: + msm_anlg_cdc_disable_supplies(sdm660_cdc, pdata); +rtn: + return ret; +} + +static int msm_anlg_cdc_remove(struct platform_device *pdev) +{ + struct sdm660_cdc_priv *sdm660_cdc = dev_get_drvdata(&pdev->dev); + struct sdm660_cdc_pdata *pdata = sdm660_cdc->dev->platform_data; + + snd_soc_unregister_codec(&pdev->dev); + msm_anlg_cdc_disable_supplies(sdm660_cdc, pdata); + return 0; +} + +static struct platform_driver msm_anlg_codec_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .of_match_table = of_match_ptr(sdm660_codec_of_match) + }, + .probe = msm_anlg_cdc_probe, + .remove = msm_anlg_cdc_remove, +}; +module_platform_driver(msm_anlg_codec_driver); + +MODULE_DESCRIPTION("MSM Audio Analog codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h new file mode 100644 index 000000000000..9563565f36d2 --- /dev/null +++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h @@ -0,0 +1,240 @@ +/* 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_ANALOG_CDC_H +#define MSM_ANALOG_CDC_H + +#include +#include +#include +#include "../wcd-mbhc-v2.h" +#include "../wcdcal-hwdep.h" +#include "sdm660-cdc-registers.h" + +#define MICBIAS_EXT_BYP_CAP 0x00 +#define MICBIAS_NO_EXT_BYP_CAP 0x01 + +#define MSM89XX_NUM_IRQ_REGS 2 +#define MAX_REGULATOR 7 +#define MSM89XX_REG_VAL(reg, val) {reg, 0, val} + +#define MSM89XX_VDD_SPKDRV_NAME "cdc-vdd-spkdrv" + +#define DEFAULT_MULTIPLIER 800 +#define DEFAULT_GAIN 9 +#define DEFAULT_OFFSET 100 + +extern const u8 msm89xx_pmic_cdc_reg_readable[MSM89XX_PMIC_CDC_CACHE_SIZE]; +extern const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE]; +extern struct regmap_config msm89xx_cdc_core_regmap_config; +extern struct regmap_config msm89xx_pmic_cdc_regmap_config; + +enum wcd_curr_ref { + I_h4_UA = 0, + I_pt5_UA, + I_14_UA, + I_l4_UA, + I_1_UA, +}; + +enum wcd_mbhc_imp_det_pin { + WCD_MBHC_DET_NONE = 0, + WCD_MBHC_DET_HPHL, + WCD_MBHC_DET_HPHR, + WCD_MBHC_DET_BOTH, +}; + + +/* Each micbias can be assigned to one of three cfilters + * Vbatt_min >= .15V + ldoh_v + * ldoh_v >= .15v + cfiltx_mv + * If ldoh_v = 1.95 160 mv < cfiltx_mv < 1800 mv + * If ldoh_v = 2.35 200 mv < cfiltx_mv < 2200 mv + * If ldoh_v = 2.75 240 mv < cfiltx_mv < 2600 mv + * If ldoh_v = 2.85 250 mv < cfiltx_mv < 2700 mv + */ + +struct wcd_micbias_setting { + u8 ldoh_v; + u32 cfilt1_mv; /* in mv */ + u32 cfilt2_mv; /* in mv */ + u32 cfilt3_mv; /* in mv */ + /* Different WCD9xxx series codecs may not + * have 4 mic biases. If a codec has fewer + * mic biases, some of these properties will + * not be used. + */ + u8 bias1_cfilt_sel; + u8 bias2_cfilt_sel; + u8 bias3_cfilt_sel; + u8 bias4_cfilt_sel; + u8 bias1_cap_mode; + u8 bias2_cap_mode; + u8 bias3_cap_mode; + u8 bias4_cap_mode; + bool bias2_is_headset_only; +}; + +enum sdm660_cdc_pid_current { + MSM89XX_PID_MIC_2P5_UA, + MSM89XX_PID_MIC_5_UA, + MSM89XX_PID_MIC_10_UA, + MSM89XX_PID_MIC_20_UA, +}; + +struct sdm660_cdc_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +enum { + /* INTR_REG 0 - Digital Periph */ + MSM89XX_IRQ_SPKR_CNP = 0, + MSM89XX_IRQ_SPKR_CLIP, + MSM89XX_IRQ_SPKR_OCP, + MSM89XX_IRQ_MBHC_INSREM_DET1, + MSM89XX_IRQ_MBHC_RELEASE, + MSM89XX_IRQ_MBHC_PRESS, + MSM89XX_IRQ_MBHC_INSREM_DET, + MSM89XX_IRQ_MBHC_HS_DET, + /* INTR_REG 1 - Analog Periph */ + MSM89XX_IRQ_EAR_OCP, + MSM89XX_IRQ_HPHR_OCP, + MSM89XX_IRQ_HPHL_OCP, + MSM89XX_IRQ_EAR_CNP, + MSM89XX_IRQ_HPHR_CNP, + MSM89XX_IRQ_HPHL_CNP, + MSM89XX_NUM_IRQS, +}; + +enum { + ON_DEMAND_MICBIAS = 0, + ON_DEMAND_SPKDRV, + ON_DEMAND_SUPPLIES_MAX, +}; + +/* + * The delay list is per codec HW specification. + * Please add delay in the list in the future instead + * of magic number + */ +enum { + CODEC_DELAY_1_MS = 1000, + CODEC_DELAY_1_1_MS = 1100, +}; + +struct sdm660_cdc_regulator { + const char *name; + int min_uv; + int max_uv; + int optimum_ua; + bool ondemand; + struct regulator *regulator; +}; + +struct on_demand_supply { + struct regulator *supply; + atomic_t ref; + int min_uv; + int max_uv; + int optimum_ua; +}; + +struct wcd_imped_i_ref { + enum wcd_curr_ref curr_ref; + int min_val; + int multiplier; + int gain_adj; + int offset; +}; + +enum sdm660_cdc_micbias_num { + MSM89XX_MICBIAS1 = 0, +}; + +/* Hold instance to digital codec platform device */ +struct msm_dig_ctrl_data { + struct platform_device *dig_pdev; +}; + +struct msm_dig_ctrl_platform_data { + void *handle; + void (*update_clkdiv)(void *handle, int val); + int (*get_cdc_version)(void *handle); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); +}; + +struct sdm660_cdc_priv { + struct device *dev; + u32 num_of_supplies; + struct regulator_bulk_data *supplies; + struct snd_soc_codec *codec; + struct work_struct msm_anlg_add_child_devices_work; + struct msm_dig_ctrl_platform_data dig_plat_data; + /* digital codec data structure */ + struct msm_dig_ctrl_data *dig_ctrl_data; + struct blocking_notifier_head notifier; + u16 pmic_rev; + u16 codec_version; + u16 analog_major_rev; + u32 boost_voltage; + u32 adc_count; + u32 rx_bias_count; + bool int_mclk0_enabled; + u16 boost_option; + /* mode to select hd2 */ + u32 hph_mode; + /* compander used for each rx chain */ + bool spk_boost_set; + bool ear_pa_boost_set; + bool ext_spk_boost_set; + struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX]; + struct regulator *spkdrv_reg; + struct blocking_notifier_head notifier_mbhc; + /* mbhc module */ + struct wcd_mbhc mbhc; + /* cal info for codec */ + struct fw_info *fw_data; + struct notifier_block audio_ssr_nb; + int (*codec_spk_ext_pa_cb)(struct snd_soc_codec *codec, int enable); + unsigned long status_mask; + struct wcd_imped_i_ref imped_i_ref; + enum wcd_mbhc_imp_det_pin imped_det_pin; + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; +}; + +struct sdm660_cdc_pdata { + struct wcd_micbias_setting micbias; + struct sdm660_cdc_regulator regulator[MAX_REGULATOR]; +}; + + +extern int msm_anlg_cdc_mclk_enable(struct snd_soc_codec *codec, + int mclk_enable, bool dapm); + +extern int msm_anlg_cdc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg); + +extern void msm_anlg_cdc_hs_detect_exit(struct snd_soc_codec *codec); + +extern void sdm660_cdc_update_int_spk_boost(bool enable); + +extern void msm_anlg_cdc_spk_ext_pa_cb( + int (*codec_spk_ext_pa)(struct snd_soc_codec *codec, + int enable), struct snd_soc_codec *codec); +int msm_anlg_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +#endif diff --git a/sound/soc/codecs/sdm660_cdc/msm-cdc-common.h b/sound/soc/codecs/sdm660_cdc/msm-cdc-common.h new file mode 100644 index 000000000000..1a490a492b45 --- /dev/null +++ b/sound/soc/codecs/sdm660_cdc/msm-cdc-common.h @@ -0,0 +1,67 @@ +/* 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 "sdm660-cdc-registers.h" + +extern struct reg_default + msm89xx_cdc_core_defaults[MSM89XX_CDC_CORE_CACHE_SIZE]; +extern struct reg_default + msm89xx_pmic_cdc_defaults[MSM89XX_PMIC_CDC_CACHE_SIZE]; + +bool msm89xx_cdc_core_readable_reg(struct device *dev, unsigned int reg); +bool msm89xx_cdc_core_writeable_reg(struct device *dev, unsigned int reg); +bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg); + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_VIFEED, + AIF3_SVA, + NUM_CODEC_DAIS, +}; + +enum codec_versions { + TOMBAK_1_0, + TOMBAK_2_0, + CONGA, + CAJON, + CAJON_2_0, + DIANGU, + DRAX_CDC, + UNSUPPORTED, +}; + +/* Support different hph modes */ +enum { + NORMAL_MODE = 0, + HD2_MODE, +}; + +enum dig_cdc_notify_event { + DIG_CDC_EVENT_INVALID, + DIG_CDC_EVENT_CLK_ON, + DIG_CDC_EVENT_CLK_OFF, + DIG_CDC_EVENT_RX1_MUTE_ON, + DIG_CDC_EVENT_RX1_MUTE_OFF, + DIG_CDC_EVENT_RX2_MUTE_ON, + DIG_CDC_EVENT_RX2_MUTE_OFF, + DIG_CDC_EVENT_RX3_MUTE_ON, + DIG_CDC_EVENT_RX3_MUTE_OFF, + DIG_CDC_EVENT_PRE_RX1_INT_ON, + DIG_CDC_EVENT_PRE_RX2_INT_ON, + DIG_CDC_EVENT_POST_RX1_INT_OFF, + DIG_CDC_EVENT_POST_RX2_INT_OFF, + DIG_CDC_EVENT_SSR_DOWN, + DIG_CDC_EVENT_SSR_UP, + DIG_CDC_EVENT_LAST, +}; diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c new file mode 100644 index 000000000000..3f9c0b4a5b83 --- /dev/null +++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -0,0 +1,2189 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include "sdm660-cdc-registers.h" +#include "msm-digital-cdc.h" +#include "msm-cdc-common.h" +#include "../../msm/sdm660-common.h" +#include "../../../../drivers/base/regmap/internal.h" + +#define DRV_NAME "msm_digital_codec" +#define MCLK_RATE_9P6MHZ 9600000 +#define MCLK_RATE_12P288MHZ 12288000 +#define TX_MUX_CTL_CUT_OFF_FREQ_MASK 0x30 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define MSM_DIG_CDC_VERSION_ENTRY_SIZE 32 + +static unsigned long rx_digital_gain_reg[] = { + MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, + MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, + MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, +}; + +static unsigned long tx_digital_gain_reg[] = { + MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN, + MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN, +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); + +struct snd_soc_codec *registered_digcodec; +struct hpf_work tx_hpf_work[NUM_DECIMATORS]; + +/* Codec supports 2 IIR filters */ +enum { + IIR1 = 0, + IIR2, + IIR_MAX, +}; + +static int msm_digcdc_clock_control(bool flag) +{ + int ret = -EINVAL; + struct msm_asoc_mach_data *pdata = NULL; + struct msm_dig_priv *msm_dig_cdc = + snd_soc_codec_get_drvdata(registered_digcodec); + + pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card); + + if (flag) { + mutex_lock(&pdata->cdc_int_mclk0_mutex); + if (atomic_read(&pdata->int_mclk0_enabled) == false) { + 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\n", + __func__); + /* + * Avoid access to lpass register + * as clock enable failed during SSR. + */ + if (ret == -ENODEV) + msm_dig_cdc->regmap->cache_only = true; + return ret; + } + pr_debug("enabled digital codec core clk\n"); + atomic_set(&pdata->int_mclk0_enabled, true); + schedule_delayed_work(&pdata->disable_int_mclk0_work, + 50); + } + } else { + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + dev_dbg(registered_digcodec->dev, + "disable MCLK, workq to disable set already\n"); + } + return 0; +} + +static void enable_digital_callback(void *flag) +{ + msm_digcdc_clock_control(true); +} + +static void disable_digital_callback(void *flag) +{ + msm_digcdc_clock_control(false); + pr_debug("disable mclk happens in workq\n"); +} + +static int msm_dig_cdc_put_dec_enum(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 *w = wlist->widgets[0]; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int dec_mux, decimator; + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + u16 tx_mux_ctl_reg; + u8 adc_dmic_sel = 0x0; + int ret = 0; + char *dec_num; + + if (ucontrol->value.enumerated.item[0] > e->items) { + dev_err(codec->dev, "%s: Invalid enum value: %d\n", + __func__, ucontrol->value.enumerated.item[0]); + return -EINVAL; + } + dec_mux = ucontrol->value.enumerated.item[0]; + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) { + dev_err(codec->dev, "%s: failed to copy string\n", + __func__); + return -ENOMEM; + } + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + + dec_num = strpbrk(dec_name, "12345"); + if (dec_num == NULL) { + dev_err(codec->dev, "%s: Invalid DEC selected\n", __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec_num, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, dec_name); + ret = -EINVAL; + goto out; + } + + dev_dbg(w->dapm->dev, "%s(): widget = %s decimator = %u dec_mux = %u\n" + , __func__, w->name, decimator, dec_mux); + + switch (decimator) { + case 1: + case 2: + case 3: + case 4: + case 5: + if ((dec_mux == 4) || (dec_mux == 5) || + (dec_mux == 6) || (dec_mux == 7)) + adc_dmic_sel = 0x1; + else + adc_dmic_sel = 0x0; + break; + default: + dev_err(codec->dev, "%s: Invalid Decimator = %u\n", + __func__, decimator); + ret = -EINVAL; + goto out; + } + + tx_mux_ctl_reg = + MSM89XX_CDC_CORE_TX1_MUX_CTL + 32 * (decimator - 1); + + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x1, adc_dmic_sel); + + ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); + +out: + kfree(widget_name); + return ret; +} + + +static int msm_dig_cdc_codec_config_compander(struct snd_soc_codec *codec, + int interp_n, int event) +{ + struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: event %d shift %d, enabled %d\n", + __func__, event, interp_n, + dig_cdc->comp_enabled[interp_n]); + + /* compander is not enabled */ + if (!dig_cdc->comp_enabled[interp_n]) + return 0; + + switch (dig_cdc->comp_enabled[interp_n]) { + case COMPANDER_1: + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x09); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL, + 1 << interp_n, 1 << interp_n); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x01); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0x50); + /* add sleep for compander to settle */ + usleep_range(1000, 1100); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x28); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0xB0); + + /* Enable Compander GPIO */ + if (dig_cdc->codec_hph_comp_gpio) + dig_cdc->codec_hph_comp_gpio(1, codec); + } else if (SND_SOC_DAPM_EVENT_OFF(event)) { + /* Disable Compander GPIO */ + if (dig_cdc->codec_hph_comp_gpio) + dig_cdc->codec_hph_comp_gpio(0, codec); + + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x05); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL, + 1 << interp_n, 0); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x00); + } + break; + default: + dev_dbg(codec->dev, "%s: Invalid compander %d\n", __func__, + dig_cdc->comp_enabled[interp_n]); + break; + }; + + return 0; +} + +/** + * msm_dig_cdc_hph_comp_cb - registers callback to codec by machine driver. + * + * @codec_hph_comp_gpio: function pointer to set comp gpio at machine driver + * @codec: codec pointer + * + */ +void msm_dig_cdc_hph_comp_cb( + int (*codec_hph_comp_gpio)(bool enable, struct snd_soc_codec *codec), + struct snd_soc_codec *codec) +{ + struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: Enter\n", __func__); + dig_cdc->codec_hph_comp_gpio = codec_hph_comp_gpio; +} +EXPORT_SYMBOL(msm_dig_cdc_hph_comp_cb); + +static int msm_dig_cdc_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (w->shift >= MSM89XX_RX_MAX || w->shift < 0) { + dev_err(codec->dev, "%s: wrong RX index: %d\n", + __func__, w->shift); + return -EINVAL; + } + switch (event) { + case SND_SOC_DAPM_POST_PMU: + msm_dig_cdc_codec_config_compander(codec, w->shift, event); + /* apply the digital gain after the interpolator is enabled*/ + if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg)) + snd_soc_write(codec, + rx_digital_gain_reg[w->shift], + snd_soc_read(codec, + rx_digital_gain_reg[w->shift]) + ); + break; + case SND_SOC_DAPM_POST_PMD: + msm_dig_cdc_codec_config_compander(codec, w->shift, event); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, + 1 << w->shift, 1 << w->shift); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, + 1 << w->shift, 0x0); + /* + * disable the mute enabled during the PMD of this device + */ + if ((w->shift == 0) && + (msm_dig_cdc->mute_mask & HPHL_PA_DISABLE)) { + pr_debug("disabling HPHL mute\n"); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= ~(HPHL_PA_DISABLE); + } else if ((w->shift == 1) && + (msm_dig_cdc->mute_mask & HPHR_PA_DISABLE)) { + pr_debug("disabling HPHR mute\n"); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= ~(HPHR_PA_DISABLE); + } else if ((w->shift == 2) && + (msm_dig_cdc->mute_mask & SPKR_PA_DISABLE)) { + pr_debug("disabling SPKR mute\n"); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= ~(SPKR_PA_DISABLE); + } + } + return 0; +} + +static int msm_dig_cdc_get_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_dig_cdc_put_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx), + (1 << band_idx), (value << band_idx)); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + ((snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) & + (1 << band_idx)) != 0)); + + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 8); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 16); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + + 64 * iir_idx)) & 0x3f) << 24); + + return value; + +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx), + (value >> 24) & 0x3F); + +} + +static int msm_dig_cdc_get_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static int msm_dig_cdc_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + /* Mask top bit it is reserved */ + /* Updates addr automatically for each B2 write */ + snd_soc_write(codec, + (MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[0]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[1]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[2]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[3]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[4]); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static void tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct snd_soc_codec *codec; + struct msm_dig_priv *msm_dig_cdc; + u16 tx_mux_ctl_reg; + u8 hpf_cut_of_freq; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + codec = hpf_work->dig_cdc->codec; + msm_dig_cdc = hpf_work->dig_cdc; + hpf_cut_of_freq = hpf_work->tx_hpf_cut_of_freq; + + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL + + (hpf_work->decimator - 1) * 32; + + dev_dbg(codec->dev, "%s(): decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, (unsigned int)hpf_cut_of_freq); + msm_dig_cdc->update_clkdiv(msm_dig_cdc->handle, 0x51); + + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, hpf_cut_of_freq << 4); +} + +static int msm_dig_cdc_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int value = 0, reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (w->shift == 0) + reg = MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL; + else if (w->shift == 1) + reg = MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL; + else + goto ret; + value = snd_soc_read(codec, reg); + snd_soc_write(codec, reg, value); + break; + default: + pr_err("%s: event = %d not expected\n", __func__, event); + } +ret: + return 0; +} + +static int msm_dig_cdc_compander_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + int comp_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int rx_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + dev_dbg(codec->dev, "%s: msm_dig_cdc->comp[%d]_enabled[%d] = %d\n", + __func__, comp_idx, rx_idx, + dig_cdc->comp_enabled[rx_idx]); + + ucontrol->value.integer.value[0] = dig_cdc->comp_enabled[rx_idx]; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_dig_cdc_compander_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + int comp_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int rx_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (dig_cdc->version >= DIANGU) { + if (!value) + dig_cdc->comp_enabled[rx_idx] = 0; + else + dig_cdc->comp_enabled[rx_idx] = comp_idx; + } + + dev_dbg(codec->dev, "%s: msm_dig_cdc->comp[%d]_enabled[%d] = %d\n", + __func__, comp_idx, rx_idx, + dig_cdc->comp_enabled[rx_idx]); + + return 0; +} + +static const struct snd_kcontrol_new compander_kcontrols[] = { + SOC_SINGLE_EXT("COMP0 RX1", COMPANDER_1, MSM89XX_RX1, 1, 0, + msm_dig_cdc_compander_get, msm_dig_cdc_compander_set), + + SOC_SINGLE_EXT("COMP0 RX2", COMPANDER_1, MSM89XX_RX2, 1, 0, + msm_dig_cdc_compander_get, msm_dig_cdc_compander_set), + +}; + +static int msm_dig_cdc_set_interpolator_rate(struct snd_soc_dai *dai, + u8 rx_fs_rate_reg_val, + u32 sample_rate) +{ + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_RX1_B5_CTL, 0xF0, rx_fs_rate_reg_val); + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_RX2_B5_CTL, 0xF0, rx_fs_rate_reg_val); + return 0; +} + +static int msm_dig_cdc_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u8 tx_fs_rate, rx_fs_rate, rx_clk_fs_rate; + int ret; + + dev_dbg(dai->codec->dev, + "%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params), params_format(params)); + + switch (params_rate(params)) { + case 8000: + tx_fs_rate = 0x00; + rx_fs_rate = 0x00; + rx_clk_fs_rate = 0x00; + break; + case 16000: + tx_fs_rate = 0x20; + rx_fs_rate = 0x20; + rx_clk_fs_rate = 0x01; + break; + case 32000: + tx_fs_rate = 0x40; + rx_fs_rate = 0x40; + rx_clk_fs_rate = 0x02; + break; + case 44100: + case 48000: + tx_fs_rate = 0x60; + rx_fs_rate = 0x60; + rx_clk_fs_rate = 0x03; + break; + case 96000: + tx_fs_rate = 0x80; + rx_fs_rate = 0x80; + rx_clk_fs_rate = 0x04; + break; + case 192000: + tx_fs_rate = 0xA0; + rx_fs_rate = 0xA0; + rx_clk_fs_rate = 0x05; + break; + default: + dev_err(dai->codec->dev, + "%s: Invalid sampling rate %d\n", __func__, + params_rate(params)); + return -EINVAL; + } + + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x0F, rx_clk_fs_rate); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_CAPTURE: + break; + case SNDRV_PCM_STREAM_PLAYBACK: + ret = msm_dig_cdc_set_interpolator_rate(dai, rx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(dai->codec->dev, + "%s: set decimator rate failed %d\n", __func__, + ret); + return ret; + } + break; + default: + dev_err(dai->codec->dev, + "%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + } + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x20); + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + snd_soc_update_bits(dai->codec, + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x00); + break; + default: + dev_err(dai->codec->dev, "%s: wrong format selected\n", + __func__); + return -EINVAL; + } + return 0; +} + +static int msm_dig_cdc_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + u8 dmic_clk_en; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + unsigned int dmic; + int ret; + char *dmic_num = strpbrk(w->name, "1234"); + + if (dmic_num == NULL) { + dev_err(codec->dev, "%s: Invalid DMIC\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(dmic_num, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, + "%s: Invalid DMIC line on the codec\n", __func__); + return -EINVAL; + } + + switch (dmic) { + case 1: + case 2: + dmic_clk_en = 0x01; + dmic_clk_cnt = &(dig_cdc->dmic_1_2_clk_cnt); + dmic_clk_reg = MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL; + dev_dbg(codec->dev, + "%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + break; + case 3: + case 4: + dmic_clk_en = 0x01; + dmic_clk_cnt = &(dig_cdc->dmic_3_4_clk_cnt); + dmic_clk_reg = MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL; + dev_dbg(codec->dev, + "%s() event %d DMIC%d dmic_3_4_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x0E, 0x04); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TX1_DMIC_CTL + (dmic - 1) * 0x20, + 0x07, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + break; + } + return 0; +} + +static int msm_dig_cdc_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_asoc_mach_data *pdata = NULL; + unsigned int decimator; + struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec); + char *dec_name = NULL; + char *widget_name = NULL; + char *temp; + int ret = 0, i; + u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg; + u8 dec_hpf_cut_of_freq; + int offset; + char *dec_num; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + temp = widget_name; + + dec_name = strsep(&widget_name, " "); + widget_name = temp; + if (!dec_name) { + dev_err(codec->dev, + "%s: Invalid decimator = %s\n", __func__, w->name); + ret = -EINVAL; + goto out; + } + + dec_num = strpbrk(dec_name, "12345"); + if (dec_num == NULL) { + dev_err(codec->dev, "%s: Invalid Decimator\n", __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec_num, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, + "%s: Invalid decimator = %s\n", __func__, dec_name); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, + "%s(): widget = %s dec_name = %s decimator = %u\n", __func__, + w->name, dec_name, decimator); + + if (w->reg == MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL) { + dec_reset_reg = MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL; + offset = 0; + } else { + dev_err(codec->dev, "%s: Error, incorrect dec\n", __func__); + ret = -EINVAL; + goto out; + } + + tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG + + 32 * (decimator - 1); + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL + + 32 * (decimator - 1); + if (decimator == 5) { + tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG; + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX5_MUX_CTL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Enableable TX digital mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); + for (i = 0; i < NUM_DECIMATORS; i++) { + if (decimator == i + 1) + msm_dig_cdc->dec_active[i] = true; + } + + dec_hpf_cut_of_freq = snd_soc_read(codec, tx_mux_ctl_reg); + + dec_hpf_cut_of_freq = (dec_hpf_cut_of_freq & 0x30) >> 4; + + tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq = + dec_hpf_cut_of_freq; + + if (dec_hpf_cut_of_freq != CF_MIN_3DB_150HZ) { + + /* set cut of freq to CF_MIN_3DB_150HZ (0x1); */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, + CF_MIN_3DB_150HZ << 4); + } + msm_dig_cdc->update_clkdiv(msm_dig_cdc->handle, 0x42); + break; + case SND_SOC_DAPM_POST_PMU: + /* enable HPF */ + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x00); + + if (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq != + CF_MIN_3DB_150HZ) { + + schedule_delayed_work(&tx_hpf_work[decimator - 1].dwork, + msecs_to_jiffies(300)); + } + /* apply the digital gain after the decimator is enabled*/ + if ((w->shift) < ARRAY_SIZE(tx_digital_gain_reg)) + snd_soc_write(codec, + tx_digital_gain_reg[w->shift + offset], + snd_soc_read(codec, + tx_digital_gain_reg[w->shift + offset]) + ); + if (pdata->lb_mode) { + pr_debug("%s: loopback mode unmute the DEC\n", + __func__); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); + } + snd_soc_update_bits(codec, tx_vol_ctl_reg, + 0x01, 0x00); + + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); + msleep(20); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); + cancel_delayed_work_sync(&tx_hpf_work[decimator - 1].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, + 1 << w->shift); + snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, + (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq) << 4); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); + for (i = 0; i < NUM_DECIMATORS; i++) { + if (decimator == i + 1) + msm_dig_cdc->dec_active[i] = false; + } + break; + } +out: + kfree(widget_name); + return ret; +} + +static int msm_dig_cdc_event_notify(struct notifier_block *block, + unsigned long val, + void *data) +{ + enum dig_cdc_notify_event event = (enum dig_cdc_notify_event)val; + struct snd_soc_codec *codec = registered_digcodec; + struct msm_dig_priv *msm_dig_cdc = snd_soc_codec_get_drvdata(codec); + struct msm_asoc_mach_data *pdata = NULL; + int ret = -EINVAL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + + switch (event) { + case DIG_CDC_EVENT_CLK_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x03); + if (pdata->mclk_freq == MCLK_RATE_12P288MHZ || + pdata->native_clk_set) + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x00); + else if (pdata->mclk_freq == MCLK_RATE_9P6MHZ) + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x01); + break; + case DIG_CDC_EVENT_CLK_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x00); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x00); + break; + case DIG_CDC_EVENT_RX1_MUTE_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x01); + msm_dig_cdc->mute_mask |= HPHL_PA_DISABLE; + break; + case DIG_CDC_EVENT_RX1_MUTE_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= (~HPHL_PA_DISABLE); + break; + case DIG_CDC_EVENT_RX2_MUTE_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x01); + msm_dig_cdc->mute_mask |= HPHR_PA_DISABLE; + break; + case DIG_CDC_EVENT_RX2_MUTE_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= (~HPHR_PA_DISABLE); + break; + case DIG_CDC_EVENT_RX3_MUTE_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x01); + msm_dig_cdc->mute_mask |= SPKR_PA_DISABLE; + break; + case DIG_CDC_EVENT_RX3_MUTE_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00); + msm_dig_cdc->mute_mask &= (~SPKR_PA_DISABLE); + break; + case DIG_CDC_EVENT_PRE_RX1_INT_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x28); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0x10); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x80); + break; + case DIG_CDC_EVENT_PRE_RX2_INT_ON: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x28); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0x10); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x80); + break; + case DIG_CDC_EVENT_POST_RX1_INT_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x3C, 0x00); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0xFF); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x00); + break; + case DIG_CDC_EVENT_POST_RX2_INT_OFF: + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x3C, 0x00); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0xFF); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x00); + break; + case DIG_CDC_EVENT_SSR_DOWN: + regcache_cache_only(msm_dig_cdc->regmap, true); + break; + case DIG_CDC_EVENT_SSR_UP: + regcache_cache_only(msm_dig_cdc->regmap, false); + regcache_mark_dirty(msm_dig_cdc->regmap); + + mutex_lock(&pdata->cdc_int_mclk0_mutex); + 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\n", + __func__); + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + break; + } + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + + regcache_sync(msm_dig_cdc->regmap); + + mutex_lock(&pdata->cdc_int_mclk0_mutex); + pdata->digital_cdc_core_clk.enable = 0; + afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + break; + case DIG_CDC_EVENT_INVALID: + default: + break; + } + return 0; +} + +static ssize_t msm_dig_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct msm_dig_priv *msm_dig; + char buffer[MSM_DIG_CDC_VERSION_ENTRY_SIZE]; + int len = 0; + + msm_dig = (struct msm_dig_priv *) entry->private_data; + if (!msm_dig) { + pr_err("%s: msm_dig priv is null\n", __func__); + return -EINVAL; + } + + switch (msm_dig->version) { + case DRAX_CDC: + len = snprintf(buffer, sizeof(buffer), "SDM660-CDC_1_0\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops msm_dig_codec_info_ops = { + .read = msm_dig_codec_version_read, +}; + +/* + * msm_dig_codec_info_create_codec_entry - creates msm_dig module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates msm_dig module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int msm_dig_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct msm_dig_priv *msm_dig; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + msm_dig = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + msm_dig->entry = snd_register_module_info(codec_root->module, + "msm_digital_codec", + codec_root); + if (!msm_dig->entry) { + dev_dbg(codec->dev, "%s: failed to create msm_digital entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + msm_dig->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create msm_digital version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = msm_dig; + version_entry->size = MSM_DIG_CDC_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &msm_dig_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + msm_dig->version_entry = version_entry; + if (msm_dig->get_cdc_version) + msm_dig->version = msm_dig->get_cdc_version(msm_dig->handle); + else + msm_dig->version = DRAX_CDC; + + return 0; +} +EXPORT_SYMBOL(msm_dig_codec_info_create_codec_entry); + +static int msm_dig_cdc_soc_probe(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int i, ret; + + msm_dig_cdc->codec = codec; + + snd_soc_add_codec_controls(codec, compander_kcontrols, + ARRAY_SIZE(compander_kcontrols)); + + for (i = 0; i < NUM_DECIMATORS; i++) { + tx_hpf_work[i].dig_cdc = msm_dig_cdc; + tx_hpf_work[i].decimator = i + 1; + INIT_DELAYED_WORK(&tx_hpf_work[i].dwork, + tx_hpf_corner_freq_callback); + } + + for (i = 0; i < MSM89XX_RX_MAX; i++) + msm_dig_cdc->comp_enabled[i] = COMPANDER_NONE; + + /* Register event notifier */ + msm_dig_cdc->nblock.notifier_call = msm_dig_cdc_event_notify; + if (msm_dig_cdc->register_notifier) { + ret = msm_dig_cdc->register_notifier(msm_dig_cdc->handle, + &msm_dig_cdc->nblock, + true); + if (ret) { + pr_err("%s: Failed to register notifier %d\n", + __func__, ret); + return ret; + } + } + registered_digcodec = codec; + + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "ADC1_IN"); + snd_soc_dapm_ignore_suspend(dapm, "ADC2_IN"); + snd_soc_dapm_ignore_suspend(dapm, "ADC3_IN"); + snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX1"); + snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX2"); + snd_soc_dapm_ignore_suspend(dapm, "PDM_OUT_RX3"); + + snd_soc_dapm_sync(dapm); + + return 0; +} + +static int msm_dig_cdc_soc_remove(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + + if (msm_dig_cdc->register_notifier) + msm_dig_cdc->register_notifier(msm_dig_cdc->handle, + &msm_dig_cdc->nblock, + false); + iounmap(msm_dig_cdc->dig_base); + return 0; +} + +static const struct snd_soc_dapm_route audio_dig_map[] = { + {"RX_I2S_CLK", NULL, "CDC_CONN"}, + {"I2S RX1", NULL, "RX_I2S_CLK"}, + {"I2S RX2", NULL, "RX_I2S_CLK"}, + {"I2S RX3", NULL, "RX_I2S_CLK"}, + + {"I2S TX1", NULL, "TX_I2S_CLK"}, + {"I2S TX2", NULL, "TX_I2S_CLK"}, + {"I2S TX3", NULL, "TX_I2S_CLK"}, + {"I2S TX4", NULL, "TX_I2S_CLK"}, + {"I2S TX5", NULL, "TX_I2S_CLK"}, + {"I2S TX6", NULL, "TX_I2S_CLK"}, + + {"I2S TX1", NULL, "DEC1 MUX"}, + {"I2S TX2", NULL, "DEC2 MUX"}, + {"I2S TX3", NULL, "I2S TX2 INP1"}, + {"I2S TX4", NULL, "I2S TX2 INP2"}, + {"I2S TX5", NULL, "DEC3 MUX"}, + {"I2S TX6", NULL, "I2S TX3 INP2"}, + + {"I2S TX2 INP1", "RX_MIX1", "RX1 MIX2"}, + {"I2S TX2 INP1", "DEC3", "DEC3 MUX"}, + {"I2S TX2 INP2", "RX_MIX2", "RX2 MIX2"}, + {"I2S TX2 INP2", "RX_MIX3", "RX3 MIX1"}, + {"I2S TX2 INP2", "DEC4", "DEC4 MUX"}, + {"I2S TX3 INP2", "DEC4", "DEC4 MUX"}, + {"I2S TX3 INP2", "DEC5", "DEC5 MUX"}, + + {"PDM_OUT_RX1", NULL, "RX1 CHAIN"}, + {"PDM_OUT_RX2", NULL, "RX2 CHAIN"}, + {"PDM_OUT_RX3", NULL, "RX3 CHAIN"}, + + {"RX1 CHAIN", NULL, "RX1 MIX2"}, + {"RX2 CHAIN", NULL, "RX2 MIX2"}, + {"RX3 CHAIN", NULL, "RX3 MIX1"}, + + {"RX1 MIX1", NULL, "RX1 MIX1 INP1"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP2"}, + {"RX1 MIX1", NULL, "RX1 MIX1 INP3"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP1"}, + {"RX2 MIX1", NULL, "RX2 MIX1 INP2"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP1"}, + {"RX3 MIX1", NULL, "RX3 MIX1 INP2"}, + {"RX1 MIX2", NULL, "RX1 MIX1"}, + {"RX1 MIX2", NULL, "RX1 MIX2 INP1"}, + {"RX2 MIX2", NULL, "RX2 MIX1"}, + {"RX2 MIX2", NULL, "RX2 MIX2 INP1"}, + + {"RX1 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX1 MIX1 INP1", "IIR2", "IIR2"}, + {"RX1 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX1 MIX1 INP2", "IIR2", "IIR2"}, + {"RX1 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX1 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX1 MIX1 INP3", "RX3", "I2S RX3"}, + + {"RX2 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP1", "IIR1", "IIR1"}, + {"RX2 MIX1 INP1", "IIR2", "IIR2"}, + {"RX2 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX2 MIX1 INP2", "IIR1", "IIR1"}, + {"RX2 MIX1 INP2", "IIR2", "IIR2"}, + + {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP1", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP1", "IIR1", "IIR1"}, + {"RX3 MIX1 INP1", "IIR2", "IIR2"}, + {"RX3 MIX1 INP2", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP2", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, + {"RX3 MIX1 INP2", "IIR1", "IIR1"}, + {"RX3 MIX1 INP2", "IIR2", "IIR2"}, + + {"RX1 MIX2 INP1", "IIR1", "IIR1"}, + {"RX2 MIX2 INP1", "IIR1", "IIR1"}, + {"RX1 MIX2 INP1", "IIR2", "IIR2"}, + {"RX2 MIX2 INP1", "IIR2", "IIR2"}, + + /* Decimator Inputs */ + {"DEC1 MUX", "DMIC1", "DMIC1"}, + {"DEC1 MUX", "DMIC2", "DMIC2"}, + {"DEC1 MUX", "DMIC3", "DMIC3"}, + {"DEC1 MUX", "DMIC4", "DMIC4"}, + {"DEC1 MUX", "ADC1", "ADC1_IN"}, + {"DEC1 MUX", "ADC2", "ADC2_IN"}, + {"DEC1 MUX", "ADC3", "ADC3_IN"}, + {"DEC1 MUX", NULL, "CDC_CONN"}, + + {"DEC2 MUX", "DMIC1", "DMIC1"}, + {"DEC2 MUX", "DMIC2", "DMIC2"}, + {"DEC2 MUX", "DMIC3", "DMIC3"}, + {"DEC2 MUX", "DMIC4", "DMIC4"}, + {"DEC2 MUX", "ADC1", "ADC1_IN"}, + {"DEC2 MUX", "ADC2", "ADC2_IN"}, + {"DEC2 MUX", "ADC3", "ADC3_IN"}, + {"DEC2 MUX", NULL, "CDC_CONN"}, + + {"DEC3 MUX", "DMIC1", "DMIC1"}, + {"DEC3 MUX", "DMIC2", "DMIC2"}, + {"DEC3 MUX", "DMIC3", "DMIC3"}, + {"DEC3 MUX", "DMIC4", "DMIC4"}, + {"DEC3 MUX", "ADC1", "ADC1_IN"}, + {"DEC3 MUX", "ADC2", "ADC2_IN"}, + {"DEC3 MUX", "ADC3", "ADC3_IN"}, + {"DEC3 MUX", NULL, "CDC_CONN"}, + + {"DEC4 MUX", "DMIC1", "DMIC1"}, + {"DEC4 MUX", "DMIC2", "DMIC2"}, + {"DEC4 MUX", "DMIC3", "DMIC3"}, + {"DEC4 MUX", "DMIC4", "DMIC4"}, + {"DEC4 MUX", "ADC1", "ADC1_IN"}, + {"DEC4 MUX", "ADC2", "ADC2_IN"}, + {"DEC4 MUX", "ADC3", "ADC3_IN"}, + {"DEC4 MUX", NULL, "CDC_CONN"}, + + {"DEC5 MUX", "DMIC1", "DMIC1"}, + {"DEC5 MUX", "DMIC2", "DMIC2"}, + {"DEC5 MUX", "DMIC3", "DMIC3"}, + {"DEC5 MUX", "DMIC4", "DMIC4"}, + {"DEC5 MUX", "ADC1", "ADC1_IN"}, + {"DEC5 MUX", "ADC2", "ADC2_IN"}, + {"DEC5 MUX", "ADC3", "ADC3_IN"}, + {"DEC5 MUX", NULL, "CDC_CONN"}, + + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"}, + {"IIR1 INP1 MUX", "DEC3", "DEC3 MUX"}, + {"IIR1 INP1 MUX", "DEC4", "DEC4 MUX"}, + {"IIR2", NULL, "IIR2 INP1 MUX"}, + {"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"}, + {"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"}, + {"IIR1 INP1 MUX", "DEC3", "DEC3 MUX"}, + {"IIR1 INP1 MUX", "DEC4", "DEC4 MUX"}, +}; + + +static const char * const i2s_tx2_inp1_text[] = { + "ZERO", "RX_MIX1", "DEC3" +}; + +static const char * const i2s_tx2_inp2_text[] = { + "ZERO", "RX_MIX2", "RX_MIX3", "DEC4" +}; + +static const char * const i2s_tx3_inp2_text[] = { + "DEC4", "DEC5" +}; + +static const char * const rx_mix1_text[] = { + "ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3" +}; + +static const char * const rx_mix2_text[] = { + "ZERO", "IIR1", "IIR2" +}; + +static const char * const dec_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2", "DMIC3", "DMIC4" +}; + +static const char * const iir_inp1_text[] = { + "ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3", "DEC3", "DEC4" +}; + +/* I2S TX MUXes */ +static const struct soc_enum i2s_tx2_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, + 2, 3, i2s_tx2_inp1_text); + +static const struct soc_enum i2s_tx2_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, + 0, 4, i2s_tx2_inp2_text); + +static const struct soc_enum i2s_tx3_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, + 4, 2, i2s_tx3_inp2_text); + +/* RX1 MIX1 */ +static const struct soc_enum rx_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B2_CTL, + 0, 6, rx_mix1_text); + +/* RX1 MIX2 */ +static const struct soc_enum rx_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B3_CTL, + 0, 3, rx_mix2_text); + +/* RX2 MIX1 */ +static const struct soc_enum rx2_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx2_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx2_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, + 0, 6, rx_mix1_text); + +/* RX2 MIX2 */ +static const struct soc_enum rx2_mix2_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B3_CTL, + 0, 3, rx_mix2_text); + +/* RX3 MIX1 */ +static const struct soc_enum rx3_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 0, 6, rx_mix1_text); + +static const struct soc_enum rx3_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 3, 6, rx_mix1_text); + +static const struct soc_enum rx3_mix1_inp3_chain_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, + 0, 6, rx_mix1_text); + +/* DEC */ +static const struct soc_enum dec1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL, + 0, 8, dec_mux_text); + +static const struct soc_enum dec2_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL, + 3, 8, dec_mux_text); + +static const struct soc_enum dec3_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B2_CTL, + 0, 8, dec_mux_text); + +static const struct soc_enum dec4_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B2_CTL, + 3, 8, dec_mux_text); + +static const struct soc_enum decsva_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B3_CTL, + 0, 8, dec_mux_text); + +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL, + 0, 8, iir_inp1_text); + +static const struct soc_enum iir2_inp1_mux_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL, + 0, 8, iir_inp1_text); + +/*cut of frequency for high pass filter*/ +static const char * const cf_text[] = { + "MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz" +}; + +static const struct soc_enum cf_rxmix1_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX1_B4_CTL, 0, 3, cf_text); + +static const struct soc_enum cf_rxmix2_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX2_B4_CTL, 0, 3, cf_text); + +static const struct soc_enum cf_rxmix3_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX3_B4_CTL, 0, 3, cf_text); + +static const struct snd_kcontrol_new rx3_mix1_inp1_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum); + +#define MSM89XX_DEC_ENUM(xname, xenum) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = snd_soc_info_enum_double, \ + .get = snd_soc_dapm_get_enum_double, \ + .put = msm_dig_cdc_put_dec_enum, \ + .private_value = (unsigned long)&xenum } + +static const struct snd_kcontrol_new dec1_mux = + MSM89XX_DEC_ENUM("DEC1 MUX Mux", dec1_mux_enum); + +static const struct snd_kcontrol_new dec2_mux = + MSM89XX_DEC_ENUM("DEC2 MUX Mux", dec2_mux_enum); + +static const struct snd_kcontrol_new dec3_mux = + MSM89XX_DEC_ENUM("DEC3 MUX Mux", dec3_mux_enum); + +static const struct snd_kcontrol_new dec4_mux = + MSM89XX_DEC_ENUM("DEC4 MUX Mux", dec4_mux_enum); + +static const struct snd_kcontrol_new decsva_mux = + MSM89XX_DEC_ENUM("DEC5 MUX Mux", decsva_mux_enum); + +static const struct snd_kcontrol_new i2s_tx2_inp1_mux = + SOC_DAPM_ENUM("I2S TX2 INP1 Mux", i2s_tx2_inp1_chain_enum); + +static const struct snd_kcontrol_new i2s_tx2_inp2_mux = + SOC_DAPM_ENUM("I2S TX2 INP2 Mux", i2s_tx2_inp2_chain_enum); + +static const struct snd_kcontrol_new i2s_tx3_inp2_mux = + SOC_DAPM_ENUM("I2S TX3 INP2 Mux", i2s_tx3_inp2_chain_enum); + +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); + +static const struct snd_kcontrol_new iir2_inp1_mux = + SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum); + +static const struct snd_kcontrol_new rx_mix1_inp1_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_mix1_inp2_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_mix1_inp3_mux = + SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp1_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp2_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP2 Mux", rx2_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx2_mix1_inp3_mux = + SOC_DAPM_ENUM("RX2 MIX1 INP3 Mux", rx2_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx3_mix1_inp2_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx3_mix1_inp3_mux = + SOC_DAPM_ENUM("RX3 MIX1 INP3 Mux", rx3_mix1_inp3_chain_enum); + +static const struct snd_kcontrol_new rx1_mix2_inp1_mux = + SOC_DAPM_ENUM("RX1 MIX2 INP1 Mux", rx_mix2_inp1_chain_enum); + +static const struct snd_kcontrol_new rx2_mix2_inp1_mux = + SOC_DAPM_ENUM("RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum); + +static const struct snd_soc_dapm_widget msm_dig_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("I2S RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("I2S RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT("I2S TX1", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX2", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX3", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX4", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX5", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("I2S TX6", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER_E("RX1 MIX2", MSM89XX_CDC_CORE_CLK_RX_B1_CTL, + MSM89XX_RX1, 0, NULL, 0, + msm_dig_cdc_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX2 MIX2", MSM89XX_CDC_CORE_CLK_RX_B1_CTL, + MSM89XX_RX2, 0, NULL, 0, + msm_dig_cdc_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX3 MIX1", MSM89XX_CDC_CORE_CLK_RX_B1_CTL, + MSM89XX_RX3, 0, NULL, 0, + msm_dig_cdc_codec_enable_interpolator, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX1 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX2 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX3 CHAIN", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx2_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp1_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp2_mux), + SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0, + &rx3_mix1_inp3_mux), + + SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx1_mix2_inp1_mux), + SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0, + &rx2_mix2_inp1_mux), + + SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, MSM89XX_CDC_CORE_CLK_OTHR_CTL, + 2, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("DEC1 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0, 0, + &dec1_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("DEC2 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 1, 0, + &dec2_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("DEC3 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 2, 0, + &dec3_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("DEC4 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 3, 0, + &dec4_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("DEC5 MUX", + MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 4, 0, + &decsva_mux, msm_dig_cdc_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + /* Sidetone */ + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_PGA_E("IIR1", MSM89XX_CDC_CORE_CLK_SD_CTL, 0, 0, NULL, 0, + msm_dig_cdc_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux), + SND_SOC_DAPM_PGA_E("IIR2", MSM89XX_CDC_CORE_CLK_SD_CTL, 1, 0, NULL, 0, + msm_dig_cdc_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", + MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", + MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 4, 0, NULL, 0), + + + SND_SOC_DAPM_MUX("I2S TX2 INP1", SND_SOC_NOPM, 0, 0, + &i2s_tx2_inp1_mux), + SND_SOC_DAPM_MUX("I2S TX2 INP2", SND_SOC_NOPM, 0, 0, + &i2s_tx2_inp2_mux), + SND_SOC_DAPM_MUX("I2S TX3 INP2", SND_SOC_NOPM, 0, 0, + &i2s_tx3_inp2_mux), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0, + msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0, + msm_dig_cdc_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("ADC1_IN"), + SND_SOC_DAPM_INPUT("ADC2_IN"), + SND_SOC_DAPM_INPUT("ADC3_IN"), + SND_SOC_DAPM_OUTPUT("PDM_OUT_RX1"), + SND_SOC_DAPM_OUTPUT("PDM_OUT_RX2"), + SND_SOC_DAPM_OUTPUT("PDM_OUT_RX3"), +}; + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX1_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX2_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec3_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX3_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_dec4_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX4_MUX_CTL, 4, 3, cf_text); + +static const struct soc_enum cf_decsva_enum = + SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX5_MUX_CTL, 4, 3, cf_text); + +static const struct snd_kcontrol_new msm_dig_snd_controls[] = { + SOC_SINGLE_SX_TLV("DEC1 Volume", + MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", + MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC3 Volume", + MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC4 Volume", + MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC5 Volume", + MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP4 Volume", + MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR2 INP1 Volume", + MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("RX1 Digital Volume", + MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Digital Volume", + MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Digital Volume", + MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + + SOC_SINGLE_EXT("IIR2 Enable Band1", IIR2, BAND1, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band2", IIR2, BAND2, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band3", IIR2, BAND3, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band4", IIR2, BAND4, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR2 Enable Band5", IIR2, BAND5, 1, 0, + msm_dig_cdc_get_iir_enable_audio_mixer, + msm_dig_cdc_put_iir_enable_audio_mixer), + + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + + SOC_SINGLE_MULTI_EXT("IIR2 Band1", IIR2, BAND1, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band2", IIR2, BAND2, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band3", IIR2, BAND3, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band4", IIR2, BAND4, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5, + msm_dig_cdc_get_iir_band_audio_mixer, + msm_dig_cdc_put_iir_band_audio_mixer), + + SOC_SINGLE("RX1 HPF Switch", + MSM89XX_CDC_CORE_RX1_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX2 HPF Switch", + MSM89XX_CDC_CORE_RX2_B5_CTL, 2, 1, 0), + SOC_SINGLE("RX3 HPF Switch", + MSM89XX_CDC_CORE_RX3_B5_CTL, 2, 1, 0), + + SOC_ENUM("RX1 HPF cut off", cf_rxmix1_enum), + SOC_ENUM("RX2 HPF cut off", cf_rxmix2_enum), + SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum), + + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_ENUM("TX3 HPF cut off", cf_dec3_enum), + SOC_ENUM("TX4 HPF cut off", cf_dec4_enum), + SOC_ENUM("TX5 HPF cut off", cf_decsva_enum), + SOC_SINGLE("TX1 HPF Switch", + MSM89XX_CDC_CORE_TX1_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX2 HPF Switch", + MSM89XX_CDC_CORE_TX2_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX3 HPF Switch", + MSM89XX_CDC_CORE_TX3_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX4 HPF Switch", + MSM89XX_CDC_CORE_TX4_MUX_CTL, 3, 1, 0), + SOC_SINGLE("TX5 HPF Switch", + MSM89XX_CDC_CORE_TX5_MUX_CTL, 3, 1, 0), +}; + +static int msm_dig_cdc_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = NULL; + u16 tx_vol_ctl_reg = 0; + u8 decimator = 0, i; + struct msm_dig_priv *dig_cdc; + + pr_debug("%s: Digital Mute val = %d\n", __func__, mute); + + if (!dai || !dai->codec) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + codec = dai->codec; + dig_cdc = snd_soc_codec_get_drvdata(codec); + + if (dai->id == AIF1_PB) { + dev_dbg(codec->dev, "%s: Not capture use case skip\n", + __func__); + return 0; + } + + mute = (mute) ? 1 : 0; + if (!mute) { + /* + * 15 ms is an emperical value for the mute time + * that was arrived by checking the pop level + * to be inaudible + */ + usleep_range(15000, 15010); + } + + if (dai->id == AIF3_SVA) { + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG, 0x01, mute); + goto ret; + } + for (i = 0; i < (NUM_DECIMATORS - 1); i++) { + if (dig_cdc->dec_active[i]) + decimator = i + 1; + if (decimator && decimator < NUM_DECIMATORS) { + /* mute/unmute decimators corresponding to Tx DAI's */ + tx_vol_ctl_reg = + MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG + + 32 * (decimator - 1); + snd_soc_update_bits(codec, tx_vol_ctl_reg, + 0x01, mute); + } + decimator = 0; + } +ret: + return 0; +} + +static struct snd_soc_dai_ops msm_dig_dai_ops = { + .hw_params = msm_dig_cdc_hw_params, + .digital_mute = msm_dig_cdc_digital_mute, +}; + + +static struct snd_soc_dai_driver msm_codec_dais[] = { + { + .name = "msm_dig_cdc_dai_rx1", + .id = AIF1_PB, + .playback = { /* Support maximum range */ + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_max = 192000, + .rate_min = 8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, + }, + .ops = &msm_dig_dai_ops, + }, + { + .name = "msm_dig_cdc_dai_tx1", + .id = AIF1_CAP, + .capture = { /* Support maximum range */ + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &msm_dig_dai_ops, + }, + { + .name = "msm_dig_cdc_dai_tx2", + .id = AIF3_SVA, + .capture = { /* Support maximum range */ + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &msm_dig_dai_ops, + }, + { + .name = "msm_dig_cdc_dai_vifeed", + .id = AIF2_VIFEED, + .capture = { /* Support maximum range */ + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &msm_dig_dai_ops, + }, +}; + +static struct regmap *msm_digital_get_regmap(struct device *dev) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(dev); + + return msm_dig_cdc->regmap; +} + +static int msm_dig_cdc_suspend(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + + msm_dig_cdc->dapm_bias_off = 1; + return 0; +} + +static int msm_dig_cdc_resume(struct snd_soc_codec *codec) +{ + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); + + msm_dig_cdc->dapm_bias_off = 0; + return 0; +} + +static struct snd_soc_codec_driver soc_msm_dig_codec = { + .probe = msm_dig_cdc_soc_probe, + .remove = msm_dig_cdc_soc_remove, + .suspend = msm_dig_cdc_suspend, + .resume = msm_dig_cdc_resume, + .controls = msm_dig_snd_controls, + .num_controls = ARRAY_SIZE(msm_dig_snd_controls), + .dapm_widgets = msm_dig_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm_dig_dapm_widgets), + .dapm_routes = audio_dig_map, + .num_dapm_routes = ARRAY_SIZE(audio_dig_map), + .get_regmap = msm_digital_get_regmap, +}; + +const struct regmap_config msm_digital_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 8, + .lock = enable_digital_callback, + .unlock = disable_digital_callback, + .cache_type = REGCACHE_FLAT, + .reg_defaults = msm89xx_cdc_core_defaults, + .num_reg_defaults = MSM89XX_CDC_CORE_MAX_REGISTER, + .writeable_reg = msm89xx_cdc_core_writeable_reg, + .readable_reg = msm89xx_cdc_core_readable_reg, + .volatile_reg = msm89xx_cdc_core_volatile_reg, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .max_register = MSM89XX_CDC_CORE_MAX_REGISTER, +}; + +static int msm_dig_cdc_probe(struct platform_device *pdev) +{ + int ret; + u32 dig_cdc_addr; + struct msm_dig_priv *msm_dig_cdc; + struct dig_ctrl_platform_data *pdata; + + msm_dig_cdc = devm_kzalloc(&pdev->dev, sizeof(struct msm_dig_priv), + GFP_KERNEL); + if (!msm_dig_cdc) + return -ENOMEM; + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "%s: pdata from parent is NULL\n", + __func__); + ret = -EINVAL; + goto rtn; + } + + ret = of_property_read_u32(pdev->dev.of_node, "reg", + &dig_cdc_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + return ret; + } + + msm_dig_cdc->dig_base = ioremap(dig_cdc_addr, + MSM89XX_CDC_CORE_MAX_REGISTER); + if (msm_dig_cdc->dig_base == NULL) { + dev_err(&pdev->dev, "%s ioremap failed\n", __func__); + return -ENOMEM; + } + msm_dig_cdc->regmap = + devm_regmap_init_mmio_clk(&pdev->dev, NULL, + msm_dig_cdc->dig_base, &msm_digital_regmap_config); + + msm_dig_cdc->update_clkdiv = pdata->update_clkdiv; + msm_dig_cdc->get_cdc_version = pdata->get_cdc_version; + msm_dig_cdc->handle = pdata->handle; + msm_dig_cdc->register_notifier = pdata->register_notifier; + + dev_set_drvdata(&pdev->dev, msm_dig_cdc); + snd_soc_register_codec(&pdev->dev, &soc_msm_dig_codec, + msm_codec_dais, ARRAY_SIZE(msm_codec_dais)); + dev_dbg(&pdev->dev, "%s: registered DIG CODEC 0x%x\n", + __func__, dig_cdc_addr); +rtn: + return ret; +} + +static int msm_dig_cdc_remove(struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +#ifdef CONFIG_PM +static int msm_dig_suspend(struct device *dev) +{ + struct msm_asoc_mach_data *pdata; + struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(dev); + + if (!registered_digcodec || !msm_dig_cdc) { + pr_debug("%s:digcodec not initialized, return\n", __func__); + return 0; + } + pdata = snd_soc_card_get_drvdata(registered_digcodec->component.card); + if (!pdata) { + pr_debug("%s:card not initialized, return\n", __func__); + return 0; + } + if (msm_dig_cdc->dapm_bias_off) { + pr_debug("%s: mclk cnt = %d, mclk_enabled = %d\n", + __func__, atomic_read(&pdata->int_mclk0_rsc_ref), + atomic_read(&pdata->int_mclk0_enabled)); + + if (atomic_read(&pdata->int_mclk0_enabled) == true) { + cancel_delayed_work_sync( + &pdata->disable_int_mclk0_work); + mutex_lock(&pdata->cdc_int_mclk0_mutex); + pdata->digital_cdc_core_clk.enable = 0; + afe_set_lpass_clock_v2(AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + atomic_set(&pdata->int_mclk0_enabled, false); + mutex_unlock(&pdata->cdc_int_mclk0_mutex); + } + } + + return 0; +} + +static int msm_dig_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops msm_dig_pm_ops = { + .suspend_late = msm_dig_suspend, + .resume_early = msm_dig_resume, +}; +#endif + +static const struct of_device_id msm_dig_cdc_of_match[] = { + {.compatible = "qcom,msm-digital-codec"}, + {}, +}; + +static struct platform_driver msm_digcodec_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .of_match_table = msm_dig_cdc_of_match, +#ifdef CONFIG_PM + .pm = &msm_dig_pm_ops, +#endif + }, + .probe = msm_dig_cdc_probe, + .remove = msm_dig_cdc_remove, +}; +module_platform_driver(msm_digcodec_driver); + +MODULE_DESCRIPTION("MSM Audio Digital codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h new file mode 100644 index 000000000000..f0e7a9cf9228 --- /dev/null +++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h @@ -0,0 +1,91 @@ +/* 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 MSM_DIGITAL_CDC_H +#define MSM_DIGITAL_CDC_H + +#define HPHL_PA_DISABLE (0x01 << 1) +#define HPHR_PA_DISABLE (0x01 << 2) +#define SPKR_PA_DISABLE (0x01 << 3) + +#define NUM_DECIMATORS 5 +/* Codec supports 1 compander */ +enum { + COMPANDER_NONE = 0, + COMPANDER_1, /* HPHL/R */ + COMPANDER_MAX, +}; + +/* Number of output I2S port */ +enum { + MSM89XX_RX1 = 0, + MSM89XX_RX2, + MSM89XX_RX3, + MSM89XX_RX_MAX, +}; + +struct msm_dig_priv { + struct snd_soc_codec *codec; + u32 comp_enabled[MSM89XX_RX_MAX]; + int (*codec_hph_comp_gpio)(bool enable, struct snd_soc_codec *codec); + s32 dmic_1_2_clk_cnt; + s32 dmic_3_4_clk_cnt; + bool dec_active[NUM_DECIMATORS]; + int version; + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + char __iomem *dig_base; + struct regmap *regmap; + struct notifier_block nblock; + u32 mute_mask; + int dapm_bias_off; + void *handle; + void (*update_clkdiv)(void *handle, int val); + int (*get_cdc_version)(void *handle); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); +}; + +struct dig_ctrl_platform_data { + void *handle; + void (*update_clkdiv)(void *handle, int val); + int (*get_cdc_version)(void *handle); + int (*register_notifier)(void *handle, + struct notifier_block *nblock, + bool enable); +}; + +struct hpf_work { + struct msm_dig_priv *dig_cdc; + u32 decimator; + u8 tx_hpf_cut_of_freq; + struct delayed_work dwork; +}; + +/* Codec supports 5 bands */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +extern void msm_dig_cdc_hph_comp_cb( + int (*codec_hph_comp_gpio)( + bool enable, struct snd_soc_codec *codec), + struct snd_soc_codec *codec); +int msm_dig_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +#endif diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.c b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.c new file mode 100644 index 000000000000..ee4ec34fa2d7 --- /dev/null +++ b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.c @@ -0,0 +1,413 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include "msm-analog-cdc.h" +#include "sdm660-cdc-irq.h" +#include "sdm660-cdc-registers.h" + +#define MAX_NUM_IRQS 14 +#define NUM_IRQ_REGS 2 +#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 700 + +#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE)) +#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) + +static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data); + +char *irq_names[MAX_NUM_IRQS] = { + "spk_cnp_int", + "spk_clip_int", + "spk_ocp_int", + "ins_rem_det1", + "but_rel_det", + "but_press_det", + "ins_rem_det", + "mbhc_int", + "ear_ocp_int", + "hphr_ocp_int", + "hphl_ocp_det", + "ear_cnp_int", + "hphr_cnp_int", + "hphl_cnp_int" +}; + +int order[MAX_NUM_IRQS] = { + MSM89XX_IRQ_SPKR_CNP, + MSM89XX_IRQ_SPKR_CLIP, + MSM89XX_IRQ_SPKR_OCP, + MSM89XX_IRQ_MBHC_INSREM_DET1, + MSM89XX_IRQ_MBHC_RELEASE, + MSM89XX_IRQ_MBHC_PRESS, + MSM89XX_IRQ_MBHC_INSREM_DET, + MSM89XX_IRQ_MBHC_HS_DET, + MSM89XX_IRQ_EAR_OCP, + MSM89XX_IRQ_HPHR_OCP, + MSM89XX_IRQ_HPHL_OCP, + MSM89XX_IRQ_EAR_CNP, + MSM89XX_IRQ_HPHR_CNP, + MSM89XX_IRQ_HPHL_CNP, +}; + +enum wcd9xxx_spmi_pm_state { + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE, + WCD9XXX_PM_ASLEEP, +}; + +struct wcd9xxx_spmi_map { + uint8_t handled[NUM_IRQ_REGS]; + uint8_t mask[NUM_IRQ_REGS]; + int linuxirq[MAX_NUM_IRQS]; + irq_handler_t handler[MAX_NUM_IRQS]; + struct platform_device *spmi[NUM_IRQ_REGS]; + struct snd_soc_codec *codec; + + enum wcd9xxx_spmi_pm_state pm_state; + struct mutex pm_lock; + /* pm_wq notifies change of pm_state */ + wait_queue_head_t pm_wq; + struct pm_qos_request pm_qos_req; + int wlock_holders; +}; + +struct wcd9xxx_spmi_map map; + +void wcd9xxx_spmi_enable_irq(int irq) +{ + pr_debug("%s: irqno =%d\n", __func__, irq); + + if (!(map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq)))) + return; + + map.mask[BIT_BYTE(irq)] &= + ~(BYTE_BIT_MASK(irq)); + + enable_irq(map.linuxirq[irq]); +} + +void wcd9xxx_spmi_disable_irq(int irq) +{ + pr_debug("%s: irqno =%d\n", __func__, irq); + + if (map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq))) + return; + + map.mask[BIT_BYTE(irq)] |= + (BYTE_BIT_MASK(irq)); + + disable_irq_nosync(map.linuxirq[irq]); +} + +int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler, + const char *name, void *priv) +{ + int rc; + unsigned long irq_flags; + + map.linuxirq[irq] = + platform_get_irq_byname(map.spmi[BIT_BYTE(irq)], + irq_names[irq]); + + if (strcmp(name, "mbhc sw intr")) + irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT; + else + irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT | IRQF_NO_SUSPEND; + pr_debug("%s: name:%s irq_flags = %lx\n", __func__, name, irq_flags); + + rc = devm_request_threaded_irq(&map.spmi[BIT_BYTE(irq)]->dev, + map.linuxirq[irq], NULL, + wcd9xxx_spmi_irq_handler, + irq_flags, + name, priv); + if (rc < 0) { + dev_err(&map.spmi[BIT_BYTE(irq)]->dev, + "Can't request %d IRQ\n", irq); + return rc; + } + + dev_dbg(&map.spmi[BIT_BYTE(irq)]->dev, + "irq %d linuxIRQ: %d\n", irq, map.linuxirq[irq]); + map.mask[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq); + map.handler[irq] = handler; + enable_irq_wake(map.linuxirq[irq]); + return 0; +} + +int wcd9xxx_spmi_free_irq(int irq, void *priv) +{ + devm_free_irq(&map.spmi[BIT_BYTE(irq)]->dev, map.linuxirq[irq], + priv); + map.mask[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq); + return 0; +} + +static int get_irq_bit(int linux_irq) +{ + int i = 0; + + for (; i < MAX_NUM_IRQS; i++) + if (map.linuxirq[i] == linux_irq) + return i; + + return i; +} + +static int get_order_irq(int i) +{ + return order[i]; +} + +static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data) +{ + int irq, i, j; + unsigned long status[NUM_IRQ_REGS] = {0}; + + if (unlikely(wcd9xxx_spmi_lock_sleep() == false)) { + pr_err("Failed to hold suspend\n"); + return IRQ_NONE; + } + + irq = get_irq_bit(linux_irq); + if (irq == MAX_NUM_IRQS) + return IRQ_HANDLED; + + status[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq); + for (i = 0; i < NUM_IRQ_REGS; i++) { + status[i] |= snd_soc_read(map.codec, + BIT_BYTE(irq) * 0x100 + + MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS); + status[i] &= ~map.mask[i]; + } + for (i = 0; i < MAX_NUM_IRQS; i++) { + j = get_order_irq(i); + if ((status[BIT_BYTE(j)] & BYTE_BIT_MASK(j)) && + ((map.handled[BIT_BYTE(j)] & + BYTE_BIT_MASK(j)) == 0)) { + map.handler[j](irq, data); + map.handled[BIT_BYTE(j)] |= + BYTE_BIT_MASK(j); + } + } + map.handled[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq); + wcd9xxx_spmi_unlock_sleep(); + + return IRQ_HANDLED; +} + +enum wcd9xxx_spmi_pm_state wcd9xxx_spmi_pm_cmpxchg( + enum wcd9xxx_spmi_pm_state o, + enum wcd9xxx_spmi_pm_state n) +{ + enum wcd9xxx_spmi_pm_state old; + + mutex_lock(&map.pm_lock); + old = map.pm_state; + if (old == o) + map.pm_state = n; + pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state); + mutex_unlock(&map.pm_lock); + return old; +} +EXPORT_SYMBOL(wcd9xxx_spmi_pm_cmpxchg); + +int wcd9xxx_spmi_suspend(pm_message_t pmesg) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + /* + * pm_qos_update_request() can be called after this suspend chain call + * started. thus suspend can be called while lock is being held + */ + mutex_lock(&map.pm_lock); + if (map.pm_state == WCD9XXX_PM_SLEEPABLE) { + pr_debug("%s: suspending system, state %d, wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + map.pm_state = WCD9XXX_PM_ASLEEP; + } else if (map.pm_state == WCD9XXX_PM_AWAKE) { + /* + * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE + * then set to WCD9XXX_PM_ASLEEP + */ + pr_debug("%s: waiting to suspend system, state %d, wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + mutex_unlock(&map.pm_lock); + if (!(wait_event_timeout(map.pm_wq, + wcd9xxx_spmi_pm_cmpxchg( + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_ASLEEP) == + WCD9XXX_PM_SLEEPABLE, + HZ))) { + pr_debug("%s: suspend failed state %d, wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + ret = -EBUSY; + } else { + pr_debug("%s: done, state %d, wlock %d\n", __func__, + map.pm_state, + map.wlock_holders); + } + mutex_lock(&map.pm_lock); + } else if (map.pm_state == WCD9XXX_PM_ASLEEP) { + pr_warn("%s: system is already suspended, state %d, wlock %dn", + __func__, map.pm_state, + map.wlock_holders); + } + mutex_unlock(&map.pm_lock); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_spmi_suspend); + +int wcd9xxx_spmi_resume(void) +{ + int ret = 0; + + pr_debug("%s: enter\n", __func__); + mutex_lock(&map.pm_lock); + if (map.pm_state == WCD9XXX_PM_ASLEEP) { + pr_debug("%s: resuming system, state %d, wlock %d\n", __func__, + map.pm_state, + map.wlock_holders); + map.pm_state = WCD9XXX_PM_SLEEPABLE; + } else { + pr_warn("%s: system is already awake, state %d wlock %d\n", + __func__, map.pm_state, + map.wlock_holders); + } + mutex_unlock(&map.pm_lock); + wake_up_all(&map.pm_wq); + + return ret; +} +EXPORT_SYMBOL(wcd9xxx_spmi_resume); + +bool wcd9xxx_spmi_lock_sleep(void) +{ + /* + * wcd9xxx_spmi_{lock/unlock}_sleep will be called by + * wcd9xxx_spmi_irq_thread + * and its subroutines only motly. + * but btn0_lpress_fn is not wcd9xxx_spmi_irq_thread's subroutine and + * It can race with wcd9xxx_spmi_irq_thread. + * So need to embrace wlock_holders with mutex. + */ + mutex_lock(&map.pm_lock); + if (map.wlock_holders++ == 0) { + pr_debug("%s: holding wake lock\n", __func__); + pm_qos_update_request(&map.pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + pm_stay_awake(&map.spmi[0]->dev); + } + mutex_unlock(&map.pm_lock); + pr_debug("%s: wake lock counter %d\n", __func__, + map.wlock_holders); + pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state); + + if (!wait_event_timeout(map.pm_wq, + ((wcd9xxx_spmi_pm_cmpxchg( + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE)) == + WCD9XXX_PM_SLEEPABLE || + (wcd9xxx_spmi_pm_cmpxchg( + WCD9XXX_PM_SLEEPABLE, + WCD9XXX_PM_AWAKE) == + WCD9XXX_PM_AWAKE)), + msecs_to_jiffies( + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) { + pr_warn("%s: system didn't resume within %dms, s %d, w %d\n", + __func__, + WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, map.pm_state, + map.wlock_holders); + wcd9xxx_spmi_unlock_sleep(); + return false; + } + wake_up_all(&map.pm_wq); + pr_debug("%s: leaving pm_state = %d\n", __func__, map.pm_state); + return true; +} +EXPORT_SYMBOL(wcd9xxx_spmi_lock_sleep); + +void wcd9xxx_spmi_unlock_sleep(void) +{ + mutex_lock(&map.pm_lock); + if (--map.wlock_holders == 0) { + pr_debug("%s: releasing wake lock pm_state %d -> %d\n", + __func__, map.pm_state, WCD9XXX_PM_SLEEPABLE); + /* + * if wcd9xxx_spmi_lock_sleep failed, pm_state would be still + * WCD9XXX_PM_ASLEEP, don't overwrite + */ + if (likely(map.pm_state == WCD9XXX_PM_AWAKE)) + map.pm_state = WCD9XXX_PM_SLEEPABLE; + pm_qos_update_request(&map.pm_qos_req, + PM_QOS_DEFAULT_VALUE); + pm_relax(&map.spmi[0]->dev); + } + mutex_unlock(&map.pm_lock); + pr_debug("%s: wake lock counter %d\n", __func__, + map.wlock_holders); + pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state); + wake_up_all(&map.pm_wq); +} +EXPORT_SYMBOL(wcd9xxx_spmi_unlock_sleep); + +void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec) +{ + map.codec = codec; +} + +void wcd9xxx_spmi_set_dev(struct platform_device *spmi, int i) +{ + if (i < NUM_IRQ_REGS) + map.spmi[i] = spmi; +} + +int wcd9xxx_spmi_irq_init(void) +{ + int i = 0; + + for (; i < MAX_NUM_IRQS; i++) + map.mask[BIT_BYTE(i)] |= BYTE_BIT_MASK(i); + mutex_init(&map.pm_lock); + map.wlock_holders = 0; + map.pm_state = WCD9XXX_PM_SLEEPABLE; + init_waitqueue_head(&map.pm_wq); + pm_qos_add_request(&map.pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + + return 0; +} + +MODULE_DESCRIPTION("MSM8x16 SPMI IRQ driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.h b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.h new file mode 100644 index 000000000000..d0f48d0c2af2 --- /dev/null +++ b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.h @@ -0,0 +1,34 @@ +/* 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 __WCD9XXX_SPMI_IRQ_H__ +#define __WCD9XXX_SPMI_IRQ_H__ + +#include +#include +#include +#include +#include + +extern void wcd9xxx_spmi_enable_irq(int irq); +extern void wcd9xxx_spmi_disable_irq(int irq); +extern int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler, + const char *name, void *priv); +extern int wcd9xxx_spmi_free_irq(int irq, void *priv); +extern void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec); +extern void wcd9xxx_spmi_set_dev(struct platform_device *spmi, int i); +extern int wcd9xxx_spmi_irq_init(void); +extern int wcd9xxx_spmi_suspend(pm_message_t pmesg); +extern int wcd9xxx_spmi_resume(void); +bool wcd9xxx_spmi_lock_sleep(void); +void wcd9xxx_spmi_unlock_sleep(void); + +#endif diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-cdc-registers.h b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-registers.h new file mode 100644 index 000000000000..1317ce169755 --- /dev/null +++ b/sound/soc/codecs/sdm660_cdc/sdm660-cdc-registers.h @@ -0,0 +1,603 @@ +/* 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 SDM660_WCD_REGISTERS_H +#define SDM660_WCD_REGISTERS_H + +#define CDC_DIG_BASE 0xF000 +#define CDC_ANA_BASE 0xF100 + +#define MSM89XX_PMIC_DIGITAL_REVISION1 (CDC_DIG_BASE+0x000) +#define MSM89XX_PMIC_DIGITAL_REVISION1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_REVISION2 (CDC_DIG_BASE+0x001) +#define MSM89XX_PMIC_DIGITAL_REVISION2__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PERPH_TYPE (CDC_DIG_BASE+0x004) +#define MSM89XX_PMIC_DIGITAL_PERPH_TYPE__POR (0x23) +#define MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE (CDC_DIG_BASE+0x005) +#define MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE__POR (0x01) +#define MSM89XX_PMIC_DIGITAL_INT_RT_STS (CDC_DIG_BASE+0x010) +#define MSM89XX_PMIC_DIGITAL_INT_RT_STS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_SET_TYPE (CDC_DIG_BASE+0x011) +#define MSM89XX_PMIC_DIGITAL_INT_SET_TYPE__POR (0xFF) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH (CDC_DIG_BASE+0x012) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH__POR (0xFF) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW (CDC_DIG_BASE+0x013) +#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR (CDC_DIG_BASE+0x014) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_EN_SET (CDC_DIG_BASE+0x015) +#define MSM89XX_PMIC_DIGITAL_INT_EN_SET__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_EN_CLR (CDC_DIG_BASE+0x016) +#define MSM89XX_PMIC_DIGITAL_INT_EN_CLR__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS (CDC_DIG_BASE+0x018) +#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_PENDING_STS (CDC_DIG_BASE+0x019) +#define MSM89XX_PMIC_DIGITAL_INT_PENDING_STS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_MID_SEL (CDC_DIG_BASE+0x01A) +#define MSM89XX_PMIC_DIGITAL_INT_MID_SEL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_PRIORITY (CDC_DIG_BASE+0x01B) +#define MSM89XX_PMIC_DIGITAL_INT_PRIORITY__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_GPIO_MODE (CDC_DIG_BASE+0x040) +#define MSM89XX_PMIC_DIGITAL_GPIO_MODE__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_OE (CDC_DIG_BASE+0x041) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_OE__POR (0x01) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA (CDC_DIG_BASE+0x042) +#define MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PIN_STATUS (CDC_DIG_BASE+0x043) +#define MSM89XX_PMIC_DIGITAL_PIN_STATUS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_HDRIVE_CTL (CDC_DIG_BASE+0x044) +#define MSM89XX_PMIC_DIGITAL_HDRIVE_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_RST_CTL (CDC_DIG_BASE+0x046) +#define MSM89XX_PMIC_DIGITAL_CDC_RST_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL (CDC_DIG_BASE+0x048) +#define MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL (CDC_DIG_BASE+0x049) +#define MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL (CDC_DIG_BASE+0x04A) +#define MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL (CDC_DIG_BASE+0x050) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL__POR (0x02) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL (CDC_DIG_BASE+0x051) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL__POR (0x02) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL (CDC_DIG_BASE+0x052) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL (CDC_DIG_BASE+0x053) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL (CDC_DIG_BASE+0x054) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL (CDC_DIG_BASE+0x055) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL (CDC_DIG_BASE+0x056) +#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1 (CDC_DIG_BASE+0x058) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1__POR (0x7C) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2 (CDC_DIG_BASE+0x059) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2__POR (0x7C) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3 (CDC_DIG_BASE+0x05A) +#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3__POR (0x7C) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0 (CDC_DIG_BASE+0x05B) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1 (CDC_DIG_BASE+0x05C) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2 (CDC_DIG_BASE+0x05D) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3 (CDC_DIG_BASE+0x05E) +#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL (CDC_DIG_BASE+0x068) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN (CDC_DIG_BASE+0x069) +#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SPARE_0 (CDC_DIG_BASE+0x070) +#define MSM89XX_PMIC_DIGITAL_SPARE_0__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SPARE_1 (CDC_DIG_BASE+0x071) +#define MSM89XX_PMIC_DIGITAL_SPARE_1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SPARE_2 (CDC_DIG_BASE+0x072) +#define MSM89XX_PMIC_DIGITAL_SPARE_2__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_SEC_ACCESS (CDC_DIG_BASE+0x0D0) +#define MSM89XX_PMIC_DIGITAL_SEC_ACCESS__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1 (CDC_DIG_BASE+0x0D8) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2 (CDC_DIG_BASE+0x0D9) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2__POR (0x01) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3 (CDC_DIG_BASE+0x0DA) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3__POR (0x05) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4 (CDC_DIG_BASE+0x0DB) +#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_TEST1 (CDC_DIG_BASE+0x0E0) +#define MSM89XX_PMIC_DIGITAL_INT_TEST1__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_INT_TEST_VAL (CDC_DIG_BASE+0x0E1) +#define MSM89XX_PMIC_DIGITAL_INT_TEST_VAL__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_TRIM_NUM (CDC_DIG_BASE+0x0F0) +#define MSM89XX_PMIC_DIGITAL_TRIM_NUM__POR (0x00) +#define MSM89XX_PMIC_DIGITAL_TRIM_CTRL (CDC_DIG_BASE+0x0F1) +#define MSM89XX_PMIC_DIGITAL_TRIM_CTRL__POR (0x00) + +#define MSM89XX_PMIC_ANALOG_REVISION1 (CDC_ANA_BASE+0x00) +#define MSM89XX_PMIC_ANALOG_REVISION1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_REVISION2 (CDC_ANA_BASE+0x01) +#define MSM89XX_PMIC_ANALOG_REVISION2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_REVISION3 (CDC_ANA_BASE+0x02) +#define MSM89XX_PMIC_ANALOG_REVISION3__POR (0x00) +#define MSM89XX_PMIC_ANALOG_REVISION4 (CDC_ANA_BASE+0x03) +#define MSM89XX_PMIC_ANALOG_REVISION4__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PERPH_TYPE (CDC_ANA_BASE+0x04) +#define MSM89XX_PMIC_ANALOG_PERPH_TYPE__POR (0x23) +#define MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE (CDC_ANA_BASE+0x05) +#define MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE__POR (0x09) +#define MSM89XX_PMIC_ANALOG_INT_RT_STS (CDC_ANA_BASE+0x10) +#define MSM89XX_PMIC_ANALOG_INT_RT_STS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_SET_TYPE (CDC_ANA_BASE+0x11) +#define MSM89XX_PMIC_ANALOG_INT_SET_TYPE__POR (0x3F) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH (CDC_ANA_BASE+0x12) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH__POR (0x3F) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW (CDC_ANA_BASE+0x13) +#define MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR (CDC_ANA_BASE+0x14) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_EN_SET (CDC_ANA_BASE+0x15) +#define MSM89XX_PMIC_ANALOG_INT_EN_SET__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_EN_CLR (CDC_ANA_BASE+0x16) +#define MSM89XX_PMIC_ANALOG_INT_EN_CLR__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_STS (CDC_ANA_BASE+0x18) +#define MSM89XX_PMIC_ANALOG_INT_LATCHED_STS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_PENDING_STS (CDC_ANA_BASE+0x19) +#define MSM89XX_PMIC_ANALOG_INT_PENDING_STS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_MID_SEL (CDC_ANA_BASE+0x1A) +#define MSM89XX_PMIC_ANALOG_INT_MID_SEL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_PRIORITY (CDC_ANA_BASE+0x1B) +#define MSM89XX_PMIC_ANALOG_INT_PRIORITY__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MICB_1_EN (CDC_ANA_BASE+0x40) +#define MSM89XX_PMIC_ANALOG_MICB_1_EN__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MICB_1_VAL (CDC_ANA_BASE+0x41) +#define MSM89XX_PMIC_ANALOG_MICB_1_VAL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_MICB_1_CTL (CDC_ANA_BASE+0x42) +#define MSM89XX_PMIC_ANALOG_MICB_1_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS (CDC_ANA_BASE+0x43) +#define MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS__POR (0x49) +#define MSM89XX_PMIC_ANALOG_MICB_2_EN (CDC_ANA_BASE+0x44) +#define MSM89XX_PMIC_ANALOG_MICB_2_EN__POR (0x20) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2 (CDC_ANA_BASE+0x45) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL (CDC_ANA_BASE+0x46) +#define MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1 (CDC_ANA_BASE+0x47) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1__POR (0x35) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2 (CDC_ANA_BASE+0x50) +#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2__POR (0x08) +#define MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL (CDC_ANA_BASE+0x51) +#define MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER (CDC_ANA_BASE+0x52) +#define MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER__POR (0x98) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL (CDC_ANA_BASE+0x53) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL (CDC_ANA_BASE+0x54) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL (CDC_ANA_BASE+0x55) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL__POR (0x40) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL (CDC_ANA_BASE+0x56) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL__POR (0x61) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL (CDC_ANA_BASE+0x57) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL__POR (0x80) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT (CDC_ANA_BASE+0x58) +#define MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT__POR (0x00) +#define MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT (CDC_ANA_BASE+0x59) +#define MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TX_1_EN (CDC_ANA_BASE+0x60) +#define MSM89XX_PMIC_ANALOG_TX_1_EN__POR (0x03) +#define MSM89XX_PMIC_ANALOG_TX_2_EN (CDC_ANA_BASE+0x61) +#define MSM89XX_PMIC_ANALOG_TX_2_EN__POR (0x03) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1 (CDC_ANA_BASE+0x62) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1__POR (0xBF) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2 (CDC_ANA_BASE+0x63) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2__POR (0x8C) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL (CDC_ANA_BASE+0x64) +#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS (CDC_ANA_BASE+0x65) +#define MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS__POR (0x6B) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV (CDC_ANA_BASE+0x66) +#define MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV__POR (0x51) +#define MSM89XX_PMIC_ANALOG_TX_3_EN (CDC_ANA_BASE+0x67) +#define MSM89XX_PMIC_ANALOG_TX_3_EN__POR (0x02) +#define MSM89XX_PMIC_ANALOG_NCP_EN (CDC_ANA_BASE+0x80) +#define MSM89XX_PMIC_ANALOG_NCP_EN__POR (0x26) +#define MSM89XX_PMIC_ANALOG_NCP_CLK (CDC_ANA_BASE+0x81) +#define MSM89XX_PMIC_ANALOG_NCP_CLK__POR (0x23) +#define MSM89XX_PMIC_ANALOG_NCP_DEGLITCH (CDC_ANA_BASE+0x82) +#define MSM89XX_PMIC_ANALOG_NCP_DEGLITCH__POR (0x5B) +#define MSM89XX_PMIC_ANALOG_NCP_FBCTRL (CDC_ANA_BASE+0x83) +#define MSM89XX_PMIC_ANALOG_NCP_FBCTRL__POR (0x08) +#define MSM89XX_PMIC_ANALOG_NCP_BIAS (CDC_ANA_BASE+0x84) +#define MSM89XX_PMIC_ANALOG_NCP_BIAS__POR (0x29) +#define MSM89XX_PMIC_ANALOG_NCP_VCTRL (CDC_ANA_BASE+0x85) +#define MSM89XX_PMIC_ANALOG_NCP_VCTRL__POR (0x24) +#define MSM89XX_PMIC_ANALOG_NCP_TEST (CDC_ANA_BASE+0x86) +#define MSM89XX_PMIC_ANALOG_NCP_TEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR (CDC_ANA_BASE+0x87) +#define MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR__POR (0xD5) +#define MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER (CDC_ANA_BASE+0x90) +#define MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER__POR (0xE8) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL (CDC_ANA_BASE+0x91) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL__POR (0xCF) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT (CDC_ANA_BASE+0x92) +#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT__POR (0x6E) +#define MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC (CDC_ANA_BASE+0x93) +#define MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC__POR (0x18) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA (CDC_ANA_BASE+0x94) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA__POR (0x5A) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP (CDC_ANA_BASE+0x95) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP__POR (0x69) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP (CDC_ANA_BASE+0x96) +#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP__POR (0x29) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN (CDC_ANA_BASE+0x97) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN__POR (0x80) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL (CDC_ANA_BASE+0x98) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL__POR (0xDA) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME (CDC_ANA_BASE+0x99) +#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME__POR (0x16) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST (CDC_ANA_BASE+0x9A) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL (CDC_ANA_BASE+0x9B) +#define MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST (CDC_ANA_BASE+0x9C) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL (CDC_ANA_BASE+0x9D) +#define MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL__POR (0x20) +#define MSM89XX_PMIC_ANALOG_RX_EAR_CTL (CDC_ANA_BASE+0x9E) +#define MSM89XX_PMIC_ANALOG_RX_EAR_CTL___POR (0x12) +#define MSM89XX_PMIC_ANALOG_RX_ATEST (CDC_ANA_BASE+0x9F) +#define MSM89XX_PMIC_ANALOG_RX_ATEST__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_HPH_STATUS (CDC_ANA_BASE+0xA0) +#define MSM89XX_PMIC_ANALOG_RX_HPH_STATUS__POR (0x0C) +#define MSM89XX_PMIC_ANALOG_RX_EAR_STATUS (CDC_ANA_BASE+0xA1) +#define MSM89XX_PMIC_ANALOG_RX_EAR_STATUS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL (CDC_ANA_BASE+0xAC) +#define MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL (CDC_ANA_BASE+0xAD) +#define MSM89XX_PMIC_ANALOG_RX_RX_LO_EN_CTL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL (CDC_ANA_BASE+0xB0) +#define MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL__POR (0x83) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET (CDC_ANA_BASE+0xB1) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET__POR (0x91) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL (CDC_ANA_BASE+0xB2) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL__POR (0x29) +#define MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET (CDC_ANA_BASE+0xB3) +#define MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET__POR (0x4D) +#define MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL (CDC_ANA_BASE+0xB4) +#define MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL__POR (0xE1) +#define MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL (CDC_ANA_BASE+0xB5) +#define MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL__POR (0x1E) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC (CDC_ANA_BASE+0xB6) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC__POR (0xCB) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG (CDC_ANA_BASE+0xB7) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG__POR (0x00) +#define MSM89XX_PMIC_ANALOG_CURRENT_LIMIT (CDC_ANA_BASE+0xC0) +#define MSM89XX_PMIC_ANALOG_CURRENT_LIMIT__POR (0x02) +#define MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE (CDC_ANA_BASE+0xC1) +#define MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE__POR (0x14) +#define MSM89XX_PMIC_ANALOG_BYPASS_MODE (CDC_ANA_BASE+0xC2) +#define MSM89XX_PMIC_ANALOG_BYPASS_MODE__POR (0x00) +#define MSM89XX_PMIC_ANALOG_BOOST_EN_CTL (CDC_ANA_BASE+0xC3) +#define MSM89XX_PMIC_ANALOG_BOOST_EN_CTL__POR (0x1F) +#define MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO (CDC_ANA_BASE+0xC4) +#define MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO__POR (0x8C) +#define MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE (CDC_ANA_BASE+0xC5) +#define MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE__POR (0xC0) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST1_1 (CDC_ANA_BASE+0xC6) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST1_1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST_2 (CDC_ANA_BASE+0xC7) +#define MSM89XX_PMIC_ANALOG_BOOST_TEST_2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS (CDC_ANA_BASE+0xC8) +#define MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS (CDC_ANA_BASE+0xC9) +#define MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR (CDC_ANA_BASE+0xCE) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL (CDC_ANA_BASE+0xCF) +#define MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_SEC_ACCESS (CDC_ANA_BASE+0xD0) +#define MSM89XX_PMIC_ANALOG_SEC_ACCESS__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1 (CDC_ANA_BASE+0xD8) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2 (CDC_ANA_BASE+0xD9) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2__POR (0x01) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3 (CDC_ANA_BASE+0xDA) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3__POR (0x05) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4 (CDC_ANA_BASE+0xDB) +#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_TEST1 (CDC_ANA_BASE+0xE0) +#define MSM89XX_PMIC_ANALOG_INT_TEST1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_INT_TEST_VAL (CDC_ANA_BASE+0xE1) +#define MSM89XX_PMIC_ANALOG_INT_TEST_VAL__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_NUM (CDC_ANA_BASE+0xF0) +#define MSM89XX_PMIC_ANALOG_TRIM_NUM__POR (0x04) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL1 (CDC_ANA_BASE+0xF1) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL1__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL2 (CDC_ANA_BASE+0xF2) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL2__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL3 (CDC_ANA_BASE+0xF3) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL3__POR (0x00) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL4 (CDC_ANA_BASE+0xF4) +#define MSM89XX_PMIC_ANALOG_TRIM_CTRL4__POR (0x00) + +#define MSM89XX_PMIC_CDC_NUM_REGISTERS \ + (MSM89XX_PMIC_ANALOG_TRIM_CTRL4+1) +#define MSM89XX_PMIC_CDC_MAX_REGISTER \ + (MSM89XX_PMIC_CDC_NUM_REGISTERS-1) +#define MSM89XX_PMIC_CDC_CACHE_SIZE \ + MSM89XX_PMIC_CDC_NUM_REGISTERS + + +#define MSM89XX_CDC_CORE_CLK_RX_RESET_CTL (0x00) +#define MSM89XX_CDC_CORE_CLK_RX_RESET_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL (0x04) +#define MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL (0x08) +#define MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_RX_I2S_CTL (0x0C) +#define MSM89XX_CDC_CORE_CLK_RX_I2S_CTL__POR (0x13) +#define MSM89XX_CDC_CORE_CLK_TX_I2S_CTL (0x10) +#define MSM89XX_CDC_CORE_CLK_TX_I2S_CTL__POR (0x13) +#define MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL (0x14) +#define MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL (0x18) +#define MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_OTHR_CTL (0x1C) +#define MSM89XX_CDC_CORE_CLK_OTHR_CTL__POR (0x04) +#define MSM89XX_CDC_CORE_CLK_RX_B1_CTL (0x20) +#define MSM89XX_CDC_CORE_CLK_RX_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_MCLK_CTL (0x24) +#define MSM89XX_CDC_CORE_CLK_MCLK_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_PDM_CTL (0x28) +#define MSM89XX_CDC_CORE_CLK_PDM_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_SD_CTL (0x2C) +#define MSM89XX_CDC_CORE_CLK_SD_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL (0x30) +#define MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_RX_B2_CTL (0x34) +#define MSM89XX_CDC_CORE_CLK_RX_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL (0x38) +#define MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL__POR (0x13) +#define MSM89XX_CDC_CORE_RX1_B1_CTL (0x40) +#define MSM89XX_CDC_CORE_RX1_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B1_CTL (0x60) +#define MSM89XX_CDC_CORE_RX2_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B1_CTL (0x80) +#define MSM89XX_CDC_CORE_RX3_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B2_CTL (0x44) +#define MSM89XX_CDC_CORE_RX1_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B2_CTL (0x64) +#define MSM89XX_CDC_CORE_RX2_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B2_CTL (0x84) +#define MSM89XX_CDC_CORE_RX3_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B3_CTL (0x48) +#define MSM89XX_CDC_CORE_RX1_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B3_CTL (0x68) +#define MSM89XX_CDC_CORE_RX2_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B3_CTL (0x88) +#define MSM89XX_CDC_CORE_RX3_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B4_CTL (0x4C) +#define MSM89XX_CDC_CORE_RX1_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B4_CTL (0x6C) +#define MSM89XX_CDC_CORE_RX2_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B4_CTL (0x8C) +#define MSM89XX_CDC_CORE_RX3_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_B5_CTL (0x50) +#define MSM89XX_CDC_CORE_RX1_B5_CTL__POR (0x68) +#define MSM89XX_CDC_CORE_RX2_B5_CTL (0x70) +#define MSM89XX_CDC_CORE_RX2_B5_CTL__POR (0x68) +#define MSM89XX_CDC_CORE_RX3_B5_CTL (0x90) +#define MSM89XX_CDC_CORE_RX3_B5_CTL__POR (0x68) +#define MSM89XX_CDC_CORE_RX1_B6_CTL (0x54) +#define MSM89XX_CDC_CORE_RX1_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_B6_CTL (0x74) +#define MSM89XX_CDC_CORE_RX2_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_B6_CTL (0x94) +#define MSM89XX_CDC_CORE_RX3_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL (0x58) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL (0x78) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL (0x98) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL (0x5C) +#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL (0x7C) +#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL (0x9C) +#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TOP_GAIN_UPDATE (0xA0) +#define MSM89XX_CDC_CORE_TOP_GAIN_UPDATE__POR (0x00) +#define MSM89XX_CDC_CORE_TOP_CTL (0xA4) +#define MSM89XX_CDC_CORE_TOP_CTL__POR (0x01) +#define MSM89XX_CDC_CORE_COMP0_B1_CTL (0xB0) +#define MSM89XX_CDC_CORE_COMP0_B1_CTL__POR (0x30) +#define MSM89XX_CDC_CORE_COMP0_B2_CTL (0xB4) +#define MSM89XX_CDC_CORE_COMP0_B2_CTL__POR (0xB5) +#define MSM89XX_CDC_CORE_COMP0_B3_CTL (0xB8) +#define MSM89XX_CDC_CORE_COMP0_B3_CTL__POR (0x28) +#define MSM89XX_CDC_CORE_COMP0_B4_CTL (0xBC) +#define MSM89XX_CDC_CORE_COMP0_B4_CTL__POR (0x37) +#define MSM89XX_CDC_CORE_COMP0_B5_CTL (0xC0) +#define MSM89XX_CDC_CORE_COMP0_B5_CTL__POR (0x7F) +#define MSM89XX_CDC_CORE_COMP0_B6_CTL (0xC4) +#define MSM89XX_CDC_CORE_COMP0_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS (0xC8) +#define MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS__POR (0x03) +#define MSM89XX_CDC_CORE_COMP0_FS_CFG (0xCC) +#define MSM89XX_CDC_CORE_COMP0_FS_CFG__POR (0x03) +#define MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL (0xD0) +#define MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL__POR (0x02) +#define MSM89XX_CDC_CORE_DEBUG_DESER1_CTL (0xE0) +#define MSM89XX_CDC_CORE_DEBUG_DESER1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_DESER2_CTL (0xE4) +#define MSM89XX_CDC_CORE_DEBUG_DESER2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG (0xE8) +#define MSM89XX_CDC_CORE_DEBUG_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG (0xEC) +#define MSM89XX_CDC_CORE_DEBUG_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG (0xF0) +#define MSM89XX_CDC_CORE_DEBUG_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL (0x100) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL (0x140) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL (0x104) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL (0x144) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL (0x108) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL (0x148) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL (0x10C) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL (0x14C) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL (0x110) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL (0x150) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL (0x114) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL (0x154) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL (0x118) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL (0x158) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL (0x11C) +#define MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL (0x15C) +#define MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_CTL (0x120) +#define MSM89XX_CDC_CORE_IIR1_CTL__POR (0x40) +#define MSM89XX_CDC_CORE_IIR2_CTL (0x160) +#define MSM89XX_CDC_CORE_IIR2_CTL__POR (0x40) +#define MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL (0x124) +#define MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL (0x164) +#define MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL (0x128) +#define MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL (0x168) +#define MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL (0x12C) +#define MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL (0x16C) +#define MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX1_B1_CTL (0x180) +#define MSM89XX_CDC_CORE_CONN_RX1_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX1_B2_CTL (0x184) +#define MSM89XX_CDC_CORE_CONN_RX1_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX1_B3_CTL (0x188) +#define MSM89XX_CDC_CORE_CONN_RX1_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX2_B1_CTL (0x18C) +#define MSM89XX_CDC_CORE_CONN_RX2_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX2_B2_CTL (0x190) +#define MSM89XX_CDC_CORE_CONN_RX2_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX2_B3_CTL (0x194) +#define MSM89XX_CDC_CORE_CONN_RX2_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX3_B1_CTL (0x198) +#define MSM89XX_CDC_CORE_CONN_RX3_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_RX3_B2_CTL (0x19C) +#define MSM89XX_CDC_CORE_CONN_RX3_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_TX_B1_CTL (0x1A0) +#define MSM89XX_CDC_CORE_CONN_TX_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_TX_B2_CTL (0x1A4) +#define MSM89XX_CDC_CORE_CONN_TX_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL (0x1A8) +#define MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL (0x1AC) +#define MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL (0x1B0) +#define MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL (0x1B4) +#define MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL (0x1B8) +#define MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL (0x1BC) +#define MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL (0x1C0) +#define MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL (0x1C4) +#define MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL (0x1C8) +#define MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_CONN_TX_B3_CTL (0x1CC) +#define MSM89XX_CDC_CORE_CONN_TX_B3_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER (0x1E0) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN (0x1E4) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG (0x1E8) +#define MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX5_MUX_CTL (0x1EC) +#define MSM89XX_CDC_CORE_TX5_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX5_CLK_FS_CTL (0x1F0) +#define MSM89XX_CDC_CORE_TX5_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX5_DMIC_CTL (0x1F4) +#define MSM89XX_CDC_CORE_TX5_DMIC_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER (0x280) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER (0x2A0) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER (0x2C0) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER (0x2E0) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN (0x284) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN (0x2A4) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN (0x2C4) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN (0x2E4) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG (0x288) +#define MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG (0x2A8) +#define MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG (0x2C8) +#define MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG (0x2E8) +#define MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_MUX_CTL (0x28C) +#define MSM89XX_CDC_CORE_TX1_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_MUX_CTL (0x2AC) +#define MSM89XX_CDC_CORE_TX2_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_MUX_CTL (0x2CC) +#define MSM89XX_CDC_CORE_TX3_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_MUX_CTL (0x2EC) +#define MSM89XX_CDC_CORE_TX4_MUX_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX1_CLK_FS_CTL (0x290) +#define MSM89XX_CDC_CORE_TX1_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX2_CLK_FS_CTL (0x2B0) +#define MSM89XX_CDC_CORE_TX2_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX3_CLK_FS_CTL (0x2D0) +#define MSM89XX_CDC_CORE_TX3_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX4_CLK_FS_CTL (0x2F0) +#define MSM89XX_CDC_CORE_TX4_CLK_FS_CTL__POR (0x03) +#define MSM89XX_CDC_CORE_TX1_DMIC_CTL (0x294) +#define MSM89XX_CDC_CORE_TX1_DMIC_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX2_DMIC_CTL (0x2B4) +#define MSM89XX_CDC_CORE_TX2_DMIC_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX3_DMIC_CTL (0x2D4) +#define MSM89XX_CDC_CORE_TX3_DMIC_CTL__POR (0x00) +#define MSM89XX_CDC_CORE_TX4_DMIC_CTL (0x2F4) +#define MSM89XX_CDC_CORE_TX4_DMIC_CTL__POR (0x00) + +#define MSM89XX_CDC_CORE_NUM_REGISTERS \ + (MSM89XX_CDC_CORE_TX4_DMIC_CTL+1) +#define MSM89XX_CDC_CORE_MAX_REGISTER \ + (MSM89XX_CDC_CORE_NUM_REGISTERS-1) +#define MSM89XX_CDC_CORE_CACHE_SIZE \ + MSM89XX_CDC_CORE_NUM_REGISTERS +#endif diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-regmap.c b/sound/soc/codecs/sdm660_cdc/sdm660-regmap.c new file mode 100644 index 000000000000..7d8ac6df14bb --- /dev/null +++ b/sound/soc/codecs/sdm660_cdc/sdm660-regmap.c @@ -0,0 +1,611 @@ +/* + * 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 "msm-cdc-common.h" +#include "sdm660-cdc-registers.h" + +/* + * Default register reset values that are common across different versions + * are defined here. If a register reset value is changed based on version + * then remove it from this structure and add it in version specific + * structures. + */ +struct reg_default + msm89xx_cdc_core_defaults[MSM89XX_CDC_CORE_CACHE_SIZE] = { + {MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x13}, + {MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 0x13}, + {MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_OTHR_CTL, 0x04}, + {MSM89XX_CDC_CORE_CLK_RX_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_SD_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL, 0x13}, + {MSM89XX_CDC_CORE_RX1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX2_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX3_B5_CTL, 0x68}, + {MSM89XX_CDC_CORE_RX1_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_TOP_GAIN_UPDATE, 0x00}, + {MSM89XX_CDC_CORE_TOP_CTL, 0x01}, + {MSM89XX_CDC_CORE_COMP0_B1_CTL, 0x30}, + {MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xB5}, + {MSM89XX_CDC_CORE_COMP0_B3_CTL, 0x28}, + {MSM89XX_CDC_CORE_COMP0_B4_CTL, 0x37}, + {MSM89XX_CDC_CORE_COMP0_B5_CTL, 0x7F}, + {MSM89XX_CDC_CORE_COMP0_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS, 0x03}, + {MSM89XX_CDC_CORE_COMP0_FS_CFG, 0x03}, + {MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL, 0x02}, + {MSM89XX_CDC_CORE_DEBUG_DESER1_CTL, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_DESER2_CTL, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_CTL, 0x40}, + {MSM89XX_CDC_CORE_IIR2_CTL, 0x40}, + {MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_RX3_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, 0x00}, + {MSM89XX_CDC_CORE_CONN_TX_B3_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX5_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX5_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX5_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN, 0x00}, + {MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG, 0x00}, + {MSM89XX_CDC_CORE_TX1_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX2_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX3_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX4_MUX_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX1_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX2_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX3_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX4_CLK_FS_CTL, 0x03}, + {MSM89XX_CDC_CORE_TX1_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX2_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX3_DMIC_CTL, 0x00}, + {MSM89XX_CDC_CORE_TX4_DMIC_CTL, 0x00}, +}; + +struct reg_default + msm89xx_pmic_cdc_defaults[MSM89XX_PMIC_CDC_CACHE_SIZE] = { + {MSM89XX_PMIC_DIGITAL_REVISION1, 0x00}, + {MSM89XX_PMIC_DIGITAL_REVISION2, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_TYPE, 0x23}, + {MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE, 0x01}, + {MSM89XX_PMIC_DIGITAL_INT_RT_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_SET_TYPE, 0xFF}, + {MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH, 0xFF}, + {MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_EN_SET, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_EN_CLR, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_PENDING_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_MID_SEL, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_PRIORITY, 0x00}, + {MSM89XX_PMIC_DIGITAL_GPIO_MODE, 0x00}, + {MSM89XX_PMIC_DIGITAL_PIN_CTL_OE, 0x01}, + {MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA, 0x00}, + {MSM89XX_PMIC_DIGITAL_PIN_STATUS, 0x00}, + {MSM89XX_PMIC_DIGITAL_HDRIVE_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, 0x02}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, 0x02}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1, 0x7C}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2, 0x7C}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3, 0x7C}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3, 0x00}, + {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_0, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_1, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_2, 0x00}, + {MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2, 0x02}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x05}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_TEST1, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_TEST_VAL, 0x00}, + {MSM89XX_PMIC_DIGITAL_TRIM_NUM, 0x00}, + {MSM89XX_PMIC_DIGITAL_TRIM_CTRL, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION1, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION2, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION3, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION4, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_TYPE, 0x23}, + {MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x09}, + {MSM89XX_PMIC_ANALOG_INT_RT_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_SET_TYPE, 0x3F}, + {MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH, 0x3F}, + {MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_EN_SET, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_EN_CLR, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_LATCHED_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_PENDING_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_MID_SEL, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_PRIORITY, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_VAL, 0x20}, + {MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, 0x49}, + {MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20}, + {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, 0x00}, + {MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x35}, + {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08}, + {MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x98}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, 0x40}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x61}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL, 0x80}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x00}, + {MSM89XX_PMIC_ANALOG_TX_1_EN, 0x03}, + {MSM89XX_PMIC_ANALOG_TX_2_EN, 0x03}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1, 0xBF}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2, 0x8C}, + {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x6B}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, 0x51}, + {MSM89XX_PMIC_ANALOG_TX_3_EN, 0x02}, + {MSM89XX_PMIC_ANALOG_NCP_EN, 0x26}, + {MSM89XX_PMIC_ANALOG_NCP_CLK, 0x23}, + {MSM89XX_PMIC_ANALOG_NCP_DEGLITCH, 0x5B}, + {MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x08}, + {MSM89XX_PMIC_ANALOG_NCP_BIAS, 0x29}, + {MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0x24}, + {MSM89XX_PMIC_ANALOG_NCP_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR, 0xD5}, + {MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER, 0xE8}, + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xCF}, + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0x6E}, + {MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x18}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0x5A}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP, 0x69}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, 0x29}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x80}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL, 0xDA}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0x16}, + {MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12}, + {MSM89XX_PMIC_ANALOG_RX_ATEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_STATUS, 0x0C}, + {MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x83}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET, 0x91}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x29}, + {MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x4D}, + {MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1}, + {MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x1E}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC, 0xCB}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x00}, + {MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x02}, + {MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE, 0x14}, + {MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x00}, + {MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x1F}, + {MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x8C}, + {MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE, 0xC0}, + {MSM89XX_PMIC_ANALOG_BOOST_TEST1_1, 0x00}, + {MSM89XX_PMIC_ANALOG_BOOST_TEST_2, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR, 0x00}, + {MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL, 0x00}, + {MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2, 0x01}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x05}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_TEST1, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_TEST_VAL, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_NUM, 0x04}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL1, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL2, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL3, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL4, 0x00}, +}; + +static const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE] = { + [MSM89XX_CDC_CORE_CLK_RX_RESET_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_MCLK_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_PDM_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_SD_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_TOP_GAIN_UPDATE] = 1, + [MSM89XX_CDC_CORE_TOP_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B1_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B2_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B3_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B4_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B5_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B6_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS] = 1, + [MSM89XX_CDC_CORE_COMP0_FS_CFG] = 1, + [MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER1_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER2_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B3_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX1_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX5_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_DMIC_CTL] = 1, +}; + +static const u8 msm89xx_cdc_core_reg_writeable[MSM89XX_CDC_CORE_CACHE_SIZE] = { + [MSM89XX_CDC_CORE_CLK_RX_RESET_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_OTHR_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_MCLK_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_PDM_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_SD_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_DMIC_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_RX_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CLK_TX2_I2S_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B3_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B4_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B5_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_B6_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL] = 1, + [MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL] = 1, + [MSM89XX_CDC_CORE_TOP_GAIN_UPDATE] = 1, + [MSM89XX_CDC_CORE_TOP_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B1_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B2_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B3_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B4_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B5_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_B6_CTL] = 1, + [MSM89XX_CDC_CORE_COMP0_FS_CFG] = 1, + [MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER1_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_DESER2_CTL] = 1, + [MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL] = 1, + [MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_RX3_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL] = 1, + [MSM89XX_CDC_CORE_CONN_TX_B3_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX1_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_TIMER] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN] = 1, + [MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG] = 1, + [MSM89XX_CDC_CORE_TX5_MUX_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_CLK_FS_CTL] = 1, + [MSM89XX_CDC_CORE_TX5_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX1_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX2_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX3_DMIC_CTL] = 1, + [MSM89XX_CDC_CORE_TX4_DMIC_CTL] = 1, +}; + +bool msm89xx_cdc_core_readable_reg(struct device *dev, unsigned int reg) +{ + return msm89xx_cdc_core_reg_readable[reg]; +} + +bool msm89xx_cdc_core_writeable_reg(struct device *dev, unsigned int reg) +{ + return msm89xx_cdc_core_reg_writeable[reg]; +} + +bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MSM89XX_CDC_CORE_RX1_B1_CTL: + case MSM89XX_CDC_CORE_RX2_B1_CTL: + case MSM89XX_CDC_CORE_RX3_B1_CTL: + case MSM89XX_CDC_CORE_RX1_B6_CTL: + case MSM89XX_CDC_CORE_RX2_B6_CTL: + case MSM89XX_CDC_CORE_RX3_B6_CTL: + case MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG: + case MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL: + case MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL: + case MSM89XX_CDC_CORE_CLK_MCLK_CTL: + case MSM89XX_CDC_CORE_CLK_PDM_CTL: + return true; + default: + return false; + } +} diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c new file mode 100644 index 000000000000..661db2b66324 --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-mgr.c @@ -0,0 +1,1253 @@ +/* + * 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 "wcd-dsp-utils.h" + +/* Forward declarations */ +static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type); + +/* Component related macros */ +#define WDSP_GET_COMPONENT(wdsp, x) (&(wdsp->cmpnts[x])) +#define WDSP_GET_CMPNT_TYPE_STR(x) wdsp_get_cmpnt_type_string(x) + +/* + * These #defines indicate the bit number in status field + * for each of the status. If bit is set, it indicates + * the status as done, else if bit is not set, it indicates + * the status is either failed or not done. + */ +#define WDSP_STATUS_INITIALIZED BIT(0) +#define WDSP_STATUS_CODE_DLOADED BIT(1) +#define WDSP_STATUS_DATA_DLOADED BIT(2) +#define WDSP_STATUS_BOOTED BIT(3) + +/* Helper macros for printing wdsp messages */ +#define WDSP_ERR(wdsp, fmt, ...) \ + dev_err(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__) +#define WDSP_DBG(wdsp, fmt, ...) \ + dev_dbg(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__) + +/* Helper macros for locking */ +#define WDSP_MGR_MUTEX_LOCK(wdsp, lock) \ +{ \ + WDSP_DBG(wdsp, "mutex_lock(%s)", \ + __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WDSP_MGR_MUTEX_UNLOCK(wdsp, lock) \ +{ \ + WDSP_DBG(wdsp, "mutex_unlock(%s)", \ + __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +/* Helper macros for using status mask */ +#define WDSP_SET_STATUS(wdsp, state) \ +{ \ + wdsp->status |= state; \ + WDSP_DBG(wdsp, "set 0x%lx, new_state = 0x%x", \ + state, wdsp->status); \ +} + +#define WDSP_CLEAR_STATUS(wdsp, state) \ +{ \ + wdsp->status &= (~state); \ + WDSP_DBG(wdsp, "clear 0x%lx, new_state = 0x%x", \ + state, wdsp->status); \ +} + +#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state) + +/* SSR relate status macros */ +#define WDSP_SSR_STATUS_WDSP_READY BIT(0) +#define WDSP_SSR_STATUS_CDC_READY BIT(1) +#define WDSP_SSR_STATUS_READY \ + (WDSP_SSR_STATUS_WDSP_READY | WDSP_SSR_STATUS_CDC_READY) +#define WDSP_SSR_READY_WAIT_TIMEOUT (10 * HZ) + +enum wdsp_ssr_type { + + /* Init value, indicates there is no SSR in progress */ + WDSP_SSR_TYPE_NO_SSR = 0, + + /* + * Indicates WDSP crashed. The manager driver internally + * decides when to perform WDSP restart based on the + * users of wdsp. Hence there is no explicit WDSP_UP. + */ + WDSP_SSR_TYPE_WDSP_DOWN, + + /* Indicates codec hardware is down */ + WDSP_SSR_TYPE_CDC_DOWN, + + /* Indicates codec hardware is up, trigger to restart WDSP */ + WDSP_SSR_TYPE_CDC_UP, +}; + +struct wdsp_cmpnt { + + /* OF node of the phandle */ + struct device_node *np; + + /* + * Child component's dev_name, should be set in DT for the child's + * phandle if child's dev->of_node does not match the phandle->of_node + */ + const char *cdev_name; + + /* Child component's device node */ + struct device *cdev; + + /* Private data that component may want back on callbacks */ + void *priv_data; + + /* Child ops */ + struct wdsp_cmpnt_ops *ops; +}; + +struct wdsp_ramdump_data { + + /* Ramdump device */ + void *rd_dev; + + /* DMA address of the dump */ + dma_addr_t rd_addr; + + /* Virtual address of the dump */ + void *rd_v_addr; + + /* Data provided through error interrupt */ + struct wdsp_err_signal_arg err_data; +}; + +struct wdsp_mgr_priv { + + /* Manager driver's struct device pointer */ + struct device *mdev; + + /* Match struct for component framework */ + struct component_match *match; + + /* Manager's ops/function callbacks */ + struct wdsp_mgr_ops *ops; + + /* Array to store information for all expected components */ + struct wdsp_cmpnt cmpnts[WDSP_CMPNT_TYPE_MAX]; + + /* The filename of image to be downloaded */ + const char *img_fname; + + /* Keeps track of current state of manager driver */ + u32 status; + + /* Work to load the firmware image after component binding */ + struct work_struct load_fw_work; + + /* List of segments in image to be downloaded */ + struct list_head *seg_list; + + /* Base address of the image in memory */ + u32 base_addr; + + /* Instances using dsp */ + int dsp_users; + + /* Lock for serializing ops called by components */ + struct mutex api_mutex; + + struct wdsp_ramdump_data dump_data; + + /* SSR related */ + enum wdsp_ssr_type ssr_type; + struct mutex ssr_mutex; + struct work_struct ssr_work; + u16 ready_status; + struct completion ready_compl; + + /* Debugfs related */ + struct dentry *entry; + bool panic_on_error; +}; + +static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type) +{ + switch (type) { + case WDSP_SSR_TYPE_NO_SSR: + return "NO_SSR"; + case WDSP_SSR_TYPE_WDSP_DOWN: + return "WDSP_DOWN"; + case WDSP_SSR_TYPE_CDC_DOWN: + return "CDC_DOWN"; + case WDSP_SSR_TYPE_CDC_UP: + return "CDC_UP"; + default: + pr_err("%s: Invalid ssr_type %d\n", + __func__, type); + return "Invalid"; + } +} + +static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type) +{ + switch (type) { + case WDSP_CMPNT_CONTROL: + return "control"; + case WDSP_CMPNT_IPC: + return "ipc"; + case WDSP_CMPNT_TRANSPORT: + return "transport"; + default: + pr_err("%s: Invalid component type %d\n", + __func__, type); + return "Invalid"; + } +} + +static void __wdsp_clr_ready_locked(struct wdsp_mgr_priv *wdsp, + u16 value) +{ + wdsp->ready_status &= ~(value); + WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status); +} + +static void __wdsp_set_ready_locked(struct wdsp_mgr_priv *wdsp, + u16 value, bool mark_complete) +{ + wdsp->ready_status |= value; + WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status); + + if (mark_complete && + wdsp->ready_status == WDSP_SSR_STATUS_READY) { + WDSP_DBG(wdsp, "marking ready completion"); + complete(&wdsp->ready_compl); + } +} + +static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) + cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } +} + +static void wdsp_broadcast_event_downseq(struct wdsp_mgr_priv *wdsp, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) + cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } +} + +static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp, + enum wdsp_cmpnt_type type, + enum wdsp_event_type event, + void *data) +{ + struct wdsp_cmpnt *cmpnt; + int ret; + + cmpnt = WDSP_GET_COMPONENT(wdsp, type); + if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) { + ret = cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data, + event, data); + } else { + WDSP_ERR(wdsp, "not valid event_handler for %s", + WDSP_GET_CMPNT_TYPE_STR(type)); + ret = -EINVAL; + } + + return ret; +} + +static void wdsp_deinit_components(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_cmpnt *cmpnt; + int i; + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if (cmpnt && cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data); + } +} + +static int wdsp_init_components(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_cmpnt *cmpnt; + int fail_idx = WDSP_CMPNT_TYPE_MAX; + int i, ret = 0; + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + + /* Init is allowed to be NULL */ + if (!cmpnt->ops || !cmpnt->ops->init) + continue; + ret = cmpnt->ops->init(cmpnt->cdev, cmpnt->priv_data); + if (ret) { + WDSP_ERR(wdsp, "Init failed (%d) for component %s", + ret, WDSP_GET_CMPNT_TYPE_STR(i)); + fail_idx = i; + break; + } + } + + if (fail_idx < WDSP_CMPNT_TYPE_MAX) { + /* Undo init for already initialized components */ + for (i = fail_idx - 1; i >= 0; i--) { + struct wdsp_cmpnt *cmpnt = WDSP_GET_COMPONENT(wdsp, i); + + if (cmpnt->ops && cmpnt->ops->deinit) + cmpnt->ops->deinit(cmpnt->cdev, + cmpnt->priv_data); + } + } else { + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_INIT, NULL); + } + + return ret; +} + +static int wdsp_load_each_segment(struct wdsp_mgr_priv *wdsp, + struct wdsp_img_segment *seg) +{ + struct wdsp_img_section img_section; + int ret; + + WDSP_DBG(wdsp, + "base_addr 0x%x, split_fname %s, load_addr 0x%x, size 0x%zx", + wdsp->base_addr, seg->split_fname, seg->load_addr, seg->size); + + if (seg->load_addr < wdsp->base_addr) { + WDSP_ERR(wdsp, "Invalid addr 0x%x, base_addr = 0x%x", + seg->load_addr, wdsp->base_addr); + return -EINVAL; + } + + img_section.addr = seg->load_addr - wdsp->base_addr; + img_section.size = seg->size; + img_section.data = seg->data; + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT, + WDSP_EVENT_DLOAD_SECTION, + &img_section); + if (ret < 0) + WDSP_ERR(wdsp, + "Failed, err = %d for base_addr = 0x%x split_fname = %s, load_addr = 0x%x, size = 0x%zx", + ret, wdsp->base_addr, seg->split_fname, + seg->load_addr, seg->size); + return ret; +} + +static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, + unsigned int type) +{ + struct wdsp_cmpnt *ctl; + struct wdsp_img_segment *seg = NULL; + enum wdsp_event_type pre, post; + long status; + int ret; + + ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL); + + if (type == WDSP_ELF_FLAG_RE) { + pre = WDSP_EVENT_PRE_DLOAD_CODE; + post = WDSP_EVENT_POST_DLOAD_CODE; + status = WDSP_STATUS_CODE_DLOADED; + } else if (type == WDSP_ELF_FLAG_WRITE) { + pre = WDSP_EVENT_PRE_DLOAD_DATA; + post = WDSP_EVENT_POST_DLOAD_DATA; + status = WDSP_STATUS_DATA_DLOADED; + } else { + WDSP_ERR(wdsp, "Invalid type %u", type); + return -EINVAL; + } + + ret = wdsp_get_segment_list(ctl->cdev, wdsp->img_fname, + type, wdsp->seg_list, &wdsp->base_addr); + if (ret < 0 || + list_empty(wdsp->seg_list)) { + WDSP_ERR(wdsp, "Error %d to get image segments for type %d", + ret, type); + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED, + NULL); + goto done; + } + + /* Notify all components that image is about to be downloaded */ + wdsp_broadcast_event_upseq(wdsp, pre, NULL); + + /* Go through the list of segments and download one by one */ + list_for_each_entry(seg, wdsp->seg_list, list) { + ret = wdsp_load_each_segment(wdsp, seg); + if (ret < 0) { + wdsp_broadcast_event_downseq(wdsp, + WDSP_EVENT_DLOAD_FAILED, + NULL); + goto dload_error; + } + } + + WDSP_SET_STATUS(wdsp, status); + + /* Notify all components that image is downloaded */ + wdsp_broadcast_event_downseq(wdsp, post, NULL); + +dload_error: + wdsp_flush_segment_list(wdsp->seg_list); +done: + return ret; +} + +static int wdsp_init_and_dload_code_sections(struct wdsp_mgr_priv *wdsp) +{ + int ret; + bool is_initialized; + + is_initialized = WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_INITIALIZED); + + if (!is_initialized) { + /* Components are not initialized yet, initialize them */ + ret = wdsp_init_components(wdsp); + if (ret < 0) { + WDSP_ERR(wdsp, "INIT failed, err = %d", ret); + goto done; + } + WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } + + /* Download the read-execute sections of image */ + ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE); + if (ret < 0) { + WDSP_ERR(wdsp, "Error %d to download code sections", ret); + goto done; + } +done: + return ret; +} + +static void wdsp_load_fw_image(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + return; + } + + ret = wdsp_init_and_dload_code_sections(wdsp); + if (ret < 0) + WDSP_ERR(wdsp, "dload code sections failed, err = %d", ret); +} + +static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) +{ + int ret; + + /* Make sure wdsp is in good state */ + if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) { + WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status); + ret = -EINVAL; + goto done; + } + + /* Download the read-write sections of image */ + ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE); + if (ret < 0) { + WDSP_ERR(wdsp, "Data section download failed, err = %d", ret); + goto done; + } + + wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL); + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_BOOT, NULL); + if (ret < 0) { + WDSP_ERR(wdsp, "Failed to boot dsp, err = %d", ret); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL); + WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED); +done: + return ret; +} + +static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp) +{ + int ret; + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + /* + * If Disable happened while SSR is in progress, then set the SSR + * ready status indicating WDSP is now ready. Ignore the disable + * event here and let the SSR handler go through shutdown. + */ + if (wdsp->ssr_type != WDSP_SSR_TYPE_NO_SSR) { + __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY, true); + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + return 0; + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + + /* Make sure wdsp is in good state */ + if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { + WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status); + ret = -EINVAL; + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, NULL); + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_SHUTDOWN, NULL); + if (ret < 0) { + WDSP_ERR(wdsp, "Failed to shutdown dsp, err = %d", ret); + goto done; + } + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED); + + /* Data sections are to be downloaded per boot */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); +done: + return ret; +} + +static int wdsp_register_cmpnt_ops(struct device *wdsp_dev, + struct device *cdev, + void *priv_data, + struct wdsp_cmpnt_ops *ops) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + int i, ret; + + if (!wdsp_dev || !cdev || !ops) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, i); + if ((cdev->of_node && cdev->of_node == cmpnt->np) || + (cmpnt->cdev_name && + !strcmp(dev_name(cdev), cmpnt->cdev_name))) { + break; + } + } + + if (i == WDSP_CMPNT_TYPE_MAX) { + WDSP_ERR(wdsp, "Failed to register component dev %s", + dev_name(cdev)); + ret = -EINVAL; + goto done; + } + + cmpnt->cdev = cdev; + cmpnt->ops = ops; + cmpnt->priv_data = priv_data; +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + return 0; +} + +static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev, + enum wdsp_cmpnt_type type) +{ + struct wdsp_mgr_priv *wdsp; + struct wdsp_cmpnt *cmpnt; + + if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX) + return NULL; + + wdsp = dev_get_drvdata(wdsp_dev); + cmpnt = WDSP_GET_COMPONENT(wdsp, type); + + return cmpnt->cdev; +} + +static int wdsp_get_devops_for_cmpnt(struct device *wdsp_dev, + enum wdsp_cmpnt_type type, + void *data) +{ + struct wdsp_mgr_priv *wdsp; + int ret = 0; + + if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + ret = wdsp_unicast_event(wdsp, type, + WDSP_EVENT_GET_DEVOPS, data); + if (ret) + WDSP_ERR(wdsp, "get_dev_ops failed for cmpnt type %d", + type); + return ret; +} + +static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp) +{ + struct wdsp_img_section img_section; + struct wdsp_err_signal_arg *data = &wdsp->dump_data.err_data; + struct ramdump_segment rd_seg; + int ret = 0; + + if (wdsp->ssr_type != WDSP_SSR_TYPE_WDSP_DOWN || + !data->mem_dumps_enabled) { + WDSP_DBG(wdsp, "cannot dump memory, ssr_type %s, dumps %s", + wdsp_get_ssr_type_string(wdsp->ssr_type), + !(data->mem_dumps_enabled) ? "disabled" : "enabled"); + goto done; + } + + if (data->dump_size == 0 || + data->remote_start_addr < wdsp->base_addr) { + WDSP_ERR(wdsp, "Invalid start addr 0x%x or dump_size 0x%zx", + data->remote_start_addr, data->dump_size); + goto done; + } + + if (!wdsp->dump_data.rd_dev) { + WDSP_ERR(wdsp, "Ramdump device is not setup"); + goto done; + } + + WDSP_DBG(wdsp, "base_addr 0x%x, dump_start_addr 0x%x, dump_size 0x%zx", + wdsp->base_addr, data->remote_start_addr, data->dump_size); + + /* Allocate memory for dumps */ + wdsp->dump_data.rd_v_addr = dma_alloc_coherent(wdsp->mdev, + data->dump_size, + &wdsp->dump_data.rd_addr, + GFP_KERNEL); + if (!wdsp->dump_data.rd_v_addr) + goto done; + + img_section.addr = data->remote_start_addr - wdsp->base_addr; + img_section.size = data->dump_size; + img_section.data = wdsp->dump_data.rd_v_addr; + + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT, + WDSP_EVENT_READ_SECTION, + &img_section); + if (ret < 0) { + WDSP_ERR(wdsp, "Failed to read dumps, size 0x%zx at addr 0x%x", + img_section.size, img_section.addr); + goto err_read_dumps; + } + + /* + * If panic_on_error flag is explicitly set through the debugfs, + * then cause a BUG here to aid debugging. + */ + BUG_ON(wdsp->panic_on_error); + + rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr; + rd_seg.size = img_section.size; + rd_seg.v_address = wdsp->dump_data.rd_v_addr; + + ret = do_ramdump(wdsp->dump_data.rd_dev, &rd_seg, 1); + if (ret < 0) + WDSP_ERR(wdsp, "do_ramdump failed with error %d", ret); + +err_read_dumps: + dma_free_coherent(wdsp->mdev, data->dump_size, + wdsp->dump_data.rd_v_addr, wdsp->dump_data.rd_addr); +done: + return; +} + +static void wdsp_ssr_work_fn(struct work_struct *work) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + wdsp = container_of(work, struct wdsp_mgr_priv, ssr_work); + if (!wdsp) { + pr_err("%s: Invalid private_data\n", __func__); + return; + } + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + /* Issue ramdumps and shutdown only if DSP is currently booted */ + if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { + wdsp_collect_ramdumps(wdsp); + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL, + WDSP_EVENT_DO_SHUTDOWN, NULL); + if (ret < 0) + WDSP_ERR(wdsp, "Failed WDSP shutdown, err = %d", ret); + + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, + NULL); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED); + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + ret = wait_for_completion_timeout(&wdsp->ready_compl, + WDSP_SSR_READY_WAIT_TIMEOUT); + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + if (ret == 0) { + WDSP_ERR(wdsp, "wait_for_ready timed out, status = 0x%x", + wdsp->ready_status); + goto done; + } + + /* Data sections are to downloaded per WDSP boot */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED); + + /* + * Even though code section could possible be retained on DSP + * crash, go ahead and still re-download just to avoid any + * memory corruption from previous crash. + */ + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED); + + /* If codec restarted, then all components must be re-initialized */ + if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_UP) { + wdsp_deinit_components(wdsp); + WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED); + } + + ret = wdsp_init_and_dload_code_sections(wdsp); + if (ret < 0) { + WDSP_ERR(wdsp, "Failed to dload code sections err = %d", + ret); + goto done; + } + + /* SSR handling is finished, mark SSR type as NO_SSR */ + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); +} + +static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg, + enum wdsp_ssr_type ssr_type) +{ + enum wdsp_ssr_type current_ssr_type; + struct wdsp_err_signal_arg *err_data; + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + + current_ssr_type = wdsp->ssr_type; + WDSP_DBG(wdsp, "Current ssr_type %s, handling ssr_type %s", + wdsp_get_ssr_type_string(current_ssr_type), + wdsp_get_ssr_type_string(ssr_type)); + wdsp->ssr_type = ssr_type; + + if (arg) { + err_data = (struct wdsp_err_signal_arg *) arg; + memcpy(&wdsp->dump_data.err_data, err_data, + sizeof(*err_data)); + } else { + memset(&wdsp->dump_data.err_data, 0, + sizeof(wdsp->dump_data.err_data)); + } + + switch (ssr_type) { + + case WDSP_SSR_TYPE_WDSP_DOWN: + __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY); + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, + NULL); + schedule_work(&wdsp->ssr_work); + break; + + case WDSP_SSR_TYPE_CDC_DOWN: + __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY); + /* + * If DSP is booted when CDC_DOWN is received, it needs + * to be shutdown. + */ + if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) { + __wdsp_clr_ready_locked(wdsp, + WDSP_SSR_STATUS_WDSP_READY); + wdsp_broadcast_event_downseq(wdsp, + WDSP_EVENT_PRE_SHUTDOWN, + NULL); + } + + schedule_work(&wdsp->ssr_work); + break; + + case WDSP_SSR_TYPE_CDC_UP: + __wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY, true); + break; + + default: + WDSP_ERR(wdsp, "undefined ssr_type %d\n", ssr_type); + /* Revert back the ssr_type for undefined events */ + wdsp->ssr_type = current_ssr_type; + break; + } + + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + + return 0; +} + +static int wdsp_signal_handler(struct device *wdsp_dev, + enum wdsp_signal signal, void *arg) +{ + struct wdsp_mgr_priv *wdsp; + int ret; + + if (!wdsp_dev) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + + WDSP_DBG(wdsp, "Raised signal %d", signal); + + switch (signal) { + case WDSP_IPC1_INTR: + ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC, + WDSP_EVENT_IPC1_INTR, NULL); + break; + case WDSP_ERR_INTR: + ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_WDSP_DOWN); + break; + case WDSP_CDC_DOWN_SIGNAL: + ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_DOWN); + break; + case WDSP_CDC_UP_SIGNAL: + ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_UP); + break; + default: + ret = -EINVAL; + break; + } + + if (ret < 0) + WDSP_ERR(wdsp, "handling signal %d failed with error %d", + signal, ret); + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + + return ret; +} + +static int wdsp_vote_for_dsp(struct device *wdsp_dev, + bool vote) +{ + struct wdsp_mgr_priv *wdsp; + int ret = 0; + + if (!wdsp_dev) + return -EINVAL; + + wdsp = dev_get_drvdata(wdsp_dev); + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); + WDSP_DBG(wdsp, "request %s, current users = %d", + vote ? "enable" : "disable", wdsp->dsp_users); + + if (vote) { + wdsp->dsp_users++; + if (wdsp->dsp_users == 1) + ret = wdsp_enable_dsp(wdsp); + } else { + if (wdsp->dsp_users == 0) + goto done; + + wdsp->dsp_users--; + if (wdsp->dsp_users == 0) + ret = wdsp_disable_dsp(wdsp); + } + + if (ret < 0) + WDSP_DBG(wdsp, "wdsp %s failed, err = %d", + vote ? "enable" : "disable", ret); + +done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); + return ret; +} + +static int wdsp_suspend(struct device *wdsp_dev) +{ + struct wdsp_mgr_priv *wdsp; + int rc = 0, i; + + if (!wdsp_dev) { + pr_err("%s: Invalid handle to device\n", __func__); + return -EINVAL; + } + + wdsp = dev_get_drvdata(wdsp_dev); + + for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) { + rc = wdsp_unicast_event(wdsp, i, WDSP_EVENT_SUSPEND, NULL); + if (rc < 0) { + WDSP_ERR(wdsp, "component %s failed to suspend\n", + WDSP_GET_CMPNT_TYPE_STR(i)); + break; + } + } + + return rc; +} + +static int wdsp_resume(struct device *wdsp_dev) +{ + struct wdsp_mgr_priv *wdsp; + int rc = 0, i; + + if (!wdsp_dev) { + pr_err("%s: Invalid handle to device\n", __func__); + return -EINVAL; + } + + wdsp = dev_get_drvdata(wdsp_dev); + + for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) { + rc = wdsp_unicast_event(wdsp, i, WDSP_EVENT_RESUME, NULL); + if (rc < 0) { + WDSP_ERR(wdsp, "component %s failed to resume\n", + WDSP_GET_CMPNT_TYPE_STR(i)); + break; + } + } + + return rc; +} + +static struct wdsp_mgr_ops wdsp_ops = { + .register_cmpnt_ops = wdsp_register_cmpnt_ops, + .get_dev_for_cmpnt = wdsp_get_dev_for_cmpnt, + .get_devops_for_cmpnt = wdsp_get_devops_for_cmpnt, + .signal_handler = wdsp_signal_handler, + .vote_for_dsp = wdsp_vote_for_dsp, + .suspend = wdsp_suspend, + .resume = wdsp_resume, +}; + +static int wdsp_mgr_compare_of(struct device *dev, void *data) +{ + struct wdsp_cmpnt *cmpnt = data; + + /* + * First try to match based on of_node, if of_node is not + * present, try to match on the dev_name + */ + return ((dev->of_node && dev->of_node == cmpnt->np) || + (cmpnt->cdev_name && + !strcmp(dev_name(dev), cmpnt->cdev_name))); +} + +static void wdsp_mgr_debugfs_init(struct wdsp_mgr_priv *wdsp) +{ + wdsp->entry = debugfs_create_dir("wdsp_mgr", NULL); + if (IS_ERR_OR_NULL(wdsp->entry)) + return; + + debugfs_create_bool("panic_on_error", 0644, + wdsp->entry, &wdsp->panic_on_error); +} + +static void wdsp_mgr_debugfs_remove(struct wdsp_mgr_priv *wdsp) +{ + debugfs_remove_recursive(wdsp->entry); + wdsp->entry = NULL; +} + +static int wdsp_mgr_bind(struct device *dev) +{ + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev); + struct wdsp_cmpnt *cmpnt; + int ret, idx; + + wdsp->ops = &wdsp_ops; + + /* Setup ramdump device */ + wdsp->dump_data.rd_dev = create_ramdump_device("wdsp", dev); + if (!wdsp->dump_data.rd_dev) + dev_info(dev, "%s: create_ramdump_device failed\n", __func__); + + ret = component_bind_all(dev, wdsp->ops); + if (ret < 0) + WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret); + + /* Make sure all components registered ops */ + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + if (!cmpnt->cdev || !cmpnt->ops) { + WDSP_ERR(wdsp, "%s did not register ops\n", + WDSP_GET_CMPNT_TYPE_STR(idx)); + ret = -EINVAL; + component_unbind_all(dev, wdsp->ops); + break; + } + } + + wdsp_mgr_debugfs_init(wdsp); + + /* Schedule the work to download image if binding was successful. */ + if (!ret) + schedule_work(&wdsp->load_fw_work); + + return ret; +} + +static void wdsp_mgr_unbind(struct device *dev) +{ + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev); + struct wdsp_cmpnt *cmpnt; + int idx; + + component_unbind_all(dev, wdsp->ops); + + wdsp_mgr_debugfs_remove(wdsp); + + if (wdsp->dump_data.rd_dev) { + destroy_ramdump_device(wdsp->dump_data.rd_dev); + wdsp->dump_data.rd_dev = NULL; + } + + /* Clear all status bits */ + wdsp->status = 0x00; + + /* clean up the components */ + for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { + cmpnt = WDSP_GET_COMPONENT(wdsp, idx); + cmpnt->cdev = NULL; + cmpnt->ops = NULL; + cmpnt->priv_data = NULL; + } +} + +static const struct component_master_ops wdsp_master_ops = { + .bind = wdsp_mgr_bind, + .unbind = wdsp_mgr_unbind, +}; + +static void *wdsp_mgr_parse_phandle(struct wdsp_mgr_priv *wdsp, + int index) +{ + struct device *mdev = wdsp->mdev; + struct device_node *np; + struct wdsp_cmpnt *cmpnt = NULL; + struct of_phandle_args pargs; + u32 value; + int ret; + + ret = of_parse_phandle_with_fixed_args(mdev->of_node, + "qcom,wdsp-components", 1, + index, &pargs); + if (ret) { + WDSP_ERR(wdsp, "parse_phandle at index %d failed %d", + index, ret); + return NULL; + } + + np = pargs.np; + value = pargs.args[0]; + + if (value >= WDSP_CMPNT_TYPE_MAX) { + WDSP_ERR(wdsp, "invalid phandle_arg to of_node %s", np->name); + goto done; + } + + cmpnt = WDSP_GET_COMPONENT(wdsp, value); + if (cmpnt->np || cmpnt->cdev_name) { + WDSP_ERR(wdsp, "cmpnt %d already added", value); + cmpnt = NULL; + goto done; + } + + cmpnt->np = np; + of_property_read_string(np, "qcom,wdsp-cmpnt-dev-name", + &cmpnt->cdev_name); +done: + of_node_put(np); + return cmpnt; +} + +static int wdsp_mgr_parse_dt_entries(struct wdsp_mgr_priv *wdsp) +{ + struct device *dev = wdsp->mdev; + void *match_data; + int ph_idx, ret; + + ret = of_property_read_string(dev->of_node, "qcom,img-filename", + &wdsp->img_fname); + if (ret < 0) { + WDSP_ERR(wdsp, "Reading property %s failed, error = %d", + "qcom,img-filename", ret); + return ret; + } + + ret = of_count_phandle_with_args(dev->of_node, + "qcom,wdsp-components", + NULL); + if (ret == -ENOENT) { + WDSP_ERR(wdsp, "Property %s not defined in DT", + "qcom,wdsp-components"); + goto done; + } else if (ret != WDSP_CMPNT_TYPE_MAX * 2) { + WDSP_ERR(wdsp, "Invalid phandle + arg count %d, expected %d", + ret, WDSP_CMPNT_TYPE_MAX * 2); + ret = -EINVAL; + goto done; + } + + ret = 0; + + for (ph_idx = 0; ph_idx < WDSP_CMPNT_TYPE_MAX; ph_idx++) { + + match_data = wdsp_mgr_parse_phandle(wdsp, ph_idx); + if (!match_data) { + WDSP_ERR(wdsp, "component not found at idx %d", ph_idx); + ret = -EINVAL; + goto done; + } + + component_match_add(dev, &wdsp->match, + wdsp_mgr_compare_of, match_data); + } + +done: + return ret; +} + +static int wdsp_mgr_probe(struct platform_device *pdev) +{ + struct wdsp_mgr_priv *wdsp; + struct device *mdev = &pdev->dev; + int ret; + + wdsp = devm_kzalloc(mdev, sizeof(*wdsp), GFP_KERNEL); + if (!wdsp) + return -ENOMEM; + wdsp->mdev = mdev; + wdsp->seg_list = devm_kzalloc(mdev, sizeof(struct list_head), + GFP_KERNEL); + if (!wdsp->seg_list) { + devm_kfree(mdev, wdsp); + return -ENOMEM; + } + + ret = wdsp_mgr_parse_dt_entries(wdsp); + if (ret) + goto err_dt_parse; + + INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image); + INIT_LIST_HEAD(wdsp->seg_list); + mutex_init(&wdsp->api_mutex); + mutex_init(&wdsp->ssr_mutex); + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; + wdsp->ready_status = WDSP_SSR_STATUS_READY; + INIT_WORK(&wdsp->ssr_work, wdsp_ssr_work_fn); + init_completion(&wdsp->ready_compl); + arch_setup_dma_ops(wdsp->mdev, 0, 0, NULL, 0); + dev_set_drvdata(mdev, wdsp); + + ret = component_master_add_with_match(mdev, &wdsp_master_ops, + wdsp->match); + if (ret < 0) { + WDSP_ERR(wdsp, "Failed to add master, err = %d", ret); + goto err_master_add; + } + + return 0; + +err_master_add: + mutex_destroy(&wdsp->api_mutex); + mutex_destroy(&wdsp->ssr_mutex); +err_dt_parse: + devm_kfree(mdev, wdsp->seg_list); + devm_kfree(mdev, wdsp); + dev_set_drvdata(mdev, NULL); + + return ret; +} + +static int wdsp_mgr_remove(struct platform_device *pdev) +{ + struct device *mdev = &pdev->dev; + struct wdsp_mgr_priv *wdsp = dev_get_drvdata(mdev); + + component_master_del(mdev, &wdsp_master_ops); + + mutex_destroy(&wdsp->api_mutex); + mutex_destroy(&wdsp->ssr_mutex); + devm_kfree(mdev, wdsp->seg_list); + devm_kfree(mdev, wdsp); + dev_set_drvdata(mdev, NULL); + + return 0; +}; + +static const struct of_device_id wdsp_mgr_dt_match[] = { + {.compatible = "qcom,wcd-dsp-mgr" }, + { } +}; + +static struct platform_driver wdsp_mgr_driver = { + .driver = { + .name = "wcd-dsp-mgr", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(wdsp_mgr_dt_match), + }, + .probe = wdsp_mgr_probe, + .remove = wdsp_mgr_remove, +}; + +int wcd_dsp_mgr_init(void) +{ + return platform_driver_register(&wdsp_mgr_driver); +} + +void wcd_dsp_mgr_exit(void) +{ + platform_driver_unregister(&wdsp_mgr_driver); +} + +MODULE_DESCRIPTION("WCD DSP manager driver"); +MODULE_DEVICE_TABLE(of, wdsp_mgr_dt_match); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd-dsp-utils.c b/sound/soc/codecs/wcd-dsp-utils.c new file mode 100644 index 000000000000..4eafd55894c5 --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-utils.c @@ -0,0 +1,221 @@ +/* + * 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 "wcd-dsp-utils.h" + +static bool wdsp_is_valid_elf_hdr(const struct elf32_hdr *ehdr, + size_t fw_size) +{ + if (fw_size < sizeof(*ehdr)) { + pr_err("%s: Firmware too small\n", __func__); + goto elf_check_fail; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + pr_err("%s: Not an ELF file\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { + pr_err("%s: Not an executable image\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_phnum == 0) { + pr_err("%s: no segments to load\n", __func__); + goto elf_check_fail; + } + + if (sizeof(struct elf32_phdr) * ehdr->e_phnum + + sizeof(struct elf32_hdr) > fw_size) { + pr_err("%s: Too small MDT file\n", __func__); + goto elf_check_fail; + } + + return true; + +elf_check_fail: + return false; +} + +static int wdsp_add_segment_to_list(struct device *dev, + const char *img_fname, + const struct elf32_phdr *phdr, + int phdr_idx, + struct list_head *seg_list) +{ + struct wdsp_img_segment *seg; + int ret = 0; + + /* Do not load segments with zero size */ + if (phdr->p_filesz == 0 || phdr->p_memsz == 0) + goto done; + + seg = kzalloc(sizeof(*seg), GFP_KERNEL); + if (!seg) { + ret = -ENOMEM; + goto done; + } + + snprintf(seg->split_fname, sizeof(seg->split_fname), + "%s.b%02d", img_fname, phdr_idx); + ret = request_firmware(&seg->split_fw, seg->split_fname, dev); + if (ret < 0) { + dev_err(dev, "%s: firmware %s not found\n", + __func__, seg->split_fname); + goto bad_seg; + } + + seg->load_addr = phdr->p_paddr; + seg->size = phdr->p_filesz; + seg->data = (u8 *) seg->split_fw->data; + + list_add_tail(&seg->list, seg_list); +done: + return ret; +bad_seg: + kfree(seg); + return ret; +} + +/* + * wdsp_flush_segment_list: Flush the list of segments + * @seg_list: List of segments to be flushed + * This API will traverse through the list of segments provided in + * seg_list, release the firmware for each segment and delete the + * segment from the list. + */ +void wdsp_flush_segment_list(struct list_head *seg_list) +{ + struct wdsp_img_segment *seg, *next; + + list_for_each_entry_safe(seg, next, seg_list, list) { + release_firmware(seg->split_fw); + list_del(&seg->list); + kfree(seg); + } +} +EXPORT_SYMBOL(wdsp_flush_segment_list); + +/* + * wdsp_get_segment_list: Get the list of requested segments + * @dev: struct device pointer of caller + * @img_fname: Image name for the mdt and split firmware files + * @segment_type: Requested segment type, should be either + * WDSP_ELF_FLAG_RE or WDSP_ELF_FLAG_WRITE + * @seg_list: An initialized head for list of segmented to be returned + * @entry_point: Pointer to return the entry point of the image + * This API will parse the mdt file for img_fname and create + * an struct wdsp_img_segment for each segment that matches segment_type + * and add this structure to list pointed by seg_list + */ +int wdsp_get_segment_list(struct device *dev, + const char *img_fname, + unsigned int segment_type, + struct list_head *seg_list, + u32 *entry_point) +{ + const struct firmware *fw; + const struct elf32_hdr *ehdr; + const struct elf32_phdr *phdr; + const u8 *elf_ptr; + char mdt_name[WDSP_IMG_NAME_LEN_MAX]; + int ret, phdr_idx; + bool segment_match; + + if (!dev) { + ret = -EINVAL; + pr_err("%s: Invalid device handle\n", __func__); + goto done; + } + + if (!img_fname || !seg_list || !entry_point) { + ret = -EINVAL; + dev_err(dev, "%s: Invalid input params\n", + __func__); + goto done; + } + + if (segment_type != WDSP_ELF_FLAG_RE && + segment_type != WDSP_ELF_FLAG_WRITE) { + dev_err(dev, "%s: Invalid request for segment_type %d\n", + __func__, segment_type); + ret = -EINVAL; + goto done; + } + + snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", img_fname); + ret = request_firmware(&fw, mdt_name, dev); + if (ret < 0) { + dev_err(dev, "%s: firmware %s not found\n", + __func__, mdt_name); + goto done; + } + + ehdr = (struct elf32_hdr *) fw->data; + *entry_point = ehdr->e_entry; + if (!wdsp_is_valid_elf_hdr(ehdr, fw->size)) { + dev_err(dev, "%s: fw mdt %s is invalid\n", + __func__, mdt_name); + ret = -EINVAL; + goto bad_elf; + } + + elf_ptr = fw->data + sizeof(*ehdr); + for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) { + phdr = (struct elf32_phdr *) elf_ptr; + segment_match = false; + + switch (segment_type) { + case WDSP_ELF_FLAG_RE: + /* + * Flag can be READ or EXECUTE or both but + * WRITE flag should not be set. + */ + if ((phdr->p_flags & segment_type) && + !(phdr->p_flags & WDSP_ELF_FLAG_WRITE)) + segment_match = true; + break; + case WDSP_ELF_FLAG_WRITE: + /* + * If WRITE flag is set, other flags do not + * matter. + */ + if (phdr->p_flags & segment_type) + segment_match = true; + break; + } + + if (segment_match) { + ret = wdsp_add_segment_to_list(dev, img_fname, phdr, + phdr_idx, seg_list); + if (ret < 0) { + wdsp_flush_segment_list(seg_list); + goto bad_elf; + } + } + elf_ptr = elf_ptr + sizeof(*phdr); + } + +bad_elf: + release_firmware(fw); +done: + return ret; +} +EXPORT_SYMBOL(wdsp_get_segment_list); diff --git a/sound/soc/codecs/wcd-dsp-utils.h b/sound/soc/codecs/wcd-dsp-utils.h new file mode 100644 index 000000000000..a530a1c3b67d --- /dev/null +++ b/sound/soc/codecs/wcd-dsp-utils.h @@ -0,0 +1,51 @@ +/* + * 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 __WCD_DSP_UTILS_H__ +#define __WCD_DSP_UTILS_H__ + +#define WDSP_IMG_NAME_LEN_MAX 64 + +#define WDSP_ELF_FLAG_EXECUTE (1 << 0) +#define WDSP_ELF_FLAG_WRITE (1 << 1) +#define WDSP_ELF_FLAG_READ (1 << 2) + +#define WDSP_ELF_FLAG_RE (WDSP_ELF_FLAG_READ | WDSP_ELF_FLAG_EXECUTE) + +struct wdsp_img_segment { + + /* Firmware for the slit image */ + const struct firmware *split_fw; + + /* Name of the split firmware file */ + char split_fname[WDSP_IMG_NAME_LEN_MAX]; + + /* Address where the segment is to be loaded */ + u32 load_addr; + + /* Buffer to hold the data to be loaded */ + u8 *data; + + /* Size of the data to be loaded */ + size_t size; + + /* List node pointing to next segment */ + struct list_head list; +}; + +int wdsp_get_segment_list(struct device *dev, const char *img_fname, + unsigned int segment_type, struct list_head *seg_list, + u32 *entry_point); +void wdsp_flush_segment_list(struct list_head *seg_list); + +#endif /* __WCD_DSP_UTILS_H__ */ diff --git a/sound/soc/codecs/wcd-mbhc-adc.c b/sound/soc/codecs/wcd-mbhc-adc.c new file mode 100644 index 000000000000..e44eec9fa5c7 --- /dev/null +++ b/sound/soc/codecs/wcd-mbhc-adc.c @@ -0,0 +1,1020 @@ +/* Copyright (c) 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 "wcd-mbhc-adc.h" +#include "wcd-mbhc-v2.h" + +#define WCD_MBHC_ADC_HS_THRESHOLD_MV 1700 +#define WCD_MBHC_ADC_HPH_THRESHOLD_MV 75 +#define WCD_MBHC_ADC_MICBIAS_MV 1800 + +static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc) +{ + int micbias = 0; + u8 vout_ctl = 0; + + /* Read MBHC Micbias (Mic Bias2) voltage */ + WCD_MBHC_REG_READ(WCD_MBHC_MICB2_VOUT, vout_ctl); + + /* Formula for getting micbias from vout + * micbias = 1.0V + VOUT_CTL * 50mV + */ + micbias = 1000 + (vout_ctl * 50); + pr_debug("%s: vout_ctl: %d, micbias: %d\n", + __func__, vout_ctl, micbias); + + return micbias; +} + +static int wcd_get_voltage_from_adc(u8 val, int micbias) +{ + /* Formula for calculating voltage from ADC + * Voltage = ADC_RESULT*12.5mV*V_MICBIAS/1.8 + */ + return ((val * 125 * micbias)/(WCD_MBHC_ADC_MICBIAS_MV * 10)); +} + +static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc) +{ + u8 adc_result = 0; + int output_mv = 0; + int retry = 3; + u8 adc_en = 0; + + pr_debug("%s: enter\n", __func__); + + /* Pre-requisites for ADC continuous measurement */ + /* Read legacy electircal detection and disable */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0x00); + /* Set ADC to continuous measurement */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 1); + /* Read ADC Enable bit to restore after adc measurement */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en); + /* Disable ADC_ENABLE bit */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + /* Disable MBHC FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + /* Set the MUX selection to IN2P */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, MUX_CTL_IN2P); + /* Enable MBHC FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + /* Enable ADC_ENABLE bit */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 1); + + while (retry--) { + /* wait for 3 msec before reading ADC result */ + usleep_range(3000, 3100); + + /* Read ADC result */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_RESULT, adc_result); + } + + /* Restore ADC Enable */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en); + /* Get voltage from ADC result */ + output_mv = wcd_get_voltage_from_adc(adc_result, + wcd_mbhc_get_micbias(mbhc)); + pr_debug("%s: adc_result: 0x%x, output_mv: %d\n", + __func__, adc_result, output_mv); + + return output_mv; +} + +static int wcd_measure_adc_once(struct wcd_mbhc *mbhc, int mux_ctl) +{ + u8 adc_timeout = 0; + u8 adc_complete = 0; + u8 adc_result = 0; + int retry = 6; + int ret = 0; + int output_mv = 0; + u8 adc_en = 0; + + pr_debug("%s: enter\n", __func__); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); + /* Read ADC Enable bit to restore after adc measurement */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en); + /* Trigger ADC one time measurement */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + /* Set the appropriate MUX selection */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, mux_ctl); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 1); + + while (retry--) { + /* wait for 600usec to get adc results */ + usleep_range(600, 610); + + /* check for ADC Timeout */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_TIMEOUT, adc_timeout); + if (adc_timeout) + continue; + + /* Read ADC complete bit */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_COMPLETE, adc_complete); + if (!adc_complete) + continue; + + /* Read ADC result */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_RESULT, adc_result); + + pr_debug("%s: ADC result: 0x%x\n", __func__, adc_result); + /* Get voltage from ADC result */ + output_mv = wcd_get_voltage_from_adc(adc_result, + wcd_mbhc_get_micbias(mbhc)); + break; + } + + /* Restore ADC Enable */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en); + + if (retry <= 0) { + pr_err("%s: adc complete: %d, adc timeout: %d\n", + __func__, adc_complete, adc_timeout); + ret = -EINVAL; + } else { + pr_debug("%s: adc complete: %d, adc timeout: %d output_mV: %d\n", + __func__, adc_complete, adc_timeout, output_mv); + ret = output_mv; + } + + pr_debug("%s: leave\n", __func__); + + return ret; +} + +static bool wcd_mbhc_adc_detect_anc_plug_type(struct wcd_mbhc *mbhc) +{ + bool anc_mic_found = false; + u16 fsm_en = 0; + u8 det = 0; + unsigned long retry = 0; + int valid_plug_cnt = 0, invalid_plug_cnt = 0; + int ret = 0; + u8 elect_ctl = 0; + u8 adc_mode = 0; + u8 vref = 0; + int vref_mv[] = {1650, 1500, 1600, 1700}; + + if (mbhc->mbhc_cfg->anc_micbias < MIC_BIAS_1 || + mbhc->mbhc_cfg->anc_micbias > MIC_BIAS_4) + return false; + + if (!mbhc->mbhc_cb->mbhc_micbias_control) + return false; + + /* Disable Detection done for ADC operation */ + WCD_MBHC_REG_READ(WCD_MBHC_DETECTION_DONE, det); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); + + /* Mask ADC COMPLETE interrupt */ + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); + + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_ENABLE); + + /* Read legacy electircal detection and disable */ + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0x00); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 1); + WCD_MBHC_REG_READ(WCD_MBHC_ADC_MODE, adc_mode); + + /* + * wait for button debounce time 20ms. If 4-pole plug is inserted + * into 5-pole jack, then there will be a button press interrupt + * during anc plug detection. In that case though Hs_comp_res is 0, + * it should not be declared as ANC plug type + */ + usleep_range(20000, 20100); + + /* + * After enabling FSM, to handle slow insertion scenarios, + * check IN3 voltage is below the Vref + */ + WCD_MBHC_REG_READ(WCD_MBHC_HS_VREF, vref); + + do { + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto done; + } + pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1); + ret = wcd_measure_adc_once(mbhc, MUX_CTL_IN3P); + /* TODO - check the logic */ + if (ret && (ret < vref_mv[vref])) + valid_plug_cnt++; + else + invalid_plug_cnt++; + retry++; + } while (retry < ANC_DETECT_RETRY_CNT); + + pr_debug("%s: valid: %d, invalid: %d\n", __func__, valid_plug_cnt, + invalid_plug_cnt); + + /* decision logic */ + if (valid_plug_cnt > invalid_plug_cnt) + anc_mic_found = true; +done: + /* Restore ADC mode */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, adc_mode); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + /* Set the MUX selection to AUTO */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, MUX_CTL_AUTO); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en); + /* Restore detection done */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, det); + + /* Restore electrical detection */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl); + + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_DISABLE); + pr_debug("%s: anc mic %sfound\n", __func__, + anc_mic_found ? "" : "not "); + + return anc_mic_found; +} + +/* To determine if cross connection occurred */ +static int wcd_check_cross_conn(struct wcd_mbhc *mbhc) +{ + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_NONE; + int hphl_adc_res = 0, hphr_adc_res = 0; + u8 fsm_en = 0; + int ret = 0; + u8 adc_mode = 0; + u8 elect_ctl = 0; + u8 adc_en = 0; + + pr_debug("%s: enter\n", __func__); + /* Check for button press and plug detection */ + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + return -EINVAL; + } + + /* If PA is enabled, dont check for cross-connection */ + if (mbhc->mbhc_cb->hph_pa_on_status) + if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) + return -EINVAL; + + /* Read legacy electircal detection and disable */ + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0x00); + + /* Read and set ADC to single measurement */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_MODE, adc_mode); + /* Read ADC Enable bit to restore after adc measurement */ + WCD_MBHC_REG_READ(WCD_MBHC_ADC_EN, adc_en); + /* Read FSM status */ + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + + /* Get adc result for HPH L */ + hphl_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_L); + if (hphl_adc_res < 0) { + pr_err("%s: hphl_adc_res adc measurement failed\n", __func__); + ret = hphl_adc_res; + goto done; + } + + /* Get adc result for HPH R in mV */ + hphr_adc_res = wcd_measure_adc_once(mbhc, MUX_CTL_HPH_R); + if (hphr_adc_res < 0) { + pr_err("%s: hphr_adc_res adc measurement failed\n", __func__); + ret = hphr_adc_res; + goto done; + } + + if (hphl_adc_res > 100 && hphr_adc_res > 100) { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + pr_debug("%s: Cross connection identified\n", __func__); + } else { + pr_debug("%s: No Cross connection found\n", __func__); + } + +done: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + /* Set the MUX selection to Auto */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, MUX_CTL_AUTO); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + + /* Restore ADC Enable */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, adc_en); + + /* Restore ADC mode */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, adc_mode); + + /* Restore FSM state */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, fsm_en); + + /* Restore electrical detection */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, elect_ctl); + + pr_debug("%s: leave, plug type: %d\n", __func__, plug_type); + + return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false; +} + +static bool wcd_mbhc_adc_check_for_spl_headset(struct wcd_mbhc *mbhc, + int *spl_hs_cnt) +{ + bool spl_hs = false; + int output_mv = 0; + int adc_threshold = 0, adc_hph_threshold = 0; + + pr_debug("%s: enter\n", __func__); + if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + goto exit; + + /* Bump up MB2 to 2.7V */ + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + mbhc->mbhc_cfg->mbhc_micbias, true); + usleep_range(10000, 10100); + + /* + * Use ADC single mode to minimize the chance of missing out + * btn press/relesae for HEADSET type during correct work. + */ + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * + wcd_mbhc_get_micbias(mbhc))/WCD_MBHC_ADC_MICBIAS_MV); + adc_hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV * + wcd_mbhc_get_micbias(mbhc))/ + WCD_MBHC_ADC_MICBIAS_MV); + + if (output_mv > adc_threshold || output_mv < adc_hph_threshold) { + spl_hs = false; + } else { + spl_hs = true; + if (spl_hs_cnt) + *spl_hs_cnt += 1; + } + + /* MB2 back to 1.8v if the type is not special headset */ + if (spl_hs_cnt && (*spl_hs_cnt != WCD_MBHC_SPL_HS_CNT)) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + mbhc->mbhc_cfg->mbhc_micbias, false); + /* Add 10ms delay for micbias to settle */ + usleep_range(10000, 10100); + } + + if (spl_hs) + pr_debug("%s: Detected special HS (%d)\n", __func__, spl_hs); + +exit: + pr_debug("%s: leave\n", __func__); + return spl_hs; +} + +static bool wcd_is_special_headset(struct wcd_mbhc *mbhc) +{ + int delay = 0; + bool ret = false; + bool is_spl_hs = false; + int output_mv = 0; + int adc_threshold = 0; + + /* + * Increase micbias to 2.7V to detect headsets with + * threshold on microphone + */ + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) { + pr_debug("%s: callback fn micb_ctrl_thr_mic not defined\n", + __func__); + return false; + } else if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) { + ret = mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + MIC_BIAS_2, true); + if (ret) { + pr_err("%s: mbhc_micb_ctrl_thr_mic failed, ret: %d\n", + __func__, ret); + return false; + } + } + + adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * + wcd_mbhc_get_micbias(mbhc)) / + WCD_MBHC_ADC_MICBIAS_MV); + + while (!is_spl_hs) { + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + break; + } + delay += 50; + /* Wait for 50ms for FSM to update result */ + msleep(50); + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + if (output_mv <= adc_threshold) { + pr_debug("%s: Special headset detected in %d msecs\n", + __func__, delay); + is_spl_hs = true; + } + + if (delay == SPECIAL_HS_DETECT_TIME_MS) { + pr_debug("%s: Spl headset not found in 2 sec\n", + __func__); + break; + } + } + if (is_spl_hs) { + pr_debug("%s: Headset with threshold found\n", __func__); + mbhc->micbias_enable = true; + ret = true; + } + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic && + !mbhc->micbias_enable) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, MIC_BIAS_2, + false); + pr_debug("%s: leave, micb_enable: %d\n", __func__, + mbhc->micbias_enable); + + return ret; +} + +static void wcd_mbhc_adc_update_fsm_source(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + bool micbias2; + + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_2); + switch (plug_type) { + case MBHC_PLUG_TYPE_HEADPHONE: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + break; + case MBHC_PLUG_TYPE_HEADSET: + case MBHC_PLUG_TYPE_ANC_HEADPHONE: + if (!mbhc->is_hs_recording && !micbias2) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + break; + default: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + break; + + }; +} + +/* should be called under interrupt context that hold suspend */ +static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: scheduling correct_swch_plug\n", __func__); + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + mbhc->hs_detect_work_stop = false; + mbhc->mbhc_cb->lock_sleep(mbhc, true); + schedule_work(work); +} + +/* called under codec_resource_lock acquisition */ +static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: Canceling correct_plug_swch\n", __func__); + mbhc->hs_detect_work_stop = true; + WCD_MBHC_RSC_UNLOCK(mbhc); + if (cancel_work_sync(work)) { + pr_debug("%s: correct_plug_swch is canceled\n", + __func__); + mbhc->mbhc_cb->lock_sleep(mbhc, false); + } + WCD_MBHC_RSC_LOCK(mbhc); +} + +/* called under codec_resource_lock acquisition */ +static void wcd_mbhc_adc_detect_plug_type(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(codec, false); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); + + if (mbhc->mbhc_cb->mbhc_micbias_control) { + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, + MICB_ENABLE); + } else { + pr_err("%s: Mic Bias is not enabled\n", __func__); + return; + } + + /* Re-initialize button press completion object */ + reinit_completion(&mbhc->btn_press_compl); + wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + pr_debug("%s: leave\n", __func__); +} + +static void wcd_micbias_disable(struct wcd_mbhc *mbhc) +{ + if (mbhc->micbias_enable) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + mbhc->codec, MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) + mbhc->mbhc_cb->set_micbias_value( + mbhc->codec); + mbhc->micbias_enable = false; + } +} + +static int wcd_mbhc_get_plug_from_adc(int adc_result) + +{ + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; + + if (adc_result < WCD_MBHC_ADC_HPH_THRESHOLD_MV) + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + else if (adc_result > WCD_MBHC_ADC_HS_THRESHOLD_MV) + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + else + plug_type = MBHC_PLUG_TYPE_HEADSET; + pr_debug("%s: plug type is %d found\n", __func__, plug_type); + + return plug_type; +} + +static void wcd_correct_swch_plug(struct work_struct *work) +{ + struct wcd_mbhc *mbhc; + struct snd_soc_codec *codec; + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; + unsigned long timeout; + bool wrk_complete = false; + int pt_gnd_mic_swap_cnt = 0; + int no_gnd_mic_swap_cnt = 0; + bool is_pa_on = false, spl_hs = false, spl_hs_reported = false; + int ret = 0; + int spl_hs_count = 0; + int output_mv = 0; + int cross_conn; + int try = 0; + + pr_debug("%s: enter\n", __func__); + + mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); + codec = mbhc->codec; + + WCD_MBHC_RSC_LOCK(mbhc); + /* Mask ADC COMPLETE interrupt */ + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); + WCD_MBHC_RSC_UNLOCK(mbhc); + + /* Check for cross connection */ + do { + cross_conn = wcd_check_cross_conn(mbhc); + try++; + } while (try < GND_MIC_SWAP_THRESHOLD); + + if (cross_conn > 0) { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + pr_debug("%s: cross connection found, Plug type %d\n", + __func__, plug_type); + goto correct_plug_type; + } + /* Find plug type */ + output_mv = wcd_measure_adc_continuous(mbhc); + plug_type = wcd_mbhc_get_plug_from_adc(output_mv); + + /* + * Report plug type if it is either headset or headphone + * else start the 3 sec loop + */ + if ((plug_type == MBHC_PLUG_TYPE_HEADSET || + plug_type == MBHC_PLUG_TYPE_HEADPHONE) && + (!wcd_swch_level_remove(mbhc))) { + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + + /* + * Set DETECTION_DONE bit for HEADSET and ANC_HEADPHONE, + * so that btn press/release interrupt can be generated. + */ + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET || + mbhc->current_plug == MBHC_PLUG_TYPE_ANC_HEADPHONE) { + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); + } + +correct_plug_type: + timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS); + while (!time_after(jiffies, timeout)) { + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + wcd_micbias_disable(mbhc); + goto exit; + } + + /* allow sometime and re-check stop requested again */ + msleep(20); + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + wcd_micbias_disable(mbhc); + goto exit; + } + + msleep(180); + /* + * Use ADC single mode to minimize the chance of missing out + * btn press/release for HEADSET type during correct work. + */ + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + + /* + * instead of hogging system by contineous polling, wait for + * sometime and re-check stop request again. + */ + plug_type = wcd_mbhc_get_plug_from_adc(output_mv); + + if ((output_mv > WCD_MBHC_ADC_HS_THRESHOLD_MV) && + (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) { + spl_hs = wcd_mbhc_adc_check_for_spl_headset(mbhc, + &spl_hs_count); + + if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) { + output_mv = WCD_MBHC_ADC_HS_THRESHOLD_MV; + spl_hs = true; + mbhc->micbias_enable = true; + } + } + + if (mbhc->mbhc_cb->hph_pa_on_status) + is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec); + + if ((output_mv <= WCD_MBHC_ADC_HS_THRESHOLD_MV) && + (!is_pa_on)) { + /* Check for cross connection*/ + ret = wcd_check_cross_conn(mbhc); + if (ret < 0) + continue; + else if (ret > 0) { + pt_gnd_mic_swap_cnt++; + no_gnd_mic_swap_cnt = 0; + if (pt_gnd_mic_swap_cnt < + GND_MIC_SWAP_THRESHOLD) { + continue; + } else if (pt_gnd_mic_swap_cnt > + GND_MIC_SWAP_THRESHOLD) { + /* + * This is due to GND/MIC switch didn't + * work, Report unsupported plug. + */ + pr_debug("%s: switch did not work\n", + __func__); + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + goto report; + } else { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + } + } else { + no_gnd_mic_swap_cnt++; + pt_gnd_mic_swap_cnt = 0; + plug_type = wcd_mbhc_get_plug_from_adc( + output_mv); + if ((no_gnd_mic_swap_cnt < + GND_MIC_SWAP_THRESHOLD) && + (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) { + continue; + } else { + no_gnd_mic_swap_cnt = 0; + } + } + if ((pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) && + (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) { + /* + * if switch is toggled, check again, + * otherwise report unsupported plug + */ + if (mbhc->mbhc_cfg->swap_gnd_mic && + mbhc->mbhc_cfg->swap_gnd_mic(codec, + true)) { + pr_debug("%s: US_EU gpio present,flip switch\n" + , __func__); + continue; + } + } + } + + if (output_mv > WCD_MBHC_ADC_HS_THRESHOLD_MV) { + pr_debug("%s: cable is extension cable\n", __func__); + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + wrk_complete = true; + } else { + pr_debug("%s: cable might be headset: %d\n", __func__, + plug_type); + if (plug_type != MBHC_PLUG_TYPE_GND_MIC_SWAP) { + plug_type = wcd_mbhc_get_plug_from_adc( + output_mv); + if (!spl_hs_reported && + spl_hs_count == WCD_MBHC_SPL_HS_CNT) { + spl_hs_reported = true; + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, + plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); + continue; + } else if (spl_hs_reported) + continue; + /* + * Report headset only if not already reported + * and if there is not button press without + * release + */ + if ((mbhc->current_plug != + MBHC_PLUG_TYPE_HEADSET) && + (mbhc->current_plug != + MBHC_PLUG_TYPE_ANC_HEADPHONE) && + !wcd_swch_level_remove(mbhc)) { + pr_debug("%s: cable is %s headset\n", + __func__, + ((spl_hs_count == + WCD_MBHC_SPL_HS_CNT) ? + "special ":"")); + goto report; + } + } + wrk_complete = false; + } + } + if (!wrk_complete) { + /* + * If plug_tye is headset, we might have already reported either + * in detect_plug-type or in above while loop, no need to report + * again + */ + if ((plug_type == MBHC_PLUG_TYPE_HEADSET) || + (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE)) { + pr_debug("%s: plug_type:0x%x already reported\n", + __func__, mbhc->current_plug); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + goto enable_supply; + } + } + if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) { + if (wcd_is_special_headset(mbhc)) { + pr_debug("%s: Special headset found %d\n", + __func__, plug_type); + plug_type = MBHC_PLUG_TYPE_HEADSET; + } else { + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_ISRC_EN, 1); + } + } + +report: + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto exit; + } + + pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n", + __func__, plug_type, wrk_complete, + mbhc->btn_press_intr); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); +enable_supply: + /* + * Set DETECTION_DONE bit for HEADSET and ANC_HEADPHONE, + * so that btn press/release interrupt can be generated. + * For other plug type, clear the bit. + */ + if (plug_type == MBHC_PLUG_TYPE_HEADSET || + plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); + else + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); + + if (mbhc->mbhc_cb->mbhc_micbias_control) + wcd_mbhc_adc_update_fsm_source(mbhc, plug_type); +exit: + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->micbias_enable) + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, + MICB_DISABLE); + + /* + * If plug type is corrected from special headset to headphone, + * clear the micbias enable flag, set micbias back to 1.8V and + * disable micbias. + */ + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE && + mbhc->micbias_enable) { + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control( + codec, MIC_BIAS_2, + MICB_DISABLE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + codec, + MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) { + mbhc->mbhc_cb->set_micbias_value(codec); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + } + mbhc->micbias_enable = false; + } + + if (mbhc->mbhc_cfg->detect_extn_cable && + ((plug_type == MBHC_PLUG_TYPE_HEADPHONE) || + (plug_type == MBHC_PLUG_TYPE_HEADSET)) && + !mbhc->hs_detect_work_stop) { + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, true); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + + /* + * Enable ADC COMPLETE interrupt for HEADPHONE. + * Btn release may happen after the correct work, ADC COMPLETE + * interrupt needs to be captured to correct plug type. + */ + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) { + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + true); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true); + + mbhc->mbhc_cb->lock_sleep(mbhc, false); + pr_debug("%s: leave\n", __func__); +} + +static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + unsigned long timeout; + int adc_threshold, output_mv, retry = 0; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_LOCK(mbhc); + + timeout = jiffies + + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); + adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * + wcd_mbhc_get_micbias(mbhc)) / + WCD_MBHC_ADC_MICBIAS_MV); + do { + retry++; + /* + * read output_mv every 10ms to look for + * any change in IN2_P + */ + usleep_range(10000, 10100); + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); + + pr_debug("%s: Check for fake removal: output_mv %d\n", + __func__, output_mv); + if ((output_mv <= adc_threshold) && + retry > FAKE_REM_RETRY_ATTEMPTS) { + pr_debug("%s: headset is NOT actually removed\n", + __func__); + goto exit; + } + } while (!time_after(jiffies, timeout)); + + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low ", __func__); + goto exit; + } + + /* + * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE, + * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one + * when HEADPHONE is removed. + */ + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) + mbhc->extn_cable_hph_rem = true; + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + wcd_mbhc_elec_hs_report_unplug(mbhc); +exit: + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + + pr_debug("%s: enter\n", __func__); + + /* + * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE, + * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one + * when HEADPHONE is removed. + */ + if (mbhc->extn_cable_hph_rem == true) { + mbhc->extn_cable_hph_rem = false; + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; + } + + WCD_MBHC_RSC_LOCK(mbhc); + /* + * If current plug is headphone then there is no chance to + * get ADC complete interrupt, so connected cable should be + * headset not headphone. + */ + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 1); + wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); + WCD_MBHC_RSC_UNLOCK(mbhc); + return IRQ_HANDLED; + } + + if (!mbhc->mbhc_cfg->detect_extn_cable) { + pr_debug("%s: Returning as Extension cable feature not enabled\n", + __func__); + WCD_MBHC_RSC_UNLOCK(mbhc); + return IRQ_HANDLED; + } + + pr_debug("%s: Disable electrical headset insertion interrupt\n", + __func__); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_ISRC_EN, 0); + mbhc->is_extn_cable = true; + mbhc->btn_press_intr = false; + wcd_mbhc_adc_detect_plug_type(mbhc); + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; +} + +static struct wcd_mbhc_fn mbhc_fn = { + .wcd_mbhc_hs_ins_irq = wcd_mbhc_adc_hs_ins_irq, + .wcd_mbhc_hs_rem_irq = wcd_mbhc_adc_hs_rem_irq, + .wcd_mbhc_detect_plug_type = wcd_mbhc_adc_detect_plug_type, + .wcd_mbhc_detect_anc_plug_type = wcd_mbhc_adc_detect_anc_plug_type, + .wcd_cancel_hs_detect_plug = wcd_cancel_hs_detect_plug, +}; + +/* Function: wcd_mbhc_adc_init + * @mbhc: MBHC function pointer + * Description: Initialize MBHC ADC related function pointers to MBHC structure + */ +void wcd_mbhc_adc_init(struct wcd_mbhc *mbhc) +{ + if (!mbhc) { + pr_err("%s: mbhc is NULL\n", __func__); + return; + } + mbhc->mbhc_fn = &mbhc_fn; + INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); +} +EXPORT_SYMBOL(wcd_mbhc_adc_init); diff --git a/sound/soc/codecs/wcd-mbhc-adc.h b/sound/soc/codecs/wcd-mbhc-adc.h new file mode 100644 index 000000000000..31161089e793 --- /dev/null +++ b/sound/soc/codecs/wcd-mbhc-adc.h @@ -0,0 +1,35 @@ +/* Copyright (c) 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 __WCD_MBHC_ADC_H__ +#define __WCD_MBHC_ADC_H__ + +#include "wcd-mbhc-v2.h" + +enum wcd_mbhc_adc_mux_ctl { + MUX_CTL_AUTO = 0, + MUX_CTL_IN2P, + MUX_CTL_IN3P, + MUX_CTL_IN4P, + MUX_CTL_HPH_L, + MUX_CTL_HPH_R, + MUX_CTL_NONE, +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD_MBHC_ADC) +void wcd_mbhc_adc_init(struct wcd_mbhc *mbhc); +#else +static inline void wcd_mbhc_adc_init(struct wcd_mbhc *mbhc) +{ + +} +#endif +#endif /* __WCD_MBHC_ADC_H__ */ diff --git a/sound/soc/codecs/wcd-mbhc-legacy.c b/sound/soc/codecs/wcd-mbhc-legacy.c new file mode 100644 index 000000000000..745e2e81a556 --- /dev/null +++ b/sound/soc/codecs/wcd-mbhc-legacy.c @@ -0,0 +1,975 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd-mbhc-legacy.h" +#include "wcd-mbhc-v2.h" + +static int det_extn_cable_en; +module_param(det_extn_cable_en, int, 0664); +MODULE_PARM_DESC(det_extn_cable_en, "enable/disable extn cable detect"); + +static bool wcd_mbhc_detect_anc_plug_type(struct wcd_mbhc *mbhc) +{ + bool anc_mic_found = false; + u16 val, hs_comp_res, btn_status = 0; + unsigned long retry = 0; + int valid_plug_cnt = 0, invalid_plug_cnt = 0; + int btn_status_cnt = 0; + bool is_check_btn_press = false; + + + if (mbhc->mbhc_cfg->anc_micbias < MIC_BIAS_1 || + mbhc->mbhc_cfg->anc_micbias > MIC_BIAS_4) + return false; + + if (!mbhc->mbhc_cb->mbhc_micbias_control) + return false; + + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, val); + + if (val) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_ENABLE); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x2); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + /* + * wait for button debounce time 20ms. If 4-pole plug is inserted + * into 5-pole jack, then there will be a button press interrupt + * during anc plug detection. In that case though Hs_comp_res is 0, + * it should not be declared as ANC plug type + */ + usleep_range(20000, 20100); + + /* + * After enabling FSM, to handle slow insertion scenarios, + * check hs_comp_result for few times to see if the IN3 voltage + * is below the Vref + */ + do { + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto exit; + } + pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + + if (!hs_comp_res) { + valid_plug_cnt++; + is_check_btn_press = true; + } else + invalid_plug_cnt++; + /* Wait 1ms before taking another reading */ + usleep_range(1000, 1100); + + WCD_MBHC_REG_READ(WCD_MBHC_FSM_STATUS, btn_status); + if (btn_status) + btn_status_cnt++; + + retry++; + } while (retry < ANC_DETECT_RETRY_CNT); + + pr_debug("%s: valid: %d, invalid: %d, btn_status_cnt: %d\n", + __func__, valid_plug_cnt, invalid_plug_cnt, btn_status_cnt); + + /* decision logic */ + if ((valid_plug_cnt > invalid_plug_cnt) && is_check_btn_press && + (btn_status_cnt == 0)) + anc_mic_found = true; +exit: + if (!val) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 0); + + mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec, + mbhc->mbhc_cfg->anc_micbias, + MICB_DISABLE); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x0); + pr_debug("%s: anc mic %sfound\n", __func__, + anc_mic_found ? "" : "not "); + return anc_mic_found; +} + +/* To determine if cross connection occurred */ +static int wcd_check_cross_conn(struct wcd_mbhc *mbhc) +{ + u16 swap_res = 0; + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_NONE; + s16 reg1 = 0; + bool hphl_sch_res = 0, hphr_sch_res = 0; + + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + return -EINVAL; + } + + /* If PA is enabled, dont check for cross-connection */ + if (mbhc->mbhc_cb->hph_pa_on_status) + if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) + return false; + + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, reg1); + /* + * Check if there is any cross connection, + * Micbias and schmitt trigger (HPHL-HPHR) + * needs to be enabled. For some codecs like wcd9335, + * pull-up will already be enabled when this function + * is called for cross-connection identification. No + * need to enable micbias in that case. + */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 2); + + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, swap_res); + pr_debug("%s: swap_res%x\n", __func__, swap_res); + + /* + * Read reg hphl and hphr schmitt result with cross connection + * bit. These bits will both be "0" in case of cross connection + * otherwise, they stay at 1 + */ + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch_res); + WCD_MBHC_REG_READ(WCD_MBHC_HPHR_SCHMT_RESULT, hphr_sch_res); + if (!(hphl_sch_res || hphr_sch_res)) { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + pr_debug("%s: Cross connection identified\n", __func__); + } else { + pr_debug("%s: No Cross connection found\n", __func__); + } + + /* Disable schmitt trigger and restore micbias */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, reg1); + pr_debug("%s: leave, plug type: %d\n", __func__, plug_type); + + return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false; +} + +static bool wcd_is_special_headset(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + int delay = 0, rc; + bool ret = false; + u16 hs_comp_res; + bool is_spl_hs = false; + + /* + * Increase micbias to 2.7V to detect headsets with + * threshold on microphone + */ + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) { + pr_debug("%s: callback fn micb_ctrl_thr_mic not defined\n", + __func__); + return false; + } else if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) { + rc = mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec, + MIC_BIAS_2, true); + if (rc) { + pr_err("%s: Micbias control for thr mic failed, rc: %d\n", + __func__, rc); + return false; + } + } + + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + + pr_debug("%s: special headset, start register writes\n", __func__); + + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + while (!is_spl_hs) { + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + break; + } + delay = delay + 50; + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) { + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + true); + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_SET_VAL, + true); + } + /* Wait for 50msec for MICBIAS to settle down */ + msleep(50); + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, true); + /* Wait for 50msec for FSM to update result values */ + msleep(50); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + if (!(hs_comp_res)) { + pr_debug("%s: Special headset detected in %d msecs\n", + __func__, (delay * 2)); + is_spl_hs = true; + } + if (delay == SPECIAL_HS_DETECT_TIME_MS) { + pr_debug("%s: Spl headset didn't get detect in 4 sec\n", + __func__); + break; + } + } + if (is_spl_hs) { + pr_debug("%s: Headset with threshold found\n", __func__); + mbhc->micbias_enable = true; + ret = true; + } + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + false); + if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable) + mbhc->mbhc_cb->set_micbias_value(codec); + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, false); + + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic && + !mbhc->micbias_enable) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec, MIC_BIAS_2, + false); + + pr_debug("%s: leave, micb_enable: %d\n", __func__, + mbhc->micbias_enable); + return ret; +} + +static void wcd_mbhc_update_fsm_source(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + bool micbias2; + + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_2); + switch (plug_type) { + case MBHC_PLUG_TYPE_HEADPHONE: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + break; + case MBHC_PLUG_TYPE_HEADSET: + case MBHC_PLUG_TYPE_ANC_HEADPHONE: + if (!mbhc->is_hs_recording && !micbias2) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + break; + default: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + break; + + }; +} + +static void wcd_enable_mbhc_supply(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + + struct snd_soc_codec *codec = mbhc->codec; + + /* + * Do not disable micbias if recording is going on or + * headset is inserted on the other side of the extn + * cable. If headset has been detected current source + * needs to be kept enabled for button detection to work. + * If the accessory type is invalid or unsupported, we + * dont need to enable either of them. + */ + if (det_extn_cable_en && mbhc->is_extn_cable && + mbhc->mbhc_cb && mbhc->mbhc_cb->extn_use_mb && + mbhc->mbhc_cb->extn_use_mb(codec)) { + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE || + plug_type == MBHC_PLUG_TYPE_HEADSET) + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + } else { + if (plug_type == MBHC_PLUG_TYPE_HEADSET) { + if (mbhc->is_hs_recording || mbhc->micbias_enable) { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + } else if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, + &mbhc->event_state)) || + (test_bit(WCD_MBHC_EVENT_PA_HPHR, + &mbhc->event_state))) { + wcd_enable_curr_micbias(mbhc, + WCD_MBHC_EN_PULLUP); + } else { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + } + } else if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + } else { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE); + } + } +} + +static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc, + int *spl_hs_cnt) +{ + u16 hs_comp_res_1_8v = 0, hs_comp_res_2_7v = 0; + bool spl_hs = false; + + if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + goto done; + + if (!spl_hs_cnt) { + pr_err("%s: spl_hs_cnt is NULL\n", __func__); + goto done; + } + /* Read back hs_comp_res @ 1.8v Micbias */ + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_1_8v); + if (!hs_comp_res_1_8v) { + spl_hs = false; + goto done; + } + + /* Bump up MB2 to 2.7v */ + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + mbhc->mbhc_cfg->mbhc_micbias, true); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + usleep_range(10000, 10100); + + /* Read back HS_COMP_RESULT */ + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_2_7v); + if (!hs_comp_res_2_7v && hs_comp_res_1_8v) + spl_hs = true; + + if (spl_hs) + *spl_hs_cnt += 1; + + /* MB2 back to 1.8v */ + if (*spl_hs_cnt != WCD_MBHC_SPL_HS_CNT) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec, + mbhc->mbhc_cfg->mbhc_micbias, false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + usleep_range(10000, 10100); + } + + if (spl_hs) + pr_debug("%s: Detected special HS (%d)\n", __func__, spl_hs); + +done: + return spl_hs; +} + +/* should be called under interrupt context that hold suspend */ +static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: scheduling correct_swch_plug\n", __func__); + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + mbhc->hs_detect_work_stop = false; + mbhc->mbhc_cb->lock_sleep(mbhc, true); + schedule_work(work); +} + +/* called under codec_resource_lock acquisition */ +static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc, + struct work_struct *work) +{ + pr_debug("%s: Canceling correct_plug_swch\n", __func__); + mbhc->hs_detect_work_stop = true; + WCD_MBHC_RSC_UNLOCK(mbhc); + if (cancel_work_sync(work)) { + pr_debug("%s: correct_plug_swch is canceled\n", + __func__); + mbhc->mbhc_cb->lock_sleep(mbhc, false); + } + WCD_MBHC_RSC_LOCK(mbhc); +} + +/* called under codec_resource_lock acquisition */ +static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + bool micbias1 = false; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(codec, false); + + if (mbhc->mbhc_cb->micbias_enable_status) + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true); + + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, + MICB_ENABLE); + else + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + + /* Re-initialize button press completion object */ + reinit_completion(&mbhc->btn_press_compl); + wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch); + pr_debug("%s: leave\n", __func__); +} + +static void wcd_correct_swch_plug(struct work_struct *work) +{ + struct wcd_mbhc *mbhc; + struct snd_soc_codec *codec; + enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; + unsigned long timeout; + u16 hs_comp_res = 0, hphl_sch = 0, mic_sch = 0, btn_result = 0; + bool wrk_complete = false; + int pt_gnd_mic_swap_cnt = 0; + int no_gnd_mic_swap_cnt = 0; + bool is_pa_on = false, spl_hs = false, spl_hs_reported = false; + bool micbias2 = false; + bool micbias1 = false; + int ret = 0; + int rc, spl_hs_count = 0; + int cross_conn; + int try = 0; + + pr_debug("%s: enter\n", __func__); + + mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); + codec = mbhc->codec; + + /* + * Enable micbias/pullup for detection in correct work. + * This work will get scheduled from detect_plug_type which + * will already request for pullup/micbias. If the pullup/micbias + * is handled with ref-counts by individual codec drivers, there is + * no need to enabale micbias/pullup here + */ + + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + + /* Enable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + /* + * Check for any button press interrupts before starting 3-sec + * loop. + */ + rc = wait_for_completion_timeout(&mbhc->btn_press_compl, + msecs_to_jiffies(WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS)); + + WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + + if (!rc) { + pr_debug("%s No btn press interrupt\n", __func__); + if (!btn_result && !hs_comp_res) + plug_type = MBHC_PLUG_TYPE_HEADSET; + else if (!btn_result && hs_comp_res) + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + else + plug_type = MBHC_PLUG_TYPE_INVALID; + } else { + if (!btn_result && !hs_comp_res) + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + else + plug_type = MBHC_PLUG_TYPE_INVALID; + } + + do { + cross_conn = wcd_check_cross_conn(mbhc); + try++; + } while (try < GND_MIC_SWAP_THRESHOLD); + + /* + * Check for cross connection 4 times. + * Consider the result of the fourth iteration. + */ + if (cross_conn > 0) { + pr_debug("%s: cross con found, start polling\n", + __func__); + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + pr_debug("%s: Plug found, plug type is %d\n", + __func__, plug_type); + goto correct_plug_type; + } + + if ((plug_type == MBHC_PLUG_TYPE_HEADSET || + plug_type == MBHC_PLUG_TYPE_HEADPHONE) && + (!wcd_swch_level_remove(mbhc))) { + WCD_MBHC_RSC_LOCK(mbhc); + if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, + 0); + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + +correct_plug_type: + + timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS); + while (!time_after(jiffies, timeout)) { + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + wcd_enable_curr_micbias(mbhc, + WCD_MBHC_EN_NONE); + if (mbhc->micbias_enable) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + mbhc->codec, MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) + mbhc->mbhc_cb->set_micbias_value( + mbhc->codec); + mbhc->micbias_enable = false; + } + goto exit; + } + if (mbhc->btn_press_intr) { + wcd_cancel_btn_work(mbhc); + mbhc->btn_press_intr = false; + } + /* Toggle FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + + /* allow sometime and re-check stop requested again */ + msleep(20); + if (mbhc->hs_detect_work_stop) { + pr_debug("%s: stop requested: %d\n", __func__, + mbhc->hs_detect_work_stop); + wcd_enable_curr_micbias(mbhc, + WCD_MBHC_EN_NONE); + if (mbhc->micbias_enable) { + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + mbhc->codec, MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) + mbhc->mbhc_cb->set_micbias_value( + mbhc->codec); + mbhc->micbias_enable = false; + } + goto exit; + } + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res); + + pr_debug("%s: hs_comp_res: %x\n", __func__, hs_comp_res); + if (mbhc->mbhc_cb->hph_pa_on_status) + is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(codec); + + /* + * instead of hogging system by contineous polling, wait for + * sometime and re-check stop request again. + */ + msleep(180); + if (hs_comp_res && (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) { + spl_hs = wcd_mbhc_check_for_spl_headset(mbhc, + &spl_hs_count); + + if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) { + hs_comp_res = 0; + spl_hs = true; + mbhc->micbias_enable = true; + } + } + + if ((!hs_comp_res) && (!is_pa_on)) { + /* Check for cross connection*/ + ret = wcd_check_cross_conn(mbhc); + if (ret < 0) { + continue; + } else if (ret > 0) { + pt_gnd_mic_swap_cnt++; + no_gnd_mic_swap_cnt = 0; + if (pt_gnd_mic_swap_cnt < + GND_MIC_SWAP_THRESHOLD) { + continue; + } else if (pt_gnd_mic_swap_cnt > + GND_MIC_SWAP_THRESHOLD) { + /* + * This is due to GND/MIC switch didn't + * work, Report unsupported plug. + */ + pr_debug("%s: switch didn't work\n", + __func__); + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + goto report; + } else { + plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; + } + } else { + no_gnd_mic_swap_cnt++; + pt_gnd_mic_swap_cnt = 0; + plug_type = MBHC_PLUG_TYPE_HEADSET; + if ((no_gnd_mic_swap_cnt < + GND_MIC_SWAP_THRESHOLD) && + (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) { + continue; + } else { + no_gnd_mic_swap_cnt = 0; + } + } + if ((pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) && + (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) { + /* + * if switch is toggled, check again, + * otherwise report unsupported plug + */ + if (mbhc->mbhc_cfg->swap_gnd_mic && + mbhc->mbhc_cfg->swap_gnd_mic(codec, + true)) { + pr_debug("%s: US_EU gpio present,flip switch\n" + , __func__); + continue; + } + } + } + + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch); + WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch); + if (hs_comp_res && !(hphl_sch || mic_sch)) { + pr_debug("%s: cable is extension cable\n", __func__); + plug_type = MBHC_PLUG_TYPE_HIGH_HPH; + wrk_complete = true; + } else { + pr_debug("%s: cable might be headset: %d\n", __func__, + plug_type); + if (!(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) { + plug_type = MBHC_PLUG_TYPE_HEADSET; + if (!spl_hs_reported && + spl_hs_count == WCD_MBHC_SPL_HS_CNT) { + spl_hs_reported = true; + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, + plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); + continue; + } else if (spl_hs_reported) + continue; + /* + * Report headset only if not already reported + * and if there is not button press without + * release + */ + if (((mbhc->current_plug != + MBHC_PLUG_TYPE_HEADSET) && + (mbhc->current_plug != + MBHC_PLUG_TYPE_ANC_HEADPHONE)) && + !wcd_swch_level_remove(mbhc) && + !mbhc->btn_press_intr) { + pr_debug("%s: cable is %sheadset\n", + __func__, + ((spl_hs_count == + WCD_MBHC_SPL_HS_CNT) ? + "special ":"")); + goto report; + } + } + wrk_complete = false; + } + } + if (!wrk_complete && mbhc->btn_press_intr) { + pr_debug("%s: Can be slow insertion of headphone\n", __func__); + wcd_cancel_btn_work(mbhc); + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + } + /* + * If plug_tye is headset, we might have already reported either in + * detect_plug-type or in above while loop, no need to report again + */ + if (!wrk_complete && ((plug_type == MBHC_PLUG_TYPE_HEADSET) || + (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE))) { + pr_debug("%s: plug_type:0x%x already reported\n", + __func__, mbhc->current_plug); + goto enable_supply; + } + + if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH && + (!det_extn_cable_en)) { + if (wcd_is_special_headset(mbhc)) { + pr_debug("%s: Special headset found %d\n", + __func__, plug_type); + plug_type = MBHC_PLUG_TYPE_HEADSET; + goto report; + } + } + +report: + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low\n", __func__); + goto exit; + } + if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP && mbhc->btn_press_intr) { + pr_debug("%s: insertion of headphone with swap\n", __func__); + wcd_cancel_btn_work(mbhc); + plug_type = MBHC_PLUG_TYPE_HEADPHONE; + } + pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n", + __func__, plug_type, wrk_complete, + mbhc->btn_press_intr); + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_find_plug_and_report(mbhc, plug_type); + WCD_MBHC_RSC_UNLOCK(mbhc); +enable_supply: + if (mbhc->mbhc_cb->mbhc_micbias_control) + wcd_mbhc_update_fsm_source(mbhc, plug_type); + else + wcd_enable_mbhc_supply(mbhc, plug_type); +exit: + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->micbias_enable) + mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2, + MICB_DISABLE); + + /* + * If plug type is corrected from special headset to headphone, + * clear the micbias enable flag, set micbias back to 1.8V and + * disable micbias. + */ + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE && + mbhc->micbias_enable) { + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control( + codec, MIC_BIAS_2, + MICB_DISABLE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + codec, + MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) { + mbhc->mbhc_cb->set_micbias_value(codec); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + } + mbhc->micbias_enable = false; + } + + if (mbhc->mbhc_cb->micbias_enable_status) { + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_2); + } + + if (mbhc->mbhc_cfg->detect_extn_cable && + ((plug_type == MBHC_PLUG_TYPE_HEADPHONE) || + (plug_type == MBHC_PLUG_TYPE_HEADSET)) && + !mbhc->hs_detect_work_stop) { + WCD_MBHC_RSC_LOCK(mbhc); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, true); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, micbias2); + + if (mbhc->mbhc_cb->hph_pull_down_ctrl) + mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true); + + mbhc->mbhc_cb->lock_sleep(mbhc, false); + pr_debug("%s: leave\n", __func__); +} + +static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + u8 hs_comp_result = 0, hphl_sch = 0, mic_sch = 0; + static u16 hphl_trigerred; + static u16 mic_trigerred; + unsigned long timeout; + bool removed = true; + int retry = 0; + + pr_debug("%s: enter\n", __func__); + + WCD_MBHC_RSC_LOCK(mbhc); + + timeout = jiffies + + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); + do { + retry++; + /* + * read the result register every 10ms to look for + * any change in HS_COMP_RESULT bit + */ + usleep_range(10000, 10100); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result); + pr_debug("%s: Check result reg for fake removal: hs_comp_res %x\n", + __func__, hs_comp_result); + if ((!hs_comp_result) && + retry > FAKE_REM_RETRY_ATTEMPTS) { + removed = false; + break; + } + } while (!time_after(jiffies, timeout)); + + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low ", __func__); + goto exit; + } + pr_debug("%s: headset %s actually removed\n", __func__, + removed ? "" : "not "); + + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch); + WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch); + WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result); + + if (removed) { + if (!(hphl_sch && mic_sch && hs_comp_result)) { + /* + * extension cable is still plugged in + * report it as LINEOUT device + */ + goto report_unplug; + } else { + if (!mic_sch) { + mic_trigerred++; + pr_debug("%s: Removal MIC trigerred %d\n", + __func__, mic_trigerred); + } + if (!hphl_sch) { + hphl_trigerred++; + pr_debug("%s: Removal HPHL trigerred %d\n", + __func__, hphl_trigerred); + } + if (mic_trigerred && hphl_trigerred) { + /* + * extension cable is still plugged in + * report it as LINEOUT device + */ + goto report_unplug; + } + } + } +exit: + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; + +report_unplug: + wcd_mbhc_elec_hs_report_unplug(mbhc); + hphl_trigerred = 0; + mic_trigerred = 0; + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_hs_ins_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + bool detection_type = 0, hphl_sch = 0, mic_sch = 0; + u16 elect_result = 0; + static u16 hphl_trigerred; + static u16 mic_trigerred; + + pr_debug("%s: enter\n", __func__); + if (!mbhc->mbhc_cfg->detect_extn_cable) { + pr_debug("%s: Returning as Extension cable feature not enabled\n", + __func__); + return IRQ_HANDLED; + } + WCD_MBHC_RSC_LOCK(mbhc); + + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_DETECTION_TYPE, detection_type); + WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, elect_result); + + pr_debug("%s: detection_type %d, elect_result %x\n", __func__, + detection_type, elect_result); + if (detection_type) { + /* check if both Left and MIC Schmitt triggers are triggered */ + WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch); + WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch); + if (hphl_sch && mic_sch) { + /* Go for plug type determination */ + pr_debug("%s: Go for plug type determination\n", + __func__); + goto determine_plug; + + } else { + if (mic_sch) { + mic_trigerred++; + pr_debug("%s: Insertion MIC trigerred %d\n", + __func__, mic_trigerred); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_ELECT_SCHMT_ISRC, + 0); + msleep(20); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_ELECT_SCHMT_ISRC, + 1); + } + if (hphl_sch) { + hphl_trigerred++; + pr_debug("%s: Insertion HPHL trigerred %d\n", + __func__, hphl_trigerred); + } + if (mic_trigerred && hphl_trigerred) { + /* Go for plug type determination */ + pr_debug("%s: Go for plug type determination\n", + __func__); + goto determine_plug; + } + } + } + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; + +determine_plug: + /* + * Disable HPHL trigger and MIC Schmitt triggers. + * Setup for insertion detection. + */ + pr_debug("%s: Disable insertion interrupt\n", __func__); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + false); + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + hphl_trigerred = 0; + mic_trigerred = 0; + mbhc->is_extn_cable = true; + mbhc->btn_press_intr = false; + wcd_mbhc_detect_plug_type(mbhc); + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return IRQ_HANDLED; +} + +static struct wcd_mbhc_fn mbhc_fn = { + .wcd_mbhc_hs_ins_irq = wcd_mbhc_hs_ins_irq, + .wcd_mbhc_hs_rem_irq = wcd_mbhc_hs_rem_irq, + .wcd_mbhc_detect_plug_type = wcd_mbhc_detect_plug_type, + .wcd_mbhc_detect_anc_plug_type = wcd_mbhc_detect_anc_plug_type, + .wcd_cancel_hs_detect_plug = wcd_cancel_hs_detect_plug, +}; + +/* Function: wcd_mbhc_legacy_init + * @mbhc: MBHC function pointer + * Description: Initialize MBHC legacy based function pointers to MBHC structure + */ +void wcd_mbhc_legacy_init(struct wcd_mbhc *mbhc) +{ + if (!mbhc) { + pr_err("%s: mbhc is NULL\n", __func__); + return; + } + mbhc->mbhc_fn = &mbhc_fn; + INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug); +} +EXPORT_SYMBOL(wcd_mbhc_legacy_init); diff --git a/sound/soc/codecs/wcd-mbhc-legacy.h b/sound/soc/codecs/wcd-mbhc-legacy.h new file mode 100644 index 000000000000..594393d47896 --- /dev/null +++ b/sound/soc/codecs/wcd-mbhc-legacy.h @@ -0,0 +1,26 @@ +/* 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 __WCD_MBHC_LEGACY_H__ +#define __WCD_MBHC_LEGACY_H__ + +#include "wcdcal-hwdep.h" +#include "wcd-mbhc-v2.h" + +#ifdef CONFIG_SND_SOC_WCD_MBHC_LEGACY +void wcd_mbhc_legacy_init(struct wcd_mbhc *mbhc); +#else +static inline void wcd_mbhc_legacy_init(struct wcd_mbhc *mbhc) +{ +} +#endif + +#endif /* __WCD_MBHC_LEGACY_H__ */ diff --git a/sound/soc/codecs/wcd-mbhc-v2-api.h b/sound/soc/codecs/wcd-mbhc-v2-api.h new file mode 100644 index 000000000000..7b6e94507ad0 --- /dev/null +++ b/sound/soc/codecs/wcd-mbhc-v2-api.h @@ -0,0 +1,60 @@ +/* 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 __WCD_MBHC_V2_API_H__ +#define __WCD_MBHC_V2_API_H__ + +#include "wcd-mbhc-v2.h" + +#if IS_ENABLED(CONFIG_SND_SOC_WCD_MBHC) +int wcd_mbhc_start(struct wcd_mbhc *mbhc, + struct wcd_mbhc_config *mbhc_cfg); +void wcd_mbhc_stop(struct wcd_mbhc *mbhc); +int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, + struct wcd_mbhc_register *wcd_mbhc_regs, + bool impedance_det_en); +int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr); +void wcd_mbhc_deinit(struct wcd_mbhc *mbhc); + +#else +static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc) +{ +} +int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, + struct wcd_mbhc_register *wcd_mbhc_regs, + bool impedance_det_en) +{ + return 0; +} +static inline int wcd_mbhc_start(struct wcd_mbhc *mbhc, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +static inline int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, + uint32_t *zl, + uint32_t *zr) +{ + *zl = 0; + *zr = 0; + return -EINVAL; +} +static inline void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) +{ +} +#endif + +#endif /* __WCD_MBHC_V2_API_H__ */ diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c new file mode 100644 index 000000000000..3b2426dc7234 --- /dev/null +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -0,0 +1,2074 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcdcal-hwdep.h" +#include "wcd-mbhc-legacy.h" +#include "wcd-mbhc-adc.h" +#include "wcd-mbhc-v2-api.h" + +void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc, + struct snd_soc_jack *jack, int status, int mask) +{ + snd_soc_jack_report(jack, status, mask); +} +EXPORT_SYMBOL(wcd_mbhc_jack_report); + +static void __hphocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status, + int irq) +{ + struct snd_soc_codec *codec = mbhc->codec; + + dev_dbg(codec->dev, "%s: clear ocp status %x\n", + __func__, jack_status); + + if (mbhc->hph_status & jack_status) { + mbhc->hph_status &= ~jack_status; + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, WCD_MBHC_JACK_MASK); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1); + /* + * reset retry counter as PA is turned off signifying + * start of new OCP detection session + */ + if (mbhc->intr_ids->hph_left_ocp) + mbhc->hphlocp_cnt = 0; + else + mbhc->hphrocp_cnt = 0; + mbhc->mbhc_cb->irq_control(codec, irq, true); + } +} + +static void hphrocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status) +{ + __hphocp_off_report(mbhc, SND_JACK_OC_HPHR, + mbhc->intr_ids->hph_right_ocp); +} + +static void hphlocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status) +{ + __hphocp_off_report(mbhc, SND_JACK_OC_HPHL, + mbhc->intr_ids->hph_left_ocp); +} + +static void wcd_program_hs_vref(struct wcd_mbhc *mbhc) +{ + struct wcd_mbhc_plug_type_cfg *plug_type_cfg; + struct snd_soc_codec *codec = mbhc->codec; + u32 reg_val; + + plug_type_cfg = WCD_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration); + reg_val = ((plug_type_cfg->v_hs_max - HS_VREF_MIN_VAL) / 100); + + dev_dbg(codec->dev, "%s: reg_val = %x\n", __func__, reg_val); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_VREF, reg_val); +} + +static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias) +{ + struct wcd_mbhc_btn_detect_cfg *btn_det; + struct snd_soc_codec *codec = mbhc->codec; + struct snd_soc_card *card = codec->component.card; + s16 *btn_low, *btn_high; + + if (mbhc->mbhc_cfg->calibration == NULL) { + dev_err(card->dev, "%s: calibration data is NULL\n", __func__); + return; + } + + btn_det = WCD_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration); + btn_low = btn_det->_v_btn_low; + btn_high = ((void *)&btn_det->_v_btn_low) + + (sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn); + + mbhc->mbhc_cb->set_btn_thr(codec, btn_low, btn_high, btn_det->num_btn, + micbias); +} + +void wcd_enable_curr_micbias(const struct wcd_mbhc *mbhc, + const enum wcd_mbhc_cs_mb_en_flag cs_mb_en) +{ + + /* + * Some codecs handle micbias/pullup enablement in codec + * drivers itself and micbias is not needed for regular + * plug type detection. So if micbias_control callback function + * is defined, just return. + */ + if (mbhc->mbhc_cb->mbhc_micbias_control) + return; + + pr_debug("%s: enter, cs_mb_en: %d\n", __func__, cs_mb_en); + + switch (cs_mb_en) { + case WCD_MBHC_EN_CS: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + /* Program Button threshold registers as per CS */ + wcd_program_btn_threshold(mbhc, false); + break; + case WCD_MBHC_EN_MB: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + + /* Disable PULL_UP_EN & enable MICBIAS */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 2); + /* Program Button threshold registers as per MICBIAS */ + wcd_program_btn_threshold(mbhc, true); + break; + case WCD_MBHC_EN_PULLUP: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 1); + /* Program Button threshold registers as per MICBIAS */ + wcd_program_btn_threshold(mbhc, true); + break; + case WCD_MBHC_EN_NONE: + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + break; + default: + pr_debug("%s: Invalid parameter", __func__); + break; + } + + pr_debug("%s: exit\n", __func__); +} +EXPORT_SYMBOL(wcd_enable_curr_micbias); + +static const char *wcd_mbhc_get_event_string(int event) +{ + switch (event) { + case WCD_EVENT_PRE_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_OFF); + case WCD_EVENT_POST_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_OFF); + case WCD_EVENT_PRE_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_ON); + case WCD_EVENT_POST_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_ON); + case WCD_EVENT_PRE_HPHL_PA_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_ON); + case WCD_EVENT_POST_HPHL_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHL_PA_OFF); + case WCD_EVENT_PRE_HPHR_PA_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_ON); + case WCD_EVENT_POST_HPHR_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHR_PA_OFF); + case WCD_EVENT_PRE_HPHR_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_OFF); + case WCD_EVENT_PRE_HPHL_PA_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_OFF); + case WCD_EVENT_POST_DAPM_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_ON); + case WCD_EVENT_PRE_DAPM_MICBIAS_2_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_ON); + case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_OFF); + case WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF); + case WCD_EVENT_OCP_OFF: + return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_OFF); + case WCD_EVENT_OCP_ON: + return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_ON); + case WCD_EVENT_INVALID: + default: + return WCD_MBHC_STRINGIFY(WCD_EVENT_INVALID); + } +} + +static int wcd_event_notify(struct notifier_block *self, unsigned long val, + void *data) +{ + struct wcd_mbhc *mbhc = (struct wcd_mbhc *)data; + enum wcd_notify_event event = (enum wcd_notify_event)val; + struct snd_soc_codec *codec = mbhc->codec; + bool micbias2 = false; + bool micbias1 = false; + u8 fsm_en = 0; + + pr_debug("%s: event %s (%d)\n", __func__, + wcd_mbhc_get_event_string(event), event); + if (mbhc->mbhc_cb->micbias_enable_status) { + micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_2); + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + } + switch (event) { + /* MICBIAS usage change */ + case WCD_EVENT_POST_DAPM_MICBIAS_2_ON: + mbhc->is_hs_recording = true; + pr_debug("%s: is_capture: %d\n", __func__, + mbhc->is_hs_recording); + break; + case WCD_EVENT_POST_MICBIAS_2_ON: + if (!mbhc->micbias_enable) + goto out_micb_en; + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) { + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + true); + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_SET_VAL, + true); + /* + * Special headset needs MICBIAS as 2.7V so wait for + * 50 msec for the MICBIAS to reach 2.7 volts. + */ + msleep(50); + } + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, true); + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_PRECHARGE, + false); +out_micb_en: + /* Disable current source if micbias enabled */ + if (mbhc->mbhc_cb->mbhc_micbias_control) { + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + if (fsm_en) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, + 0); + } else { + mbhc->is_hs_recording = true; + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + } + /* configure cap settings properly when micbias is enabled */ + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true); + break; + case WCD_EVENT_PRE_MICBIAS_2_OFF: + /* + * Before MICBIAS_2 is turned off, if FSM is enabled, + * make sure current source is enabled so as to detect + * button press/release events + */ + if (mbhc->mbhc_cb->mbhc_micbias_control && + !mbhc->micbias_enable) { + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + if (fsm_en) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, + 3); + } + break; + /* MICBIAS usage change */ + case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF: + mbhc->is_hs_recording = false; + pr_debug("%s: is_capture: %d\n", __func__, + mbhc->is_hs_recording); + break; + case WCD_EVENT_POST_MICBIAS_2_OFF: + if (!mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->is_hs_recording = false; + if (mbhc->micbias_enable) { + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + break; + } + + if (mbhc->mbhc_cb->set_auto_zeroing) + mbhc->mbhc_cb->set_auto_zeroing(codec, false); + if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable) + mbhc->mbhc_cb->set_micbias_value(codec); + /* Enable PULL UP if PA's are enabled */ + if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) || + (test_bit(WCD_MBHC_EVENT_PA_HPHR, + &mbhc->event_state))) + /* enable pullup and cs, disable mb */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP); + else + /* enable current source and disable mb, pullup*/ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + + /* configure cap settings properly when micbias is disabled */ + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false); + break; + case WCD_EVENT_PRE_HPHL_PA_OFF: + mutex_lock(&mbhc->hphl_pa_lock); + break; + case WCD_EVENT_POST_HPHL_PA_OFF: + clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + if (mbhc->hph_status & SND_JACK_OC_HPHL) + hphlocp_off_report(mbhc, SND_JACK_OC_HPHL); + clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, pullup & enable cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + mutex_unlock(&mbhc->hphl_pa_lock); + break; + case WCD_EVENT_PRE_HPHR_PA_OFF: + mutex_lock(&mbhc->hphr_pa_lock); + break; + case WCD_EVENT_POST_HPHR_PA_OFF: + clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + if (mbhc->hph_status & SND_JACK_OC_HPHR) + hphrocp_off_report(mbhc, SND_JACK_OC_HPHR); + clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, pullup & enable cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); + mutex_unlock(&mbhc->hphr_pa_lock); + break; + case WCD_EVENT_PRE_HPHL_PA_ON: + set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, enable pullup & cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP); + break; + case WCD_EVENT_PRE_HPHR_PA_ON: + set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state); + /* check if micbias is enabled */ + if (micbias2) + /* Disable cs, pullup & enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + else + /* Disable micbias, enable pullup & cs */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP); + break; + case WCD_EVENT_OCP_OFF: + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + false); + break; + case WCD_EVENT_OCP_ON: + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + true); + break; + default: + break; + } + return 0; +} + +int wcd_cancel_btn_work(struct wcd_mbhc *mbhc) +{ + int r; + + r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork); + /* + * if scheduled mbhc.mbhc_btn_dwork is canceled from here, + * we have to unlock from here instead btn_work + */ + if (r) + mbhc->mbhc_cb->lock_sleep(mbhc, false); + return r; +} +EXPORT_SYMBOL(wcd_cancel_btn_work); + +bool wcd_swch_level_remove(struct wcd_mbhc *mbhc) +{ + u16 result2 = 0; + + WCD_MBHC_REG_READ(WCD_MBHC_SWCH_LEVEL_REMOVE, result2); + return (result2) ? true : false; +} +EXPORT_SYMBOL(wcd_swch_level_remove); + +static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc) +{ + bool pa_turned_on = false; + u8 wg_time = 0; + + WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time); + wg_time += 1; + + mutex_lock(&mbhc->hphr_pa_lock); + if (test_and_clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, + &mbhc->hph_pa_dac_state)) { + pr_debug("%s: HPHR clear flag and enable PA\n", __func__); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_PA_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 1); + pa_turned_on = true; + } + mutex_unlock(&mbhc->hphr_pa_lock); + mutex_lock(&mbhc->hphl_pa_lock); + if (test_and_clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, + &mbhc->hph_pa_dac_state)) { + pr_debug("%s: HPHL clear flag and enable PA\n", __func__); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 1); + pa_turned_on = true; + } + mutex_unlock(&mbhc->hphl_pa_lock); + + if (pa_turned_on) { + pr_debug("%s: PA was turned on by MBHC and not by DAPM\n", + __func__); + usleep_range(wg_time * 1000, wg_time * 1000 + 50); + } +} + +static bool wcd_mbhc_is_hph_pa_on(struct wcd_mbhc *mbhc) +{ + bool hph_pa_on = false; + + WCD_MBHC_REG_READ(WCD_MBHC_HPH_PA_EN, hph_pa_on); + + return (hph_pa_on) ? true : false; +} + +static void wcd_mbhc_set_and_turnoff_hph_padac(struct wcd_mbhc *mbhc) +{ + u8 wg_time = 0; + + WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time); + wg_time += 1; + + /* If headphone PA is on, check if userspace receives + * removal event to sync-up PA's state + */ + if (wcd_mbhc_is_hph_pa_on(mbhc)) { + pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__); + set_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + set_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 0); + } else { + pr_debug("%s PA is off\n", __func__); + } + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 0); + usleep_range(wg_time * 1000, wg_time * 1000 + 50); +} + +int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + *zl = mbhc->zl; + *zr = mbhc->zr; + + if (*zl && *zr) + return 0; + else + return -EINVAL; +} +EXPORT_SYMBOL(wcd_mbhc_get_impedance); + +void wcd_mbhc_hs_elec_irq(struct wcd_mbhc *mbhc, int irq_type, + bool enable) +{ + int irq; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (irq_type == WCD_MBHC_ELEC_HS_INS) + irq = mbhc->intr_ids->mbhc_hs_ins_intr; + else if (irq_type == WCD_MBHC_ELEC_HS_REM) + irq = mbhc->intr_ids->mbhc_hs_rem_intr; + else { + pr_debug("%s: irq_type: %d, enable: %d\n", + __func__, irq_type, enable); + return; + } + + pr_debug("%s: irq: %d, enable: %d, intr_status:%lu\n", + __func__, irq, enable, mbhc->intr_status); + if ((test_bit(irq_type, &mbhc->intr_status)) != enable) { + mbhc->mbhc_cb->irq_control(mbhc->codec, irq, enable); + if (enable) + set_bit(irq_type, &mbhc->intr_status); + else + clear_bit(irq_type, &mbhc->intr_status); + } +} +EXPORT_SYMBOL(wcd_mbhc_hs_elec_irq); + +static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, + enum snd_jack_types jack_type) +{ + struct snd_soc_codec *codec = mbhc->codec; + bool is_pa_on = false; + u8 fsm_en = 0; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + pr_debug("%s: enter insertion %d hph_status %x\n", + __func__, insertion, mbhc->hph_status); + if (!insertion) { + /* Report removal */ + mbhc->hph_status &= ~jack_type; + /* + * cancel possibly scheduled btn work and + * report release if we reported button press + */ + if (wcd_cancel_btn_work(mbhc)) { + pr_debug("%s: button press is canceled\n", __func__); + } else if (mbhc->buttons_pressed) { + pr_debug("%s: release of button press%d\n", + __func__, jack_type); + wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, 0, + mbhc->buttons_pressed); + mbhc->buttons_pressed &= + ~WCD_MBHC_JACK_BUTTON_MASK; + } + + if (mbhc->micbias_enable) { + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control( + codec, MIC_BIAS_2, + MICB_DISABLE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + codec, + MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) { + mbhc->mbhc_cb->set_micbias_value(codec); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0); + } + mbhc->micbias_enable = false; + } + + mbhc->hph_type = WCD_MBHC_HPH_NONE; + mbhc->zl = mbhc->zr = 0; + pr_debug("%s: Reporting removal %d(%x)\n", __func__, + jack_type, mbhc->hph_status); + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, WCD_MBHC_JACK_MASK); + wcd_mbhc_set_and_turnoff_hph_padac(mbhc); + hphrocp_off_report(mbhc, SND_JACK_OC_HPHR); + hphlocp_off_report(mbhc, SND_JACK_OC_HPHL); + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + } else { + /* + * Report removal of current jack type. + * Headphone to headset shouldn't report headphone + * removal. + */ + if (mbhc->mbhc_cfg->detect_extn_cable && + (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH || + jack_type == SND_JACK_LINEOUT) && + (mbhc->hph_status && mbhc->hph_status != jack_type)) { + + if (mbhc->micbias_enable) { + if (mbhc->mbhc_cb->mbhc_micbias_control) + mbhc->mbhc_cb->mbhc_micbias_control( + codec, MIC_BIAS_2, + MICB_DISABLE); + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) + mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( + codec, + MIC_BIAS_2, false); + if (mbhc->mbhc_cb->set_micbias_value) { + mbhc->mbhc_cb->set_micbias_value( + codec); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_MICB_CTRL, 0); + } + mbhc->micbias_enable = false; + } + mbhc->hph_type = WCD_MBHC_HPH_NONE; + mbhc->zl = mbhc->zr = 0; + pr_debug("%s: Reporting removal (%x)\n", + __func__, mbhc->hph_status); + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + 0, WCD_MBHC_JACK_MASK); + + if (mbhc->hph_status == SND_JACK_LINEOUT) { + + pr_debug("%s: Enable micbias\n", __func__); + /* Disable current source and enable micbias */ + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB); + pr_debug("%s: set up elec removal detection\n", + __func__); + usleep_range(200, 210); + wcd_mbhc_hs_elec_irq(mbhc, + WCD_MBHC_ELEC_HS_REM, + true); + } + mbhc->hph_status &= ~(SND_JACK_HEADSET | + SND_JACK_LINEOUT | + SND_JACK_ANC_HEADPHONE | + SND_JACK_UNSUPPORTED); + } + + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET && + jack_type == SND_JACK_HEADPHONE) + mbhc->hph_status &= ~SND_JACK_HEADSET; + + /* Report insertion */ + if (jack_type == SND_JACK_HEADPHONE) + mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE; + else if (jack_type == SND_JACK_UNSUPPORTED) + mbhc->current_plug = MBHC_PLUG_TYPE_GND_MIC_SWAP; + else if (jack_type == SND_JACK_HEADSET) { + mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET; + mbhc->jiffies_atreport = jiffies; + } else if (jack_type == SND_JACK_LINEOUT) { + mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; + } else if (jack_type == SND_JACK_ANC_HEADPHONE) + mbhc->current_plug = MBHC_PLUG_TYPE_ANC_HEADPHONE; + + if (mbhc->mbhc_cb->hph_pa_on_status) + is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(codec); + + if (mbhc->impedance_detect && + mbhc->mbhc_cb->compute_impedance && + (mbhc->mbhc_cfg->linein_th != 0) && + (!is_pa_on)) { + /* Set MUX_CTL to AUTO for Z-det */ + WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, + MUX_CTL_AUTO); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1); + mbhc->mbhc_cb->compute_impedance(mbhc, + &mbhc->zl, &mbhc->zr); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, + fsm_en); + if ((mbhc->zl > mbhc->mbhc_cfg->linein_th && + mbhc->zl < MAX_IMPED) && + (mbhc->zr > mbhc->mbhc_cfg->linein_th && + mbhc->zr < MAX_IMPED) && + (jack_type == SND_JACK_HEADPHONE)) { + jack_type = SND_JACK_LINEOUT; + mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; + if (mbhc->hph_status) { + mbhc->hph_status &= ~(SND_JACK_HEADSET | + SND_JACK_LINEOUT | + SND_JACK_UNSUPPORTED); + wcd_mbhc_jack_report(mbhc, + &mbhc->headset_jack, + mbhc->hph_status, + WCD_MBHC_JACK_MASK); + } + pr_debug("%s: Marking jack type as SND_JACK_LINEOUT\n", + __func__); + } + } + + mbhc->hph_status |= jack_type; + + pr_debug("%s: Reporting insertion %d(%x)\n", __func__, + jack_type, mbhc->hph_status); + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + (mbhc->hph_status | SND_JACK_MECHANICAL), + WCD_MBHC_JACK_MASK); + wcd_mbhc_clr_and_turnon_hph_padac(mbhc); + } + pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status); +} + +void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc) +{ + /* cancel pending button press */ + if (wcd_cancel_btn_work(mbhc)) + pr_debug("%s: button press is canceled\n", __func__); + /* cancel correct work function */ + if (mbhc->mbhc_fn->wcd_cancel_hs_detect_plug) + mbhc->mbhc_fn->wcd_cancel_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + else + pr_info("%s: hs_detect_plug work not cancelled\n", __func__); + + pr_debug("%s: Report extension cable\n", __func__); + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); + /* + * If PA is enabled HPHL schmitt trigger can + * be unreliable, make sure to disable it + */ + if (test_bit(WCD_MBHC_EVENT_PA_HPHL, + &mbhc->event_state)) + wcd_mbhc_set_and_turnoff_hph_padac(mbhc); + /* + * Disable HPHL trigger and MIC Schmitt triggers. + * Setup for insertion detection. + */ + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, + false); + wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE); + /* Disable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 3); + + /* Set the detection type appropriately */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, 1); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + true); +} +EXPORT_SYMBOL(wcd_mbhc_elec_hs_report_unplug); + +void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type) +{ + bool anc_mic_found = false; + enum snd_jack_types jack_type; + + pr_debug("%s: enter current_plug(%d) new_plug(%d)\n", + __func__, mbhc->current_plug, plug_type); + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (mbhc->current_plug == plug_type) { + pr_debug("%s: cable already reported, exit\n", __func__); + goto exit; + } + + if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) { + /* + * Nothing was reported previously + * report a headphone or unsupported + */ + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE); + } else if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) { + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE); + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET); + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED); + } else if (plug_type == MBHC_PLUG_TYPE_HEADSET) { + if (mbhc->mbhc_cfg->enable_anc_mic_detect && + mbhc->mbhc_fn->wcd_mbhc_detect_anc_plug_type) + anc_mic_found = + mbhc->mbhc_fn->wcd_mbhc_detect_anc_plug_type(mbhc); + jack_type = SND_JACK_HEADSET; + if (anc_mic_found) + jack_type = SND_JACK_ANC_HEADPHONE; + + /* + * If Headphone was reported previously, this will + * only report the mic line + */ + wcd_mbhc_report_plug(mbhc, 1, jack_type); + } else if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) { + if (mbhc->mbhc_cfg->detect_extn_cable) { + /* High impedance device found. Report as LINEOUT */ + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); + pr_debug("%s: setup mic trigger for further detection\n", + __func__); + + /* Disable HW FSM and current source */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + /* Setup for insertion detection */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, + 1); + /* + * Enable HPHL trigger and MIC Schmitt triggers + * and request for elec insertion interrupts + */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, + 3); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, + true); + } else { + wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT); + } + } else { + WARN(1, "Unexpected current plug_type %d, plug_type %d\n", + mbhc->current_plug, plug_type); + } +exit: + pr_debug("%s: leave\n", __func__); +} +EXPORT_SYMBOL(wcd_mbhc_find_plug_and_report); + +static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) +{ + bool detection_type = 0; + bool micbias1 = false; + struct snd_soc_codec *codec = mbhc->codec; + enum snd_jack_types jack_type; + + dev_dbg(codec->dev, "%s: enter\n", __func__); + WCD_MBHC_RSC_LOCK(mbhc); + mbhc->in_swch_irq_handler = true; + + /* cancel pending button press */ + if (wcd_cancel_btn_work(mbhc)) + pr_debug("%s: button press is canceled\n", __func__); + + WCD_MBHC_REG_READ(WCD_MBHC_MECH_DETECTION_TYPE, detection_type); + + /* Set the detection type appropriately */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE, + !detection_type); + + pr_debug("%s: mbhc->current_plug: %d detection_type: %d\n", __func__, + mbhc->current_plug, detection_type); + if (mbhc->mbhc_fn->wcd_cancel_hs_detect_plug) + mbhc->mbhc_fn->wcd_cancel_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + else + pr_info("%s: hs_detect_plug work not cancelled\n", __func__); + + if (mbhc->mbhc_cb->micbias_enable_status) + micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc, + MIC_BIAS_1); + + if ((mbhc->current_plug == MBHC_PLUG_TYPE_NONE) && + detection_type) { + /* Make sure MASTER_BIAS_CTL is enabled */ + mbhc->mbhc_cb->mbhc_bias(codec, true); + + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_TAIL_CURR, true); + + if (!mbhc->mbhc_cfg->hs_ext_micbias && + mbhc->mbhc_cb->micb_internal) + /* + * Enable Tx2 RBias if the headset + * is using internal micbias + */ + mbhc->mbhc_cb->micb_internal(codec, 1, true); + + /* Remove micbias pulldown */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 0); + /* Apply trim if needed on the device */ + if (mbhc->mbhc_cb->trim_btn_reg) + mbhc->mbhc_cb->trim_btn_reg(codec); + /* Enable external voltage source to micbias if present */ + if (mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(mbhc, true); + mbhc->btn_press_intr = false; + mbhc->is_btn_press = false; + if (mbhc->mbhc_fn) + mbhc->mbhc_fn->wcd_mbhc_detect_plug_type(mbhc); + } else if ((mbhc->current_plug != MBHC_PLUG_TYPE_NONE) + && !detection_type) { + /* Disable external voltage source to micbias if present */ + if (mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(mbhc, false); + /* Disable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) + mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec, + MBHC_COMMON_MICB_TAIL_CURR, false); + + if (mbhc->mbhc_cb->set_cap_mode) + mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false); + + mbhc->btn_press_intr = false; + mbhc->is_btn_press = false; + switch (mbhc->current_plug) { + case MBHC_PLUG_TYPE_HEADPHONE: + jack_type = SND_JACK_HEADPHONE; + break; + case MBHC_PLUG_TYPE_GND_MIC_SWAP: + jack_type = SND_JACK_UNSUPPORTED; + break; + case MBHC_PLUG_TYPE_HEADSET: + /* make sure to turn off Rbias */ + if (mbhc->mbhc_cb->micb_internal) + mbhc->mbhc_cb->micb_internal(codec, 1, false); + /* Pulldown micbias */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 1); + jack_type = SND_JACK_HEADSET; + break; + case MBHC_PLUG_TYPE_HIGH_HPH: + mbhc->is_extn_cable = false; + jack_type = SND_JACK_LINEOUT; + break; + case MBHC_PLUG_TYPE_ANC_HEADPHONE: + jack_type = SND_JACK_ANC_HEADPHONE; + break; + default: + pr_info("%s: Invalid current plug: %d\n", + __func__, mbhc->current_plug); + jack_type = SND_JACK_UNSUPPORTED; + break; + } + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, false); + wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0); + mbhc->extn_cable_hph_rem = false; + wcd_mbhc_report_plug(mbhc, 0, jack_type); + + } else if (!detection_type) { + /* Disable external voltage source to micbias if present */ + if (mbhc->mbhc_cb->enable_mb_source) + mbhc->mbhc_cb->enable_mb_source(mbhc, false); + /* Disable HW FSM */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + mbhc->extn_cable_hph_rem = false; + } + + mbhc->in_swch_irq_handler = false; + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); +} + +static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data) +{ + int r = IRQ_HANDLED; + struct wcd_mbhc *mbhc = data; + + pr_debug("%s: enter\n", __func__); + if (unlikely((mbhc->mbhc_cb->lock_sleep(mbhc, true)) == false)) { + pr_warn("%s: failed to hold suspend\n", __func__); + r = IRQ_NONE; + } else { + /* Call handler */ + wcd_mbhc_swch_irq_handler(mbhc); + mbhc->mbhc_cb->lock_sleep(mbhc, false); + } + pr_debug("%s: leave %d\n", __func__, r); + return r; +} + +int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc) +{ + int mask = 0; + int btn; + + btn = mbhc->mbhc_cb->map_btn_code_to_num(mbhc->codec); + + switch (btn) { + case 0: + mask = SND_JACK_BTN_0; + break; + case 1: + mask = SND_JACK_BTN_1; + break; + case 2: + mask = SND_JACK_BTN_2; + break; + case 3: + mask = SND_JACK_BTN_3; + break; + case 4: + mask = SND_JACK_BTN_4; + break; + case 5: + mask = SND_JACK_BTN_5; + break; + default: + break; + } + + return mask; +} +EXPORT_SYMBOL(wcd_mbhc_get_button_mask); + +static void wcd_btn_lpress_fn(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd_mbhc *mbhc; + s16 btn_result = 0; + + pr_debug("%s: Enter\n", __func__); + + dwork = to_delayed_work(work); + mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork); + + WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result); + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) { + pr_debug("%s: Reporting long button press event, btn_result: %d\n", + __func__, btn_result); + wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, + mbhc->buttons_pressed, mbhc->buttons_pressed); + } + pr_debug("%s: leave\n", __func__); + mbhc->mbhc_cb->lock_sleep(mbhc, false); +} + +static bool wcd_mbhc_fw_validate(const void *data, size_t size) +{ + u32 cfg_offset; + struct wcd_mbhc_btn_detect_cfg *btn_cfg; + struct firmware_cal fw; + + fw.data = (void *)data; + fw.size = size; + + if (fw.size < WCD_MBHC_CAL_MIN_SIZE) + return false; + + /* + * Previous check guarantees that there is enough fw data up + * to num_btn + */ + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(fw.data); + cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data); + if (fw.size < (cfg_offset + WCD_MBHC_CAL_BTN_SZ(btn_cfg))) + return false; + + return true; +} + +static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + int mask; + unsigned long msec_val; + + pr_debug("%s: enter\n", __func__); + complete(&mbhc->btn_press_compl); + WCD_MBHC_RSC_LOCK(mbhc); + wcd_cancel_btn_work(mbhc); + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low ", __func__); + goto done; + } + + mbhc->is_btn_press = true; + msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport); + pr_debug("%s: msec_val = %ld\n", __func__, msec_val); + if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN) { + pr_debug("%s: Too short, ignore button press\n", __func__); + goto done; + } + + /* If switch interrupt already kicked in, ignore button press */ + if (mbhc->in_swch_irq_handler) { + pr_debug("%s: Swtich level changed, ignore button press\n", + __func__); + goto done; + } + mask = wcd_mbhc_get_button_mask(mbhc); + if (mask == SND_JACK_BTN_0) + mbhc->btn_press_intr = true; + + if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET) { + pr_debug("%s: Plug isn't headset, ignore button press\n", + __func__); + goto done; + } + mbhc->buttons_pressed |= mask; + mbhc->mbhc_cb->lock_sleep(mbhc, true); + if (schedule_delayed_work(&mbhc->mbhc_btn_dwork, + msecs_to_jiffies(400)) == 0) { + WARN(1, "Button pressed twice without release event\n"); + mbhc->mbhc_cb->lock_sleep(mbhc, false); + } +done: + pr_debug("%s: leave\n", __func__); + WCD_MBHC_RSC_UNLOCK(mbhc); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + int ret; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_LOCK(mbhc); + if (wcd_swch_level_remove(mbhc)) { + pr_debug("%s: Switch level is low ", __func__); + goto exit; + } + + if (mbhc->is_btn_press) { + mbhc->is_btn_press = false; + } else { + pr_debug("%s: This release is for fake btn press\n", __func__); + goto exit; + } + + /* + * If current plug is headphone then there is no chance to + * get btn release interrupt, so connected cable should be + * headset not headphone. + * For ADC MBHC, ADC_COMPLETE interrupt will be generated + * in this case. So skip the check here. + */ + if (!WCD_MBHC_DETECTION && + mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { + wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); + goto exit; + + } + if (mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK) { + ret = wcd_cancel_btn_work(mbhc); + if (ret == 0) { + pr_debug("%s: Reporting long button release event\n", + __func__); + wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, + 0, mbhc->buttons_pressed); + } else { + if (mbhc->in_swch_irq_handler) { + pr_debug("%s: Switch irq kicked in, ignore\n", + __func__); + } else { + pr_debug("%s: Reporting btn press\n", + __func__); + wcd_mbhc_jack_report(mbhc, + &mbhc->button_jack, + mbhc->buttons_pressed, + mbhc->buttons_pressed); + pr_debug("%s: Reporting btn release\n", + __func__); + wcd_mbhc_jack_report(mbhc, + &mbhc->button_jack, + 0, mbhc->buttons_pressed); + } + } + mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK; + } +exit: + pr_debug("%s: leave\n", __func__); + WCD_MBHC_RSC_UNLOCK(mbhc); + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + int val; + + pr_debug("%s: received HPHL OCP irq\n", __func__); + if (mbhc) { + if (mbhc->mbhc_cb->hph_register_recovery) { + if (mbhc->mbhc_cb->hph_register_recovery(mbhc)) { + WCD_MBHC_REG_READ(WCD_MBHC_HPHR_OCP_STATUS, + val); + if ((val != -EINVAL) && val) + mbhc->is_hph_ocp_pending = true; + goto done; + } + } + + if (mbhc->hphlocp_cnt < OCP_ATTEMPT) { + mbhc->hphlocp_cnt++; + pr_debug("%s: retry, hphlocp_cnt: %d\n", __func__, + mbhc->hphlocp_cnt); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1); + } else { + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + false); + mbhc->hph_status |= SND_JACK_OC_HPHL; + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, + WCD_MBHC_JACK_MASK); + } + } else { + pr_err("%s: Bad wcd9xxx_spmi private data\n", __func__); + } +done: + return IRQ_HANDLED; +} + +static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data) +{ + struct wcd_mbhc *mbhc = data; + + pr_debug("%s: received HPHR OCP irq\n", __func__); + + if (!mbhc) { + pr_err("%s: Bad mbhc private data\n", __func__); + goto done; + } + + if (mbhc->is_hph_ocp_pending) { + mbhc->is_hph_ocp_pending = false; + goto done; + } + + if (mbhc->mbhc_cb->hph_register_recovery) { + if (mbhc->mbhc_cb->hph_register_recovery(mbhc)) + /* register corruption, hence reset registers */ + goto done; + } + if (mbhc->hphrocp_cnt < OCP_ATTEMPT) { + mbhc->hphrocp_cnt++; + pr_debug("%s: retry, hphrocp_cnt: %d\n", __func__, + mbhc->hphrocp_cnt); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1); + } else { + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_right_ocp, + false); + mbhc->hph_status |= SND_JACK_OC_HPHR; + wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack, + mbhc->hph_status, WCD_MBHC_JACK_MASK); + } +done: + return IRQ_HANDLED; +} + +static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc) +{ + int ret = 0; + struct snd_soc_codec *codec = mbhc->codec; + + pr_debug("%s: enter\n", __func__); + WCD_MBHC_RSC_LOCK(mbhc); + + /* enable HS detection */ + if (mbhc->mbhc_cb->hph_pull_up_control) + mbhc->mbhc_cb->hph_pull_up_control(codec, I_DEFAULT); + else + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3); + + if (mbhc->mbhc_cfg->moisture_en && mbhc->mbhc_cb->mbhc_moisture_config) + mbhc->mbhc_cb->mbhc_moisture_config(mbhc); + + /* + * For USB analog we need to override the switch configuration. + * Also, disable hph_l pull-up current source as HS_DET_L is driven + * by an external source + */ + if (mbhc->mbhc_cfg->enable_usbc_analog) { + mbhc->hphl_swh = 1; + mbhc->gnd_swh = 1; + + if (mbhc->mbhc_cb->hph_pull_up_control) + mbhc->mbhc_cb->hph_pull_up_control(codec, I_OFF); + else + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, + 0); + } + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PLUG_TYPE, mbhc->hphl_swh); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_GND_PLUG_TYPE, mbhc->gnd_swh); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1); + if (mbhc->mbhc_cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl) + mbhc->mbhc_cb->mbhc_gnd_det_ctrl(codec, true); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); + + if (mbhc->mbhc_cfg->enable_usbc_analog) { + /* Insertion debounce set to 48ms */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_INSREM_DBNC, 4); + } else { + /* Insertion debounce set to 96ms */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_INSREM_DBNC, 6); + } + + /* Button Debounce set to 16ms */ + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_DBNC, 2); + + /* Enable micbias ramp */ + if (mbhc->mbhc_cb->mbhc_micb_ramp_control) + mbhc->mbhc_cb->mbhc_micb_ramp_control(codec, true); + /* enable bias */ + mbhc->mbhc_cb->mbhc_bias(codec, true); + /* enable MBHC clock */ + if (mbhc->mbhc_cb->clk_setup) + mbhc->mbhc_cb->clk_setup(codec, true); + + /* program HS_VREF value */ + wcd_program_hs_vref(mbhc); + + wcd_program_btn_threshold(mbhc, false); + + + reinit_completion(&mbhc->btn_press_compl); + + WCD_MBHC_RSC_UNLOCK(mbhc); + pr_debug("%s: leave\n", __func__); + return ret; +} + +static void wcd_mbhc_fw_read(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd_mbhc *mbhc; + struct snd_soc_codec *codec; + const struct firmware *fw; + struct firmware_cal *fw_data = NULL; + int ret = -1, retry = 0; + bool use_default_cal = false; + + dwork = to_delayed_work(work); + mbhc = container_of(dwork, struct wcd_mbhc, mbhc_firmware_dwork); + codec = mbhc->codec; + + while (retry < FW_READ_ATTEMPTS) { + retry++; + pr_debug("%s:Attempt %d to request MBHC firmware\n", + __func__, retry); + if (mbhc->mbhc_cb->get_hwdep_fw_cal) + fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(mbhc, + WCD9XXX_MBHC_CAL); + if (!fw_data) + ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin", + codec->dev); + /* + * if request_firmware and hwdep cal both fail then + * sleep for 4sec for the userspace to send data to kernel + * retry for few times before bailing out + */ + if ((ret != 0) && !fw_data) { + usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT + + WCD_MBHC_USLEEP_RANGE_MARGIN_US); + } else { + pr_debug("%s: MBHC Firmware read successful\n", + __func__); + break; + } + } + if (!fw_data) + pr_debug("%s: using request_firmware\n", __func__); + else + pr_debug("%s: using hwdep cal\n", __func__); + + if (ret != 0 && !fw_data) { + pr_err("%s: Cannot load MBHC firmware use default cal\n", + __func__); + use_default_cal = true; + } + if (!use_default_cal) { + const void *data; + size_t size; + + if (fw_data) { + data = fw_data->data; + size = fw_data->size; + } else { + data = fw->data; + size = fw->size; + } + if (wcd_mbhc_fw_validate(data, size) == false) { + pr_err("%s: Invalid MBHC cal data size use default cal\n", + __func__); + if (!fw_data) + release_firmware(fw); + } else { + if (fw_data) { + mbhc->mbhc_cfg->calibration = + (void *)fw_data->data; + mbhc->mbhc_cal = fw_data; + } else { + mbhc->mbhc_cfg->calibration = + (void *)fw->data; + mbhc->mbhc_fw = fw; + } + } + + } + + (void) wcd_mbhc_initialise(mbhc); +} + +static int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc) +{ + enum snd_jack_types type; + int i, ret, result = 0; + int *btn_key_code; + + btn_key_code = mbhc->mbhc_cfg->key_code; + + for (i = 0 ; i < WCD_MBHC_KEYCODE_NUM ; i++) { + if (btn_key_code[i] != 0) { + switch (i) { + case 0: + type = SND_JACK_BTN_0; + break; + case 1: + type = SND_JACK_BTN_1; + break; + case 2: + type = SND_JACK_BTN_2; + break; + case 3: + type = SND_JACK_BTN_3; + break; + case 4: + type = SND_JACK_BTN_4; + break; + case 5: + type = SND_JACK_BTN_5; + break; + default: + WARN_ONCE(1, "Wrong button number:%d\n", i); + result = -1; + return result; + } + ret = snd_jack_set_key(mbhc->button_jack.jack, + type, + btn_key_code[i]); + if (ret) { + pr_err("%s: Failed to set code for %d\n", + __func__, btn_key_code[i]); + result = -1; + return result; + } + input_set_capability( + mbhc->button_jack.jack->input_dev, + EV_KEY, btn_key_code[i]); + pr_debug("%s: set btn%d key code:%d\n", __func__, + i, btn_key_code[i]); + } + } + if (btn_key_code[0]) + mbhc->is_btn_already_regd = true; + return result; +} + +static int wcd_mbhc_usb_c_analog_setup_gpios(struct wcd_mbhc *mbhc, + bool active) +{ + int rc = 0; + struct usbc_ana_audio_config *config = + &mbhc->mbhc_cfg->usbc_analog_cfg; + union power_supply_propval pval; + + dev_dbg(mbhc->codec->dev, "%s: setting GPIOs active = %d\n", + __func__, active); + + memset(&pval, 0, sizeof(pval)); + + if (active) { + pval.intval = POWER_SUPPLY_TYPEC_PR_SOURCE; + if (power_supply_set_property(mbhc->usb_psy, + POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval)) + dev_info(mbhc->codec->dev, "%s: force PR_SOURCE mode unsuccessful\n", + __func__); + else + mbhc->usbc_force_pr_mode = true; + + if (config->usbc_en1_gpio_p) + rc = msm_cdc_pinctrl_select_active_state( + config->usbc_en1_gpio_p); + if (rc == 0 && config->usbc_force_gpio_p) + rc = msm_cdc_pinctrl_select_active_state( + config->usbc_force_gpio_p); + mbhc->usbc_mode = POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER; + } else { + /* no delay is required when disabling GPIOs */ + if (config->usbc_en1_gpio_p) + msm_cdc_pinctrl_select_sleep_state( + config->usbc_en1_gpio_p); + if (config->usbc_force_gpio_p) + msm_cdc_pinctrl_select_sleep_state( + config->usbc_force_gpio_p); + + if (mbhc->usbc_force_pr_mode) { + pval.intval = POWER_SUPPLY_TYPEC_PR_DUAL; + if (power_supply_set_property(mbhc->usb_psy, + POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &pval)) + dev_info(mbhc->codec->dev, "%s: force PR_DUAL mode unsuccessful\n", + __func__); + + mbhc->usbc_force_pr_mode = false; + } + + mbhc->usbc_mode = POWER_SUPPLY_TYPEC_NONE; + if (mbhc->mbhc_cfg->swap_gnd_mic) + mbhc->mbhc_cfg->swap_gnd_mic(mbhc->codec, false); + } + + return rc; +} + +/* workqueue */ +static void wcd_mbhc_usbc_analog_work_fn(struct work_struct *work) +{ + struct wcd_mbhc *mbhc = + container_of(work, struct wcd_mbhc, usbc_analog_work); + + wcd_mbhc_usb_c_analog_setup_gpios(mbhc, + mbhc->usbc_mode != POWER_SUPPLY_TYPEC_NONE); +} + +/* this callback function is used to process PMI notification */ +static int wcd_mbhc_usb_c_event_changed(struct notifier_block *nb, + unsigned long evt, void *ptr) +{ + int ret; + union power_supply_propval mode; + struct wcd_mbhc *mbhc = container_of(nb, struct wcd_mbhc, psy_nb); + struct snd_soc_codec *codec = mbhc->codec; + + if (ptr != mbhc->usb_psy || evt != PSY_EVENT_PROP_CHANGED) + return 0; + + ret = power_supply_get_property(mbhc->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, &mode); + if (ret) { + dev_err(codec->dev, "%s: Unable to read USB TYPEC_MODE: %d\n", + __func__, ret); + return ret; + } + + dev_dbg(codec->dev, "%s: USB change event received\n", + __func__); + dev_dbg(codec->dev, "%s: supply mode %d, expected %d\n", __func__, + mode.intval, POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER); + + switch (mode.intval) { + case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER: + case POWER_SUPPLY_TYPEC_NONE: + dev_dbg(codec->dev, "%s: usbc_mode: %d; mode.intval: %d\n", + __func__, mbhc->usbc_mode, mode.intval); + + if (mbhc->usbc_mode == mode.intval) + break; /* filter notifications received before */ + mbhc->usbc_mode = mode.intval; + + dev_dbg(codec->dev, "%s: queueing usbc_analog_work\n", + __func__); + schedule_work(&mbhc->usbc_analog_work); + break; + default: + break; + } + return ret; +} + +/* PMI registration code */ +static int wcd_mbhc_usb_c_analog_init(struct wcd_mbhc *mbhc) +{ + int ret = 0; + struct snd_soc_codec *codec = mbhc->codec; + + dev_dbg(mbhc->codec->dev, "%s: usb-c analog setup start\n", __func__); + INIT_WORK(&mbhc->usbc_analog_work, wcd_mbhc_usbc_analog_work_fn); + + mbhc->usb_psy = power_supply_get_by_name("usb"); + if (IS_ERR_OR_NULL(mbhc->usb_psy)) { + dev_err(codec->dev, "%s: could not get USB psy info\n", + __func__); + ret = -EPROBE_DEFER; + if (IS_ERR(mbhc->usb_psy)) + ret = PTR_ERR(mbhc->usb_psy); + mbhc->usb_psy = NULL; + goto err; + } + + ret = wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false); + if (ret) { + dev_err(codec->dev, "%s: error while setting USBC ana gpios\n", + __func__); + goto err; + } + + mbhc->psy_nb.notifier_call = wcd_mbhc_usb_c_event_changed; + mbhc->psy_nb.priority = 0; + ret = power_supply_reg_notifier(&mbhc->psy_nb); + if (ret) { + dev_err(codec->dev, "%s: power supply registration failed\n", + __func__); + goto err; + } + + /* + * as part of the init sequence check if there is a connected + * USB C analog adapter + */ + dev_dbg(mbhc->codec->dev, "%s: verify if USB adapter is already inserted\n", + __func__); + ret = wcd_mbhc_usb_c_event_changed(&mbhc->psy_nb, + PSY_EVENT_PROP_CHANGED, + mbhc->usb_psy); + +err: + return ret; +} + +static int wcd_mbhc_usb_c_analog_deinit(struct wcd_mbhc *mbhc) +{ + wcd_mbhc_usb_c_analog_setup_gpios(mbhc, false); + + /* deregister from PMI */ + power_supply_unreg_notifier(&mbhc->psy_nb); + + return 0; +} + +static int wcd_mbhc_init_gpio(struct wcd_mbhc *mbhc, + struct wcd_mbhc_config *mbhc_cfg, + const char *gpio_dt_str, + int *gpio, struct device_node **gpio_dn) +{ + int rc = 0; + struct snd_soc_codec *codec = mbhc->codec; + struct snd_soc_card *card = codec->component.card; + + dev_dbg(mbhc->codec->dev, "%s: gpio %s\n", __func__, gpio_dt_str); + + *gpio_dn = of_parse_phandle(card->dev->of_node, gpio_dt_str, 0); + + if (!(*gpio_dn)) { + *gpio = of_get_named_gpio(card->dev->of_node, gpio_dt_str, 0); + if (!gpio_is_valid(*gpio)) { + dev_err(card->dev, "%s, property %s not in node %s", + __func__, gpio_dt_str, + card->dev->of_node->full_name); + rc = -EINVAL; + } + } + + return rc; +} + +int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) +{ + int rc = 0; + struct usbc_ana_audio_config *config; + struct snd_soc_codec *codec; + struct snd_soc_card *card; + const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported"; + + if (!mbhc || !mbhc_cfg) + return -EINVAL; + + config = &mbhc_cfg->usbc_analog_cfg; + codec = mbhc->codec; + card = codec->component.card; + + /* update the mbhc config */ + mbhc->mbhc_cfg = mbhc_cfg; + + dev_dbg(mbhc->codec->dev, "%s: enter\n", __func__); + + /* check if USB C analog is defined on device tree */ + mbhc_cfg->enable_usbc_analog = 0; + if (of_find_property(card->dev->of_node, usb_c_dt, NULL)) { + rc = of_property_read_u32(card->dev->of_node, usb_c_dt, + &mbhc_cfg->enable_usbc_analog); + } + if (mbhc_cfg->enable_usbc_analog == 0 || rc != 0) { + dev_info(card->dev, + "%s: %s in dt node is missing or false\n", + __func__, usb_c_dt); + dev_info(card->dev, + "%s: skipping USB c analog configuration\n", __func__); + } + + /* initialize GPIOs */ + if (mbhc_cfg->enable_usbc_analog) { + dev_dbg(mbhc->codec->dev, "%s: usbc analog enabled\n", + __func__); + rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg, + "qcom,usbc-analog-en1-gpio", + &config->usbc_en1_gpio, + &config->usbc_en1_gpio_p); + if (rc) + goto err; + + if (of_find_property(card->dev->of_node, + "qcom,usbc-analog-force_detect_gpio", + NULL)) { + rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg, + "qcom,usbc-analog-force_detect_gpio", + &config->usbc_force_gpio, + &config->usbc_force_gpio_p); + if (rc) + goto err; + } + + dev_dbg(mbhc->codec->dev, "%s: calling usb_c_analog_init\n", + __func__); + /* init PMI notifier */ + rc = wcd_mbhc_usb_c_analog_init(mbhc); + if (rc) { + rc = EPROBE_DEFER; + goto err; + } + } + + /* Set btn key code */ + if ((!mbhc->is_btn_already_regd) && wcd_mbhc_set_keycode(mbhc)) + pr_err("Set btn key code error!!!\n"); + + if (!mbhc->mbhc_cfg->read_fw_bin || + (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) || + (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) { + rc = wcd_mbhc_initialise(mbhc); + } else { + if (!mbhc->mbhc_fw || !mbhc->mbhc_cal) + schedule_delayed_work(&mbhc->mbhc_firmware_dwork, + usecs_to_jiffies(FW_READ_TIMEOUT)); + else + pr_err("%s: Skipping to read mbhc fw, 0x%pK %pK\n", + __func__, mbhc->mbhc_fw, mbhc->mbhc_cal); + } + + return rc; +err: + if (config->usbc_en1_gpio > 0) { + dev_dbg(card->dev, "%s free usb en1 gpio %d\n", + __func__, config->usbc_en1_gpio); + gpio_free(config->usbc_en1_gpio); + config->usbc_en1_gpio = 0; + } + if (config->usbc_force_gpio > 0) { + dev_dbg(card->dev, "%s free usb_force gpio %d\n", + __func__, config->usbc_force_gpio); + gpio_free(config->usbc_force_gpio); + config->usbc_force_gpio = 0; + } + if (config->usbc_en1_gpio_p) + of_node_put(config->usbc_en1_gpio_p); + if (config->usbc_force_gpio_p) + of_node_put(config->usbc_force_gpio_p); + dev_dbg(mbhc->codec->dev, "%s: leave %d\n", __func__, rc); + return rc; +} +EXPORT_SYMBOL(wcd_mbhc_start); + +void wcd_mbhc_stop(struct wcd_mbhc *mbhc) +{ + struct usbc_ana_audio_config *config = &mbhc->mbhc_cfg->usbc_analog_cfg; + + pr_debug("%s: enter\n", __func__); + + if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE) { + if (mbhc->mbhc_cb && mbhc->mbhc_cb->skip_imped_detect) + mbhc->mbhc_cb->skip_imped_detect(mbhc->codec); + } + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + mbhc->hph_status = 0; + if (mbhc->mbhc_cb && mbhc->mbhc_cb->irq_control) { + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_left_ocp, + false); + mbhc->mbhc_cb->irq_control(mbhc->codec, + mbhc->intr_ids->hph_right_ocp, + false); + } + if (mbhc->mbhc_fw || mbhc->mbhc_cal) { + cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork); + if (!mbhc->mbhc_cal) + release_firmware(mbhc->mbhc_fw); + mbhc->mbhc_fw = NULL; + mbhc->mbhc_cal = NULL; + } + + if (mbhc->mbhc_cfg->enable_usbc_analog) { + wcd_mbhc_usb_c_analog_deinit(mbhc); + /* free GPIOs */ + if (config->usbc_en1_gpio > 0) + gpio_free(config->usbc_en1_gpio); + if (config->usbc_force_gpio) + gpio_free(config->usbc_force_gpio); + + if (config->usbc_en1_gpio_p) + of_node_put(config->usbc_en1_gpio_p); + if (config->usbc_force_gpio_p) + of_node_put(config->usbc_force_gpio_p); + } + + pr_debug("%s: leave\n", __func__); +} +EXPORT_SYMBOL(wcd_mbhc_stop); + +/* + * wcd_mbhc_init : initialize MBHC internal structures. + * + * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used + */ +int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, + const struct wcd_mbhc_cb *mbhc_cb, + const struct wcd_mbhc_intr *mbhc_cdc_intr_ids, + struct wcd_mbhc_register *wcd_mbhc_regs, + bool impedance_det_en) +{ + int ret = 0; + int hph_swh = 0; + int gnd_swh = 0; + u32 hph_moist_config[3]; + struct snd_soc_card *card = codec->component.card; + const char *hph_switch = "qcom,msm-mbhc-hphl-swh"; + const char *gnd_switch = "qcom,msm-mbhc-gnd-swh"; + + pr_debug("%s: enter\n", __func__); + + ret = of_property_read_u32(card->dev->of_node, hph_switch, &hph_swh); + if (ret) { + dev_err(card->dev, + "%s: missing %s in dt node\n", __func__, hph_switch); + goto err; + } + + ret = of_property_read_u32(card->dev->of_node, gnd_switch, &gnd_swh); + if (ret) { + dev_err(card->dev, + "%s: missing %s in dt node\n", __func__, gnd_switch); + goto err; + } + + ret = of_property_read_u32_array(card->dev->of_node, + "qcom,msm-mbhc-moist-cfg", + hph_moist_config, 3); + if (ret) { + dev_dbg(card->dev, "%s: no qcom,msm-mbhc-moist-cfg in DT\n", + __func__); + mbhc->moist_vref = V_45_MV; + mbhc->moist_iref = I_3P0_UA; + mbhc->moist_rref = R_24_KOHM; + } else { + mbhc->moist_vref = hph_moist_config[0]; + mbhc->moist_iref = hph_moist_config[1]; + mbhc->moist_rref = hph_moist_config[2]; + } + + mbhc->in_swch_irq_handler = false; + mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + mbhc->is_btn_press = false; + mbhc->codec = codec; + mbhc->intr_ids = mbhc_cdc_intr_ids; + mbhc->impedance_detect = impedance_det_en; + mbhc->hphl_swh = hph_swh; + mbhc->gnd_swh = gnd_swh; + mbhc->micbias_enable = false; + mbhc->mbhc_cb = mbhc_cb; + mbhc->btn_press_intr = false; + mbhc->is_hs_recording = false; + mbhc->is_extn_cable = false; + mbhc->extn_cable_hph_rem = false; + mbhc->hph_type = WCD_MBHC_HPH_NONE; + mbhc->wcd_mbhc_regs = wcd_mbhc_regs; + + if (mbhc->intr_ids == NULL) { + pr_err("%s: Interrupt mapping not provided\n", __func__); + return -EINVAL; + } + if (!mbhc->wcd_mbhc_regs) { + dev_err(codec->dev, "%s: mbhc registers are not defined\n", + __func__); + return -EINVAL; + } + + /* Check if IRQ and other required callbacks are defined or not */ + if (!mbhc_cb || !mbhc_cb->request_irq || !mbhc_cb->irq_control || + !mbhc_cb->free_irq || !mbhc_cb->map_btn_code_to_num || + !mbhc_cb->lock_sleep || !mbhc_cb->mbhc_bias || + !mbhc_cb->set_btn_thr) { + dev_err(codec->dev, "%s: required mbhc callbacks are not defined\n", + __func__); + return -EINVAL; + } + + /* No need to create new sound card jacks if is is already created */ + if (mbhc->headset_jack.jack == NULL) { + ret = snd_soc_card_jack_new(codec->component.card, + "Headset Jack", WCD_MBHC_JACK_MASK, + &mbhc->headset_jack, NULL, 0); + if (ret) { + pr_err("%s: Failed to create new jack\n", __func__); + return ret; + } + + ret = snd_soc_card_jack_new(codec->component.card, + "Button Jack", + WCD_MBHC_JACK_BUTTON_MASK, + &mbhc->button_jack, NULL, 0); + if (ret) { + pr_err("Failed to create new jack\n"); + return ret; + } + + ret = snd_jack_set_key(mbhc->button_jack.jack, + SND_JACK_BTN_0, + KEY_MEDIA); + if (ret) { + pr_err("%s: Failed to set code for btn-0\n", + __func__); + return ret; + } + + INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork, + wcd_mbhc_fw_read); + INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn); + } + mutex_init(&mbhc->hphl_pa_lock); + mutex_init(&mbhc->hphr_pa_lock); + init_completion(&mbhc->btn_press_compl); + + /* Register event notifier */ + mbhc->nblock.notifier_call = wcd_event_notify; + if (mbhc->mbhc_cb->register_notifier) { + ret = mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, + true); + if (ret) { + pr_err("%s: Failed to register notifier %d\n", + __func__, ret); + return ret; + } + } + + init_waitqueue_head(&mbhc->wait_btn_press); + mutex_init(&mbhc->codec_resource_lock); + + switch (WCD_MBHC_DETECTION) { + case WCD_DETECTION_LEGACY: + wcd_mbhc_legacy_init(mbhc); + break; + case WCD_DETECTION_ADC: + wcd_mbhc_adc_init(mbhc); + break; + default: + pr_err("%s: Unknown detection logic type %d\n", + __func__, WCD_MBHC_DETECTION); + break; + } + + if (!mbhc->mbhc_fn || + !mbhc->mbhc_fn->wcd_mbhc_hs_ins_irq || + !mbhc->mbhc_fn->wcd_mbhc_hs_rem_irq || + !mbhc->mbhc_fn->wcd_mbhc_detect_plug_type || + !mbhc->mbhc_fn->wcd_cancel_hs_detect_plug) { + pr_err("%s: mbhc function pointer is NULL\n", __func__); + goto err_mbhc_sw_irq; + } + ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->mbhc_sw_intr, + wcd_mbhc_mech_plug_detect_irq, + "mbhc sw intr", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d, ret = %d\n", __func__, + mbhc->intr_ids->mbhc_sw_intr, ret); + goto err_mbhc_sw_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_btn_press_intr, + wcd_mbhc_btn_press_handler, + "Button Press detect", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_btn_press_intr); + goto err_btn_press_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_btn_release_intr, + wcd_mbhc_release_handler, + "Button Release detect", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_btn_release_intr); + goto err_btn_release_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_hs_ins_intr, + mbhc->mbhc_fn->wcd_mbhc_hs_ins_irq, + "Elect Insert", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_hs_ins_intr); + goto err_mbhc_hs_ins_irq; + } + mbhc->mbhc_cb->irq_control(codec, mbhc->intr_ids->mbhc_hs_ins_intr, + false); + clear_bit(WCD_MBHC_ELEC_HS_INS, &mbhc->intr_status); + + ret = mbhc->mbhc_cb->request_irq(codec, + mbhc->intr_ids->mbhc_hs_rem_intr, + mbhc->mbhc_fn->wcd_mbhc_hs_rem_irq, + "Elect Remove", mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->mbhc_hs_rem_intr); + goto err_mbhc_hs_rem_irq; + } + mbhc->mbhc_cb->irq_control(codec, mbhc->intr_ids->mbhc_hs_rem_intr, + false); + clear_bit(WCD_MBHC_ELEC_HS_REM, &mbhc->intr_status); + + ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->hph_left_ocp, + wcd_mbhc_hphl_ocp_irq, "HPH_L OCP detect", + mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->hph_left_ocp); + goto err_hphl_ocp_irq; + } + + ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->hph_right_ocp, + wcd_mbhc_hphr_ocp_irq, "HPH_R OCP detect", + mbhc); + if (ret) { + pr_err("%s: Failed to request irq %d\n", __func__, + mbhc->intr_ids->hph_right_ocp); + goto err_hphr_ocp_irq; + } + + pr_debug("%s: leave ret %d\n", __func__, ret); + return ret; + +err_hphr_ocp_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_left_ocp, mbhc); +err_hphl_ocp_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_rem_intr, mbhc); +err_mbhc_hs_rem_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_ins_intr, mbhc); +err_mbhc_hs_ins_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_release_intr, + mbhc); +err_btn_release_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_press_intr, + mbhc); +err_btn_press_irq: + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_sw_intr, mbhc); +err_mbhc_sw_irq: + if (mbhc->mbhc_cb->register_notifier) + mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false); + mutex_destroy(&mbhc->codec_resource_lock); +err: + pr_debug("%s: leave ret %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(wcd_mbhc_init); + +void wcd_mbhc_deinit(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_sw_intr, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_press_intr, + mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_release_intr, + mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_ins_intr, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_rem_intr, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_left_ocp, mbhc); + mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_right_ocp, mbhc); + if (mbhc->mbhc_cb && mbhc->mbhc_cb->register_notifier) + mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false); + if (mbhc->mbhc_fn->wcd_cancel_hs_detect_plug) { + WCD_MBHC_RSC_LOCK(mbhc); + mbhc->mbhc_fn->wcd_cancel_hs_detect_plug(mbhc, + &mbhc->correct_plug_swch); + WCD_MBHC_RSC_UNLOCK(mbhc); + } + mutex_destroy(&mbhc->codec_resource_lock); + mutex_destroy(&mbhc->hphl_pa_lock); + mutex_destroy(&mbhc->hphr_pa_lock); +} +EXPORT_SYMBOL(wcd_mbhc_deinit); + +MODULE_DESCRIPTION("wcd MBHC v2 module"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h new file mode 100644 index 000000000000..7ed06c355990 --- /dev/null +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -0,0 +1,592 @@ +/* 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 __WCD_MBHC_V2_H__ +#define __WCD_MBHC_V2_H__ + +#include +#include +#include +#include "wcdcal-hwdep.h" + +#define TOMBAK_MBHC_NC 0 +#define TOMBAK_MBHC_NO 1 +#define WCD_MBHC_DEF_BUTTONS 8 +#define WCD_MBHC_KEYCODE_NUM 8 +#define WCD_MBHC_USLEEP_RANGE_MARGIN_US 100 +#define WCD_MBHC_THR_HS_MICB_MV 2700 +/* z value defined in Ohms */ +#define WCD_MONO_HS_MIN_THR 2 +#define WCD_MBHC_STRINGIFY(s) __stringify(s) + +#define WCD_MBHC_REGISTER(rid, rreg, rmask, rshift, rinvert) \ +{ .id = rid, .reg = rreg, .mask = rmask, .offset = rshift, .invert = rinvert } + +#define WCD_MBHC_RSC_LOCK(mbhc) \ +{ \ + pr_debug("%s: Acquiring BCL\n", __func__); \ + mutex_lock(&mbhc->codec_resource_lock); \ + pr_debug("%s: Acquiring BCL done\n", __func__); \ +} + +#define WCD_MBHC_RSC_UNLOCK(mbhc) \ +{ \ + pr_debug("%s: Release BCL\n", __func__); \ + mutex_unlock(&mbhc->codec_resource_lock); \ +} + +#define WCD_MBHC_RSC_ASSERT_LOCKED(mbhc) \ +{ \ + WARN_ONCE(!mutex_is_locked(&mbhc->codec_resource_lock), \ + "%s: BCL should have acquired\n", __func__); \ +} + +/* + * Macros to update and read mbhc register bits. Check for + * "0" before updating or reading the register, because it + * is possible that one codec wants to write to that bit and + * other codec does not. + */ +#define WCD_MBHC_REG_UPDATE_BITS(function, val) \ +do { \ + if (mbhc->wcd_mbhc_regs[function].reg) { \ + snd_soc_update_bits(mbhc->codec, \ + mbhc->wcd_mbhc_regs[function].reg, \ + mbhc->wcd_mbhc_regs[function].mask, \ + val << (mbhc->wcd_mbhc_regs[function].offset)); \ + } \ +} while (0) + +#define WCD_MBHC_REG_READ(function, val) \ +do { \ + if (mbhc->wcd_mbhc_regs[function].reg) { \ + val = (((snd_soc_read(mbhc->codec, \ + mbhc->wcd_mbhc_regs[function].reg)) & \ + (mbhc->wcd_mbhc_regs[function].mask)) >> \ + (mbhc->wcd_mbhc_regs[function].offset)); \ + } else { \ + val = -EINVAL; \ + } \ +} while (0) + +#define WCD_MBHC_CAL_SIZE(buttons, rload) ( \ + sizeof(struct wcd_mbhc_general_cfg) + \ + sizeof(struct wcd_mbhc_plug_detect_cfg) + \ + ((sizeof(s16) + sizeof(s16)) * buttons) + \ + sizeof(struct wcd_mbhc_plug_type_cfg) + \ + sizeof(struct wcd_mbhc_btn_detect_cfg) + \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + \ + ((sizeof(u16) + sizeof(u16)) * rload) \ + ) + +#define WCD_MBHC_CAL_GENERAL_PTR(cali) ( \ + (struct wcd_mbhc_general_cfg *) cali) +#define WCD_MBHC_CAL_PLUG_DET_PTR(cali) ( \ + (struct wcd_mbhc_plug_detect_cfg *) \ + &(WCD_MBHC_CAL_GENERAL_PTR(cali)[1])) +#define WCD_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \ + (struct wcd_mbhc_plug_type_cfg *) \ + &(WCD_MBHC_CAL_PLUG_DET_PTR(cali)[1])) +#define WCD_MBHC_CAL_BTN_DET_PTR(cali) ( \ + (struct wcd_mbhc_btn_detect_cfg *) \ + &(WCD_MBHC_CAL_PLUG_TYPE_PTR(cali)[1])) +#define WCD_MBHC_CAL_IMPED_DET_PTR(cali) ( \ + (struct wcd_mbhc_imped_detect_cfg *) \ + (((void *)&WCD_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \ + (WCD_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \ + (sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \ + sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \ + ) + +#define WCD_MBHC_CAL_MIN_SIZE ( \ + sizeof(struct wcd_mbhc_general_cfg) + \ + sizeof(struct wcd_mbhc_plug_detect_cfg) + \ + sizeof(struct wcd_mbhc_plug_type_cfg) + \ + sizeof(struct wcd_mbhc_btn_detect_cfg) + \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + \ + (sizeof(u16)*2) \ + ) + +#define WCD_MBHC_CAL_BTN_SZ(cfg_ptr) ( \ + sizeof(struct wcd_mbhc_btn_detect_cfg) + \ + (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \ + sizeof(cfg_ptr->_v_btn_high[0])))) + +#define WCD_MBHC_CAL_IMPED_MIN_SZ ( \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + sizeof(u16) * 2) + +#define WCD_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \ + sizeof(struct wcd_mbhc_imped_detect_cfg) + \ + (cfg_ptr->_n_rload * \ + (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0])))) + +#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \ + SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \ + SND_JACK_MECHANICAL | SND_JACK_MICROPHONE2 | \ + SND_JACK_UNSUPPORTED) + +#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \ + SND_JACK_BTN_2 | SND_JACK_BTN_3 | \ + SND_JACK_BTN_4 | SND_JACK_BTN_5) +#define OCP_ATTEMPT 20 +#define HS_DETECT_PLUG_TIME_MS (3 * 1000) +#define SPECIAL_HS_DETECT_TIME_MS (2 * 1000) +#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250 +#define GND_MIC_SWAP_THRESHOLD 4 +#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100 +#define HS_VREF_MIN_VAL 1400 +#define FW_READ_ATTEMPTS 15 +#define FW_READ_TIMEOUT 4000000 +#define FAKE_REM_RETRY_ATTEMPTS 3 +#define MAX_IMPED 60000 + +#define WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS 50 +#define ANC_DETECT_RETRY_CNT 7 +#define WCD_MBHC_SPL_HS_CNT 1 + +enum wcd_mbhc_detect_logic { + WCD_DETECTION_LEGACY, + WCD_DETECTION_ADC, +}; + +#ifdef CONFIG_SND_SOC_WCD_MBHC_ADC +#define WCD_MBHC_DETECTION WCD_DETECTION_ADC +#else +#define WCD_MBHC_DETECTION WCD_DETECTION_LEGACY +#endif + +enum wcd_mbhc_cs_mb_en_flag { + WCD_MBHC_EN_CS = 0, + WCD_MBHC_EN_MB, + WCD_MBHC_EN_PULLUP, + WCD_MBHC_EN_NONE, +}; + +enum { + WCD_MBHC_ELEC_HS_INS, + WCD_MBHC_ELEC_HS_REM, +}; + +struct wcd_mbhc; +enum wcd_mbhc_register_function { + WCD_MBHC_L_DET_EN, + WCD_MBHC_GND_DET_EN, + WCD_MBHC_MECH_DETECTION_TYPE, + WCD_MBHC_MIC_CLAMP_CTL, + WCD_MBHC_ELECT_DETECTION_TYPE, + WCD_MBHC_HS_L_DET_PULL_UP_CTRL, + WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, + WCD_MBHC_HPHL_PLUG_TYPE, + WCD_MBHC_GND_PLUG_TYPE, + WCD_MBHC_SW_HPH_LP_100K_TO_GND, + WCD_MBHC_ELECT_SCHMT_ISRC, + WCD_MBHC_FSM_EN, + WCD_MBHC_INSREM_DBNC, + WCD_MBHC_BTN_DBNC, + WCD_MBHC_HS_VREF, + WCD_MBHC_HS_COMP_RESULT, + WCD_MBHC_MIC_SCHMT_RESULT, + WCD_MBHC_HPHL_SCHMT_RESULT, + WCD_MBHC_HPHR_SCHMT_RESULT, + WCD_MBHC_OCP_FSM_EN, + WCD_MBHC_BTN_RESULT, + WCD_MBHC_BTN_ISRC_CTL, + WCD_MBHC_ELECT_RESULT, + WCD_MBHC_MICB_CTRL, /* Pull-up and micb control */ + WCD_MBHC_HPH_CNP_WG_TIME, + WCD_MBHC_HPHR_PA_EN, + WCD_MBHC_HPHL_PA_EN, + WCD_MBHC_HPH_PA_EN, + WCD_MBHC_SWCH_LEVEL_REMOVE, + WCD_MBHC_PULLDOWN_CTRL, + WCD_MBHC_ANC_DET_EN, + WCD_MBHC_FSM_STATUS, + WCD_MBHC_MUX_CTL, + WCD_MBHC_HPHL_OCP_DET_EN, + WCD_MBHC_HPHR_OCP_DET_EN, + WCD_MBHC_HPHL_OCP_STATUS, + WCD_MBHC_HPHR_OCP_STATUS, + WCD_MBHC_ADC_EN, + WCD_MBHC_ADC_COMPLETE, + WCD_MBHC_ADC_TIMEOUT, + WCD_MBHC_ADC_RESULT, + WCD_MBHC_MICB2_VOUT, + WCD_MBHC_ADC_MODE, + WCD_MBHC_DETECTION_DONE, + WCD_MBHC_ELECT_ISRC_EN, + WCD_MBHC_REG_FUNC_MAX, +}; + +enum wcd_mbhc_plug_type { + MBHC_PLUG_TYPE_INVALID = -1, + MBHC_PLUG_TYPE_NONE, + MBHC_PLUG_TYPE_HEADSET, + MBHC_PLUG_TYPE_HEADPHONE, + MBHC_PLUG_TYPE_HIGH_HPH, + MBHC_PLUG_TYPE_GND_MIC_SWAP, + MBHC_PLUG_TYPE_ANC_HEADPHONE, +}; + +enum pa_dac_ack_flags { + WCD_MBHC_HPHL_PA_OFF_ACK = 0, + WCD_MBHC_HPHR_PA_OFF_ACK, +}; + +enum wcd_mbhc_btn_det_mem { + WCD_MBHC_BTN_DET_V_BTN_LOW, + WCD_MBHC_BTN_DET_V_BTN_HIGH +}; + +enum { + MIC_BIAS_1 = 1, + MIC_BIAS_2, + MIC_BIAS_3, + MIC_BIAS_4 +}; + +enum { + MICB_PULLUP_ENABLE, + MICB_PULLUP_DISABLE, + MICB_ENABLE, + MICB_DISABLE, +}; + +enum { + MBHC_COMMON_MICB_PRECHARGE, + MBHC_COMMON_MICB_SET_VAL, + MBHC_COMMON_MICB_TAIL_CURR, +}; + +enum wcd_notify_event { + WCD_EVENT_INVALID, + /* events for micbias ON and OFF */ + WCD_EVENT_PRE_MICBIAS_2_OFF, + WCD_EVENT_POST_MICBIAS_2_OFF, + WCD_EVENT_PRE_MICBIAS_2_ON, + WCD_EVENT_POST_MICBIAS_2_ON, + WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF, + WCD_EVENT_POST_DAPM_MICBIAS_2_OFF, + WCD_EVENT_PRE_DAPM_MICBIAS_2_ON, + WCD_EVENT_POST_DAPM_MICBIAS_2_ON, + /* events for PA ON and OFF */ + WCD_EVENT_PRE_HPHL_PA_ON, + WCD_EVENT_POST_HPHL_PA_OFF, + WCD_EVENT_PRE_HPHR_PA_ON, + WCD_EVENT_POST_HPHR_PA_OFF, + WCD_EVENT_PRE_HPHL_PA_OFF, + WCD_EVENT_PRE_HPHR_PA_OFF, + WCD_EVENT_OCP_OFF, + WCD_EVENT_OCP_ON, + WCD_EVENT_LAST, +}; + +enum wcd_mbhc_event_state { + WCD_MBHC_EVENT_PA_HPHL, + WCD_MBHC_EVENT_PA_HPHR, +}; + +struct wcd_mbhc_general_cfg { + u8 t_ldoh; + u8 t_bg_fast_settle; + u8 t_shutdown_plug_rem; + u8 mbhc_nsa; + u8 mbhc_navg; + u8 v_micbias_l; + u8 v_micbias; + u8 mbhc_reserved; + u16 settle_wait; + u16 t_micbias_rampup; + u16 t_micbias_rampdown; + u16 t_supply_bringup; +} __packed; + +struct wcd_mbhc_plug_detect_cfg { + u32 mic_current; + u32 hph_current; + u16 t_mic_pid; + u16 t_ins_complete; + u16 t_ins_retry; + u16 v_removal_delta; + u8 micbias_slow_ramp; + u8 reserved0; + u8 reserved1; + u8 reserved2; +} __packed; + +struct wcd_mbhc_plug_type_cfg { + u8 av_detect; + u8 mono_detect; + u8 num_ins_tries; + u8 reserved0; + s16 v_no_mic; + s16 v_av_min; + s16 v_av_max; + s16 v_hs_min; + s16 v_hs_max; + u16 reserved1; +} __packed; + +struct wcd_mbhc_btn_detect_cfg { + s8 c[8]; + u8 nc; + u8 n_meas; + u8 mbhc_nsc; + u8 n_btn_meas; + u8 n_btn_con; + u8 num_btn; + u8 reserved0; + u8 reserved1; + u16 t_poll; + u16 t_bounce_wait; + u16 t_rel_timeout; + s16 v_btn_press_delta_sta; + s16 v_btn_press_delta_cic; + u16 t_btn0_timeout; + s16 _v_btn_low[0]; /* v_btn_low[num_btn] */ + s16 _v_btn_high[0]; /* v_btn_high[num_btn] */ + u8 _n_ready[2]; + u8 _n_cic[2]; + u8 _gain[2]; +} __packed; + +struct wcd_mbhc_imped_detect_cfg { + u8 _hs_imped_detect; + u8 _n_rload; + u8 _hph_keep_on; + u8 _repeat_rload_calc; + u16 _t_dac_ramp_time; + u16 _rhph_high; + u16 _rhph_low; + u16 _rload[0]; /* rload[n_rload] */ + u16 _alpha[0]; /* alpha[n_rload] */ + u16 _beta[3]; +} __packed; + +enum wcd_mbhc_hph_type { + WCD_MBHC_HPH_NONE = 0, + WCD_MBHC_HPH_MONO, + WCD_MBHC_HPH_STEREO, +}; + +/* + * These enum definitions are directly mapped to the register + * definitions + */ +enum mbhc_moisture_vref { + V_OFF, + V_45_MV, + V_100_MV, + V_225_MV, +}; + +enum mbhc_hs_pullup_iref { + I_DEFAULT = -1, + I_OFF = 0, + I_1P0_UA, + I_2P0_UA, + I_3P0_UA, +}; + +enum mbhc_moisture_rref { + R_OFF, + R_24_KOHM, + R_84_KOHM, + R_184_KOHM, +}; + +struct usbc_ana_audio_config { + int usbc_en1_gpio; + int usbc_en2_gpio; + int usbc_force_gpio; + struct device_node *usbc_en1_gpio_p; /* used by pinctrl API */ + struct device_node *usbc_en2_gpio_p; /* used by pinctrl API */ + struct device_node *usbc_force_gpio_p; /* used by pinctrl API */ +}; + +struct wcd_mbhc_config { + bool read_fw_bin; + void *calibration; + bool detect_extn_cable; + bool mono_stero_detection; + bool (*swap_gnd_mic)(struct snd_soc_codec *codec, bool active); + bool hs_ext_micbias; + bool gnd_det_en; + int key_code[WCD_MBHC_KEYCODE_NUM]; + uint32_t linein_th; + bool moisture_en; + int mbhc_micbias; + int anc_micbias; + bool enable_anc_mic_detect; + u32 enable_usbc_analog; + struct usbc_ana_audio_config usbc_analog_cfg; +}; + +struct wcd_mbhc_intr { + int mbhc_sw_intr; + int mbhc_btn_press_intr; + int mbhc_btn_release_intr; + int mbhc_hs_ins_intr; + int mbhc_hs_rem_intr; + int hph_left_ocp; + int hph_right_ocp; +}; + +struct wcd_mbhc_register { + const char *id; + u16 reg; + u8 mask; + u8 offset; + u8 invert; +}; + +struct wcd_mbhc_cb { + int (*enable_mb_source)(struct wcd_mbhc *, bool); + void (*trim_btn_reg)(struct snd_soc_codec *); + void (*compute_impedance)(struct wcd_mbhc *, uint32_t *, uint32_t *); + void (*set_micbias_value)(struct snd_soc_codec *); + void (*set_auto_zeroing)(struct snd_soc_codec *, bool); + struct firmware_cal * (*get_hwdep_fw_cal)(struct wcd_mbhc *, + enum wcd_cal_type); + void (*set_cap_mode)(struct snd_soc_codec *, bool, bool); + int (*register_notifier)(struct wcd_mbhc *, + struct notifier_block *nblock, + bool enable); + int (*request_irq)(struct snd_soc_codec *, + int, irq_handler_t, const char *, void *); + void (*irq_control)(struct snd_soc_codec *, + int irq, bool enable); + int (*free_irq)(struct snd_soc_codec *, + int irq, void *); + void (*clk_setup)(struct snd_soc_codec *, bool); + int (*map_btn_code_to_num)(struct snd_soc_codec *); + bool (*lock_sleep)(struct wcd_mbhc *, bool); + bool (*micbias_enable_status)(struct wcd_mbhc *, int); + void (*mbhc_bias)(struct snd_soc_codec *, bool); + void (*mbhc_common_micb_ctrl)(struct snd_soc_codec *, + int event, bool); + void (*micb_internal)(struct snd_soc_codec *, + int micb_num, bool); + bool (*hph_pa_on_status)(struct snd_soc_codec *); + void (*set_btn_thr)(struct snd_soc_codec *, s16 *, s16 *, + int num_btn, bool); + void (*hph_pull_up_control)(struct snd_soc_codec *, + enum mbhc_hs_pullup_iref); + int (*mbhc_micbias_control)(struct snd_soc_codec *, int, int req); + void (*mbhc_micb_ramp_control)(struct snd_soc_codec *, bool); + void (*skip_imped_detect)(struct snd_soc_codec *); + bool (*extn_use_mb)(struct snd_soc_codec *); + int (*mbhc_micb_ctrl_thr_mic)(struct snd_soc_codec *, int, bool); + void (*mbhc_gnd_det_ctrl)(struct snd_soc_codec *, bool); + void (*hph_pull_down_ctrl)(struct snd_soc_codec *, bool); + void (*mbhc_moisture_config)(struct wcd_mbhc *); + bool (*hph_register_recovery)(struct wcd_mbhc *); +}; + +struct wcd_mbhc_fn { + irqreturn_t (*wcd_mbhc_hs_ins_irq)(int irq, void *data); + irqreturn_t (*wcd_mbhc_hs_rem_irq)(int irq, void *data); + void (*wcd_mbhc_detect_plug_type)(struct wcd_mbhc *mbhc); + bool (*wcd_mbhc_detect_anc_plug_type)(struct wcd_mbhc *mbhc); + void (*wcd_cancel_hs_detect_plug)(struct wcd_mbhc *mbhc, + struct work_struct *work); +}; + +struct wcd_mbhc { + /* Delayed work to report long button press */ + struct delayed_work mbhc_btn_dwork; + int buttons_pressed; + struct wcd_mbhc_config *mbhc_cfg; + const struct wcd_mbhc_cb *mbhc_cb; + + u32 hph_status; /* track headhpone status */ + u8 hphlocp_cnt; /* headphone left ocp retry */ + u8 hphrocp_cnt; /* headphone right ocp retry */ + + wait_queue_head_t wait_btn_press; + bool is_btn_press; + u8 current_plug; + bool in_swch_irq_handler; + bool hphl_swh; /*track HPHL switch NC / NO */ + bool gnd_swh; /*track GND switch NC / NO */ + u32 moist_vref; + u32 moist_iref; + u32 moist_rref; + u8 micbias1_cap_mode; /* track ext cap setting */ + u8 micbias2_cap_mode; /* track ext cap setting */ + bool hs_detect_work_stop; + bool micbias_enable; + bool btn_press_intr; + bool is_hs_recording; + bool is_extn_cable; + bool skip_imped_detection; + bool is_btn_already_regd; + bool extn_cable_hph_rem; + + struct snd_soc_codec *codec; + /* Work to perform MBHC Firmware Read */ + struct delayed_work mbhc_firmware_dwork; + const struct firmware *mbhc_fw; + struct firmware_cal *mbhc_cal; + + /* track PA/DAC state to sync with userspace */ + unsigned long hph_pa_dac_state; + unsigned long event_state; + unsigned long jiffies_atreport; + + /* impedance of hphl and hphr */ + uint32_t zl, zr; + bool impedance_detect; + + /* Holds type of Headset - Mono/Stereo */ + enum wcd_mbhc_hph_type hph_type; + + struct snd_soc_jack headset_jack; + struct snd_soc_jack button_jack; + struct mutex codec_resource_lock; + + /* Holds codec specific interrupt mapping */ + const struct wcd_mbhc_intr *intr_ids; + + /* Work to correct accessory type */ + struct work_struct correct_plug_swch; + struct notifier_block nblock; + + struct wcd_mbhc_register *wcd_mbhc_regs; + + struct completion btn_press_compl; + struct mutex hphl_pa_lock; + struct mutex hphr_pa_lock; + + unsigned long intr_status; + bool is_hph_ocp_pending; + + bool usbc_force_pr_mode; + int usbc_mode; + struct notifier_block psy_nb; + struct power_supply *usb_psy; + struct work_struct usbc_analog_work; + + struct wcd_mbhc_fn *mbhc_fn; +}; + +void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, + enum wcd_mbhc_plug_type plug_type); +void wcd_mbhc_hs_elec_irq(struct wcd_mbhc *mbhc, int irq_type, bool enable); +void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc); +bool wcd_swch_level_remove(struct wcd_mbhc *mbhc); +void wcd_enable_curr_micbias(const struct wcd_mbhc *mbhc, + const enum wcd_mbhc_cs_mb_en_flag cs_mb_en); +void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc, + struct snd_soc_jack *jack, int status, int mask); +int wcd_cancel_btn_work(struct wcd_mbhc *mbhc); +int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc); + +#endif /* __WCD_MBHC_V2_H__ */ diff --git a/sound/soc/codecs/wcd-spi-registers.h b/sound/soc/codecs/wcd-spi-registers.h new file mode 100644 index 000000000000..4e579696cc49 --- /dev/null +++ b/sound/soc/codecs/wcd-spi-registers.h @@ -0,0 +1,43 @@ +/* + * 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 __WCD_SPI_REGISTERS_H__ +#define __WCD_SPI_REGISTERS_H__ + +#include + +#define WCD_SPI_SLAVE_SANITY (0x00) +#define WCD_SPI_SLAVE_DEVICE_ID (0x04) +#define WCD_SPI_SLAVE_STATUS (0x08) +#define WCD_SPI_SLAVE_CONFIG (0x0c) +#define WCD_SPI_SLAVE_SW_RESET (0x10) +#define WCD_SPI_SLAVE_IRQ_STATUS (0x14) +#define WCD_SPI_SLAVE_IRQ_EN (0x18) +#define WCD_SPI_SLAVE_IRQ_CLR (0x1c) +#define WCD_SPI_SLAVE_IRQ_FORCE (0x20) +#define WCD_SPI_SLAVE_TX (0x24) +#define WCD_SPI_SLAVE_TEST_BUS_DATA (0x2c) +#define WCD_SPI_SLAVE_TEST_BUS_CTRL (0x30) +#define WCD_SPI_SLAVE_SW_RST_IRQ (0x34) +#define WCD_SPI_SLAVE_CHAR_CFG (0x38) +#define WCD_SPI_SLAVE_CHAR_DATA_MOSI (0x3c) +#define WCD_SPI_SLAVE_CHAR_DATA_CS_N (0x40) +#define WCD_SPI_SLAVE_CHAR_DATA_MISO (0x44) +#define WCD_SPI_SLAVE_TRNS_BYTE_CNT (0x4c) +#define WCD_SPI_SLAVE_TRNS_LEN (0x50) +#define WCD_SPI_SLAVE_FIFO_LEVEL (0x54) +#define WCD_SPI_SLAVE_GENERICS (0x58) +#define WCD_SPI_SLAVE_EXT_BASE_ADDR (0x5c) +#define WCD_SPI_MAX_REGISTER (0x5F) + +#endif /* End __WCD_SPI_REGISTERS_H__ */ diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c new file mode 100644 index 000000000000..957d6428427c --- /dev/null +++ b/sound/soc/codecs/wcd-spi.c @@ -0,0 +1,1535 @@ +/* + * 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 "wcd-spi-registers.h" + +/* Byte manipulations */ +#define SHIFT_1_BYTES (8) +#define SHIFT_2_BYTES (16) +#define SHIFT_3_BYTES (24) + +/* Command opcodes */ +#define WCD_SPI_CMD_NOP (0x00) +#define WCD_SPI_CMD_WREN (0x06) +#define WCD_SPI_CMD_CLKREQ (0xDA) +#define WCD_SPI_CMD_RDSR (0x05) +#define WCD_SPI_CMD_IRR (0x81) +#define WCD_SPI_CMD_IRW (0x82) +#define WCD_SPI_CMD_MIOR (0x83) +#define WCD_SPI_CMD_FREAD (0x0B) +#define WCD_SPI_CMD_MIOW (0x02) +#define WCD_SPI_WRITE_FRAME_OPCODE \ + (WCD_SPI_CMD_MIOW << SHIFT_3_BYTES) +#define WCD_SPI_READ_FRAME_OPCODE \ + (WCD_SPI_CMD_MIOR << SHIFT_3_BYTES) +#define WCD_SPI_FREAD_FRAME_OPCODE \ + (WCD_SPI_CMD_FREAD << SHIFT_3_BYTES) + +/* Command lengths */ +#define WCD_SPI_OPCODE_LEN (0x01) +#define WCD_SPI_CMD_NOP_LEN (0x01) +#define WCD_SPI_CMD_WREN_LEN (0x01) +#define WCD_SPI_CMD_CLKREQ_LEN (0x04) +#define WCD_SPI_CMD_IRR_LEN (0x04) +#define WCD_SPI_CMD_IRW_LEN (0x06) +#define WCD_SPI_WRITE_SINGLE_LEN (0x08) +#define WCD_SPI_READ_SINGLE_LEN (0x13) +#define WCD_SPI_CMD_FREAD_LEN (0x13) + +/* Command delays */ +#define WCD_SPI_CLKREQ_DELAY_USECS (500) +#define WCD_SPI_CLK_OFF_TIMER_MS (500) +#define WCD_SPI_RESUME_TIMEOUT_MS 100 + +/* Command masks */ +#define WCD_CMD_ADDR_MASK \ + (0xFF | \ + (0xFF << SHIFT_1_BYTES) | \ + (0xFF << SHIFT_2_BYTES)) + +/* Clock ctrl request related */ +#define WCD_SPI_CLK_ENABLE true +#define WCD_SPI_CLK_DISABLE false +#define WCD_SPI_CLK_FLAG_DELAYED (1 << 0) +#define WCD_SPI_CLK_FLAG_IMMEDIATE (1 << 1) + +/* Internal addresses */ +#define WCD_SPI_ADDR_IPC_CTL_HOST (0x012014) + +/* Word sizes and min/max lengths */ +#define WCD_SPI_WORD_BYTE_CNT (4) +#define WCD_SPI_RW_MULTI_MIN_LEN (16) + +/* Max size is 32 bytes less than 64Kbytes */ +#define WCD_SPI_RW_MULTI_MAX_LEN ((64 * 1024) - 32) + +/* + * Max size for the pre-allocated buffers is the max + * possible read/write length + 32 bytes for the SPI + * read/write command header itself. + */ +#define WCD_SPI_RW_MAX_BUF_SIZE (WCD_SPI_RW_MULTI_MAX_LEN + 32) + +/* Alignment requirements */ +#define WCD_SPI_RW_MIN_ALIGN WCD_SPI_WORD_BYTE_CNT +#define WCD_SPI_RW_MULTI_ALIGN (16) + +/* Status mask bits */ +#define WCD_SPI_CLK_STATE_ENABLED BIT(0) +#define WCD_SPI_IS_SUSPENDED BIT(1) + +/* Locking related */ +#define WCD_SPI_MUTEX_LOCK(spi, lock) \ +{ \ + dev_vdbg(&spi->dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WCD_SPI_MUTEX_UNLOCK(spi, lock) \ +{ \ + dev_vdbg(&spi->dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +struct wcd_spi_debug_data { + struct dentry *dir; + u32 addr; + u32 size; +}; + +struct wcd_spi_priv { + struct spi_device *spi; + u32 mem_base_addr; + + struct regmap *regmap; + + /* Message for single transfer */ + struct spi_message msg1; + struct spi_transfer xfer1; + + /* Message for two transfers */ + struct spi_message msg2; + struct spi_transfer xfer2[2]; + + /* Register access related */ + u32 reg_bytes; + u32 val_bytes; + + /* Clock requests related */ + struct mutex clk_mutex; + int clk_users; + unsigned long status_mask; + struct delayed_work clk_dwork; + + /* Transaction related */ + struct mutex xfer_mutex; + + struct device *m_dev; + struct wdsp_mgr_ops *m_ops; + + /* Debugfs related information */ + struct wcd_spi_debug_data debug_data; + + /* Completion object to indicate system resume completion */ + struct completion resume_comp; + + /* Buffers to hold memory used for transfers */ + void *tx_buf; + void *rx_buf; +}; + +enum xfer_request { + WCD_SPI_XFER_WRITE, + WCD_SPI_XFER_READ, +}; + + +static char *wcd_spi_xfer_req_str(enum xfer_request req) +{ + if (req == WCD_SPI_XFER_WRITE) + return "xfer_write"; + else if (req == WCD_SPI_XFER_READ) + return "xfer_read"; + else + return "xfer_invalid"; +} + +static void wcd_spi_reinit_xfer(struct spi_transfer *xfer) +{ + xfer->tx_buf = NULL; + xfer->rx_buf = NULL; + xfer->delay_usecs = 0; + xfer->len = 0; +} + +static bool wcd_spi_is_suspended(struct wcd_spi_priv *wcd_spi) +{ + return test_bit(WCD_SPI_IS_SUSPENDED, &wcd_spi->status_mask); +} + +static bool wcd_spi_can_suspend(struct wcd_spi_priv *wcd_spi) +{ + struct spi_device *spi = wcd_spi->spi; + + if (wcd_spi->clk_users > 0 || + test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) { + dev_err(&spi->dev, "%s: cannot suspend, clk_users = %d\n", + __func__, wcd_spi->clk_users); + return false; + } + + return true; +} + +static int wcd_spi_wait_for_resume(struct wcd_spi_priv *wcd_spi) +{ + struct spi_device *spi = wcd_spi->spi; + int rc = 0; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + /* If the system is already in resumed state, return right away */ + if (!wcd_spi_is_suspended(wcd_spi)) + goto done; + + /* If suspended then wait for resume to happen */ + reinit_completion(&wcd_spi->resume_comp); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + rc = wait_for_completion_timeout(&wcd_spi->resume_comp, + msecs_to_jiffies(WCD_SPI_RESUME_TIMEOUT_MS)); + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + if (rc == 0) { + dev_err(&spi->dev, "%s: failed to resume in %u msec\n", + __func__, WCD_SPI_RESUME_TIMEOUT_MS); + rc = -EIO; + goto done; + } + + dev_dbg(&spi->dev, "%s: resume successful\n", __func__); + rc = 0; +done: + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + return rc; +} + +static int wcd_spi_read_single(struct spi_device *spi, + u32 remote_addr, u32 *val) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 *tx_buf = wcd_spi->tx_buf; + u32 frame = 0; + int ret; + + dev_dbg(&spi->dev, "%s: remote_addr = 0x%x\n", + __func__, remote_addr); + + if (!tx_buf) { + dev_err(&spi->dev, "%s: tx_buf not allocated\n", + __func__); + return -ENOMEM; + } + + frame |= WCD_SPI_READ_FRAME_OPCODE; + frame |= remote_addr & WCD_CMD_ADDR_MASK; + + wcd_spi_reinit_xfer(tx_xfer); + frame = cpu_to_be32(frame); + memcpy(tx_buf, &frame, sizeof(frame)); + tx_xfer->tx_buf = tx_buf; + tx_xfer->len = WCD_SPI_READ_SINGLE_LEN; + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->rx_buf = val; + rx_xfer->len = sizeof(*val); + + ret = spi_sync(spi, &wcd_spi->msg2); + + return ret; +} + +static int wcd_spi_read_multi(struct spi_device *spi, + u32 remote_addr, u8 *data, + size_t len) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 *tx_buf = wcd_spi->tx_buf; + u8 *rx_buf = wcd_spi->rx_buf; + u32 frame = 0; + int ret; + + dev_dbg(&spi->dev, "%s: addr 0x%x, len = %zd\n", + __func__, remote_addr, len); + + frame |= WCD_SPI_FREAD_FRAME_OPCODE; + frame |= remote_addr & WCD_CMD_ADDR_MASK; + + if (!tx_buf || !rx_buf) { + dev_err(&spi->dev, "%s: %s not allocated\n", __func__, + (!tx_buf) ? "tx_buf" : "rx_buf"); + return -ENOMEM; + } + + wcd_spi_reinit_xfer(xfer); + frame = cpu_to_be32(frame); + memcpy(tx_buf, &frame, sizeof(frame)); + xfer->tx_buf = tx_buf; + xfer->rx_buf = rx_buf; + xfer->len = WCD_SPI_CMD_FREAD_LEN + len; + + ret = spi_sync(spi, &wcd_spi->msg1); + if (ret) { + dev_err(&spi->dev, "%s: failed, err = %d\n", + __func__, ret); + goto done; + } + + memcpy(data, rx_buf + WCD_SPI_CMD_FREAD_LEN, len); +done: + return ret; +} + +static int wcd_spi_write_single(struct spi_device *spi, + u32 remote_addr, u32 val) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 buf[WCD_SPI_WRITE_SINGLE_LEN]; + u32 frame = 0; + + dev_dbg(&spi->dev, "%s: remote_addr = 0x%x, val = 0x%x\n", + __func__, remote_addr, val); + + memset(buf, 0, WCD_SPI_WRITE_SINGLE_LEN); + frame |= WCD_SPI_WRITE_FRAME_OPCODE; + frame |= (remote_addr & WCD_CMD_ADDR_MASK); + + frame = cpu_to_be32(frame); + memcpy(buf, &frame, sizeof(frame)); + memcpy(buf + sizeof(frame), &val, sizeof(val)); + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = buf; + xfer->len = WCD_SPI_WRITE_SINGLE_LEN; + + return spi_sync(spi, &wcd_spi->msg1); +} + +static int wcd_spi_write_multi(struct spi_device *spi, + u32 remote_addr, u8 *data, + size_t len) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u32 frame = 0; + u8 *tx_buf = wcd_spi->tx_buf; + int xfer_len, ret; + + dev_dbg(&spi->dev, "%s: addr = 0x%x len = %zd\n", + __func__, remote_addr, len); + + frame |= WCD_SPI_WRITE_FRAME_OPCODE; + frame |= (remote_addr & WCD_CMD_ADDR_MASK); + + frame = cpu_to_be32(frame); + xfer_len = len + sizeof(frame); + + if (!tx_buf) { + dev_err(&spi->dev, "%s: tx_buf not allocated\n", + __func__); + return -ENOMEM; + } + + memcpy(tx_buf, &frame, sizeof(frame)); + memcpy(tx_buf + sizeof(frame), data, len); + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = tx_buf; + xfer->len = xfer_len; + + ret = spi_sync(spi, &wcd_spi->msg1); + if (ret < 0) + dev_err(&spi->dev, + "%s: Failed, addr = 0x%x, len = %zd\n", + __func__, remote_addr, len); + return ret; +} + +static int wcd_spi_transfer_split(struct spi_device *spi, + struct wcd_spi_msg *data_msg, + enum xfer_request xfer_req) +{ + u32 addr = data_msg->remote_addr; + u8 *data = data_msg->data; + int remain_size = data_msg->len; + int to_xfer, loop_cnt, ret = 0; + + /* Perform single writes until multi word alignment is met */ + loop_cnt = 1; + while (remain_size && + !IS_ALIGNED(addr, WCD_SPI_RW_MULTI_ALIGN)) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, addr, + (*(u32 *)data)); + else + ret = wcd_spi_read_single(spi, addr, + (u32 *)data); + if (ret < 0) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) start-word addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_WORD_BYTE_CNT; + data += WCD_SPI_WORD_BYTE_CNT; + remain_size -= WCD_SPI_WORD_BYTE_CNT; + loop_cnt++; + } + + /* Perform multi writes for max allowed multi writes */ + loop_cnt = 1; + while (remain_size >= WCD_SPI_RW_MULTI_MAX_LEN) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_multi(spi, addr, data, + WCD_SPI_RW_MULTI_MAX_LEN); + else + ret = wcd_spi_read_multi(spi, addr, data, + WCD_SPI_RW_MULTI_MAX_LEN); + if (ret < 0) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) max-write addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_RW_MULTI_MAX_LEN; + data += WCD_SPI_RW_MULTI_MAX_LEN; + remain_size -= WCD_SPI_RW_MULTI_MAX_LEN; + loop_cnt++; + } + + /* + * Perform write for max possible data that is multiple + * of the minimum size for multi-write commands. + */ + to_xfer = remain_size - (remain_size % WCD_SPI_RW_MULTI_MIN_LEN); + if (remain_size >= WCD_SPI_RW_MULTI_MIN_LEN && + to_xfer > 0) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_multi(spi, addr, data, to_xfer); + else + ret = wcd_spi_read_multi(spi, addr, data, to_xfer); + if (ret < 0) { + dev_err(&spi->dev, + "%s: %s fail write addr (0x%x), size (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + addr, to_xfer); + goto done; + } + + addr += to_xfer; + data += to_xfer; + remain_size -= to_xfer; + } + + /* Perform single writes for the last remaining data */ + loop_cnt = 1; + while (remain_size > 0) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, addr, (*((u32 *)data))); + else + ret = wcd_spi_read_single(spi, addr, (u32 *) data); + if (ret < 0) { + dev_err(&spi->dev, + "%s: %s fail iter(%d) end-write addr (0x%x)\n", + __func__, wcd_spi_xfer_req_str(xfer_req), + loop_cnt, addr); + goto done; + } + + addr += WCD_SPI_WORD_BYTE_CNT; + data += WCD_SPI_WORD_BYTE_CNT; + remain_size -= WCD_SPI_WORD_BYTE_CNT; + loop_cnt++; + } + +done: + return ret; +} + +static int wcd_spi_cmd_nop(struct spi_device *spi) +{ + u8 nop = WCD_SPI_CMD_NOP; + + return spi_write(spi, &nop, WCD_SPI_CMD_NOP_LEN); +} + +static int wcd_spi_cmd_clkreq(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *xfer = &wcd_spi->xfer1; + u8 cmd[WCD_SPI_CMD_CLKREQ_LEN] = { + WCD_SPI_CMD_CLKREQ, + 0xBA, 0x80, 0x00}; + + wcd_spi_reinit_xfer(xfer); + xfer->tx_buf = cmd; + xfer->len = WCD_SPI_CMD_CLKREQ_LEN; + xfer->delay_usecs = WCD_SPI_CLKREQ_DELAY_USECS; + + return spi_sync(spi, &wcd_spi->msg1); +} + +static int wcd_spi_cmd_wr_en(struct spi_device *spi) +{ + u8 wr_en = WCD_SPI_CMD_WREN; + + return spi_write(spi, &wr_en, WCD_SPI_CMD_WREN_LEN); +} + +static int wcd_spi_cmd_rdsr(struct spi_device *spi, + u32 *rdsr_status) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 rdsr_cmd; + u32 status; + int ret; + + rdsr_cmd = WCD_SPI_CMD_RDSR; + wcd_spi_reinit_xfer(tx_xfer); + tx_xfer->tx_buf = &rdsr_cmd; + tx_xfer->len = sizeof(rdsr_cmd); + + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->rx_buf = &status; + rx_xfer->len = sizeof(status); + + ret = spi_sync(spi, &wcd_spi->msg2); + if (ret < 0) { + dev_err(&spi->dev, "%s: RDSR failed, err = %d\n", + __func__, ret); + goto done; + } + + *rdsr_status = be32_to_cpu(status); + + dev_dbg(&spi->dev, "%s: RDSR success, value = 0x%x\n", + __func__, *rdsr_status); +done: + return ret; +} + +static int wcd_spi_clk_enable(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + u32 rd_status = 0; + + ret = wcd_spi_cmd_nop(spi); + if (ret < 0) { + dev_err(&spi->dev, "%s: NOP1 failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_spi_cmd_clkreq(spi); + if (ret < 0) { + dev_err(&spi->dev, "%s: CLK_REQ failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_spi_cmd_nop(spi); + if (ret < 0) { + dev_err(&spi->dev, "%s: NOP2 failed, err = %d\n", + __func__, ret); + goto done; + } + wcd_spi_cmd_rdsr(spi, &rd_status); + /* + * Read status zero means reads are not + * happenning on the bus, possibly because + * clock request failed. + */ + if (rd_status) { + set_bit(WCD_SPI_CLK_STATE_ENABLED, + &wcd_spi->status_mask); + } else { + dev_err(&spi->dev, "%s: RDSR status is zero\n", + __func__); + ret = -EIO; + } +done: + return ret; +} + +static int wcd_spi_clk_disable(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + ret = wcd_spi_write_single(spi, WCD_SPI_ADDR_IPC_CTL_HOST, 0x01); + if (ret < 0) + dev_err(&spi->dev, "%s: Failed, err = %d\n", + __func__, ret); + else + clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask); + + return ret; +} + +static int wcd_spi_clk_ctrl(struct spi_device *spi, + bool request, u32 flags) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret = 0; + const char *delay_str; + + delay_str = (flags == WCD_SPI_CLK_FLAG_DELAYED) ? + "delayed" : "immediate"; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + + /* Reject any unbalanced disable request */ + if (wcd_spi->clk_users < 0 || + (!request && wcd_spi->clk_users == 0)) { + dev_err(&spi->dev, "%s: Unbalanced clk_users %d for %s\n", + __func__, wcd_spi->clk_users, + request ? "enable" : "disable"); + ret = -EINVAL; + + /* Reset the clk_users to 0 */ + wcd_spi->clk_users = 0; + + goto done; + } + + if (request == WCD_SPI_CLK_ENABLE) { + /* + * If the SPI bus is suspended, then return error + * as the transaction cannot be completed. + */ + if (wcd_spi_is_suspended(wcd_spi)) { + dev_err(&spi->dev, + "%s: SPI suspended, cannot enable clk\n", + __func__); + ret = -EIO; + goto done; + } + + /* Cancel the disable clk work */ + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + cancel_delayed_work_sync(&wcd_spi->clk_dwork); + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + + wcd_spi->clk_users++; + + /* + * If clk state is already set, + * then clk wasnt really disabled + */ + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + goto done; + else if (wcd_spi->clk_users == 1) + ret = wcd_spi_clk_enable(spi); + + } else { + wcd_spi->clk_users--; + + /* Clock is still voted for */ + if (wcd_spi->clk_users > 0) + goto done; + + /* + * If we are here, clk_users must be 0 and needs + * to be disabled. Call the disable based on the + * flags. + */ + if (flags == WCD_SPI_CLK_FLAG_DELAYED) { + schedule_delayed_work(&wcd_spi->clk_dwork, + msecs_to_jiffies(WCD_SPI_CLK_OFF_TIMER_MS)); + } else { + ret = wcd_spi_clk_disable(spi); + if (ret < 0) + dev_err(&spi->dev, + "%s: Failed to disable clk err = %d\n", + __func__, ret); + } + } + +done: + dev_dbg(&spi->dev, "%s: updated clk_users = %d, request_%s %s\n", + __func__, wcd_spi->clk_users, request ? "enable" : "disable", + request ? "" : delay_str); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + + return ret; +} + +static int wcd_spi_init(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (ret < 0) + goto done; + + ret = wcd_spi_cmd_wr_en(spi); + if (ret < 0) + goto err_wr_en; + + /* + * In case spi_init is called after component deinit, + * it is possible hardware register state is also reset. + * Sync the regcache here so hardware state is updated + * to reflect the cache. + */ + regcache_sync(wcd_spi->regmap); + + regmap_write(wcd_spi->regmap, WCD_SPI_SLAVE_CONFIG, + 0x0F3D0800); + + /* Write the MTU to max allowed size */ + regmap_update_bits(wcd_spi->regmap, + WCD_SPI_SLAVE_TRNS_LEN, + 0xFFFF0000, 0xFFFF0000); +err_wr_en: + wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); +done: + return ret; +} + +static void wcd_spi_clk_work(struct work_struct *work) +{ + struct delayed_work *dwork; + struct wcd_spi_priv *wcd_spi; + struct spi_device *spi; + int ret; + + dwork = to_delayed_work(work); + wcd_spi = container_of(dwork, struct wcd_spi_priv, clk_dwork); + spi = wcd_spi->spi; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + ret = wcd_spi_clk_disable(spi); + if (ret < 0) + dev_err(&spi->dev, + "%s: Failed to disable clk, err = %d\n", + __func__, ret); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); +} + +static int __wcd_spi_data_xfer(struct spi_device *spi, + struct wcd_spi_msg *msg, + enum xfer_request xfer_req) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret; + + /* Check for minimum alignment requirements */ + if (!IS_ALIGNED(msg->remote_addr, WCD_SPI_RW_MIN_ALIGN)) { + dev_err(&spi->dev, + "%s addr 0x%x is not aligned to 0x%x\n", + __func__, msg->remote_addr, WCD_SPI_RW_MIN_ALIGN); + return -EINVAL; + } else if (msg->len % WCD_SPI_WORD_BYTE_CNT) { + dev_err(&spi->dev, + "%s len 0x%zx is not multiple of %d\n", + __func__, msg->len, WCD_SPI_WORD_BYTE_CNT); + return -EINVAL; + } + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->xfer_mutex); + if (msg->len == WCD_SPI_WORD_BYTE_CNT) { + if (xfer_req == WCD_SPI_XFER_WRITE) + ret = wcd_spi_write_single(spi, msg->remote_addr, + (*((u32 *)msg->data))); + else + ret = wcd_spi_read_single(spi, msg->remote_addr, + (u32 *) msg->data); + } else { + ret = wcd_spi_transfer_split(spi, msg, xfer_req); + } + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->xfer_mutex); + + return ret; +} + +static int wcd_spi_data_xfer(struct spi_device *spi, + struct wcd_spi_msg *msg, + enum xfer_request req) +{ + int ret, ret1; + + if (msg->len <= 0) { + dev_err(&spi->dev, "%s: Invalid size %zd\n", + __func__, msg->len); + return -EINVAL; + } + + /* Request for clock */ + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (ret < 0) { + dev_err(&spi->dev, "%s: clk enable failed %d\n", + __func__, ret); + goto done; + } + + /* Perform the transaction */ + ret = __wcd_spi_data_xfer(spi, msg, req); + if (ret < 0) + dev_err(&spi->dev, + "%s: Failed %s, addr = 0x%x, size = 0x%zx, err = %d\n", + __func__, wcd_spi_xfer_req_str(req), + msg->remote_addr, msg->len, ret); + + /* Release the clock even if xfer failed */ + ret1 = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_DELAYED); + if (ret1 < 0) + dev_err(&spi->dev, "%s: clk disable failed %d\n", + __func__, ret1); +done: + return ret; +} + +/* + * wcd_spi_data_write: Write data to WCD SPI + * @spi: spi_device struct + * @msg: msg that needs to be written to WCD + * + * This API writes length of data to address specified. These details + * about the write are encapsulated in @msg. Write size should be multiple + * of 4 bytes and write address should be 4-byte aligned. + */ +static int wcd_spi_data_write(struct spi_device *spi, + struct wcd_spi_msg *msg) +{ + if (!spi || !msg) { + pr_err("%s: Invalid %s\n", __func__, + (!spi) ? "spi device" : "msg"); + return -EINVAL; + } + + dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x, len = %zu\n", + __func__, msg->remote_addr, msg->len); + return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_WRITE); +} + +/* + * wcd_spi_data_read: Read data from WCD SPI + * @spi: spi_device struct + * @msg: msg that needs to be read from WCD + * + * This API reads length of data from address specified. These details + * about the read are encapsulated in @msg. Read size should be multiple + * of 4 bytes and read address should be 4-byte aligned. + */ +static int wcd_spi_data_read(struct spi_device *spi, + struct wcd_spi_msg *msg) +{ + if (!spi || !msg) { + pr_err("%s: Invalid %s\n", __func__, + (!spi) ? "spi device" : "msg"); + return -EINVAL; + } + + dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x,len = %zu\n", + __func__, msg->remote_addr, msg->len); + return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_READ); +} + +static int wdsp_spi_dload_section(struct spi_device *spi, + void *data) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wdsp_img_section *sec = data; + struct wcd_spi_msg msg; + int ret; + + dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n", + __func__, sec->addr, sec->size); + + msg.remote_addr = sec->addr + wcd_spi->mem_base_addr; + msg.data = sec->data; + msg.len = sec->size; + + ret = __wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_WRITE); + if (ret < 0) + dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n", + __func__, msg.remote_addr, msg.len); + return ret; +} + +static int wdsp_spi_read_section(struct spi_device *spi, void *data) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wdsp_img_section *sec = data; + struct wcd_spi_msg msg; + int ret; + + msg.remote_addr = sec->addr + wcd_spi->mem_base_addr; + msg.data = sec->data; + msg.len = sec->size; + + dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n", + __func__, msg.remote_addr, msg.len); + + ret = wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_READ); + if (ret < 0) + dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n", + __func__, msg.remote_addr, msg.len); + return ret; +} + +static int wdsp_spi_event_handler(struct device *dev, void *priv_data, + enum wdsp_event_type event, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_ops *spi_ops; + int ret = 0; + + dev_dbg(&spi->dev, "%s: event type %d\n", + __func__, event); + + switch (event) { + case WDSP_EVENT_POST_SHUTDOWN: + cancel_delayed_work_sync(&wcd_spi->clk_dwork); + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + wcd_spi_clk_disable(spi); + wcd_spi->clk_users = 0; + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + break; + + case WDSP_EVENT_PRE_DLOAD_CODE: + case WDSP_EVENT_PRE_DLOAD_DATA: + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (ret < 0) + dev_err(&spi->dev, "%s: clk_req failed %d\n", + __func__, ret); + break; + + case WDSP_EVENT_POST_DLOAD_CODE: + case WDSP_EVENT_POST_DLOAD_DATA: + case WDSP_EVENT_DLOAD_FAILED: + + ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE, + WCD_SPI_CLK_FLAG_IMMEDIATE); + if (ret < 0) + dev_err(&spi->dev, "%s: clk unvote failed %d\n", + __func__, ret); + break; + + case WDSP_EVENT_DLOAD_SECTION: + ret = wdsp_spi_dload_section(spi, data); + break; + + case WDSP_EVENT_READ_SECTION: + ret = wdsp_spi_read_section(spi, data); + break; + + case WDSP_EVENT_SUSPEND: + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + if (!wcd_spi_can_suspend(wcd_spi)) + ret = -EBUSY; + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + break; + + case WDSP_EVENT_RESUME: + ret = wcd_spi_wait_for_resume(wcd_spi); + break; + + case WDSP_EVENT_GET_DEVOPS: + if (!data) { + dev_err(&spi->dev, "%s: invalid data\n", + __func__); + ret = -EINVAL; + break; + } + + spi_ops = (struct wcd_spi_ops *) data; + spi_ops->spi_dev = spi; + spi_ops->read_dev = wcd_spi_data_read; + spi_ops->write_dev = wcd_spi_data_write; + break; + + default: + dev_dbg(&spi->dev, "%s: Unhandled event %d\n", + __func__, event); + break; + } + + return ret; +} + +static int wcd_spi_bus_gwrite(void *context, const void *reg, + size_t reg_len, const void *val, + size_t val_len) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + u8 tx_buf[WCD_SPI_CMD_IRW_LEN]; + + if (!reg || !val || reg_len != wcd_spi->reg_bytes || + val_len != wcd_spi->val_bytes) { + dev_err(&spi->dev, + "%s: Invalid input, reg_len = %zd, val_len = %zd", + __func__, reg_len, val_len); + return -EINVAL; + } + + tx_buf[0] = WCD_SPI_CMD_IRW; + tx_buf[1] = *((u8 *)reg); + memcpy(&tx_buf[WCD_SPI_OPCODE_LEN + reg_len], + val, val_len); + + return spi_write(spi, tx_buf, WCD_SPI_CMD_IRW_LEN); +} + +static int wcd_spi_bus_write(void *context, const void *data, + size_t count) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + if (count < (wcd_spi->reg_bytes + wcd_spi->val_bytes)) { + dev_err(&spi->dev, "%s: Invalid size %zd\n", + __func__, count); + WARN_ON(1); + return -EINVAL; + } + + return wcd_spi_bus_gwrite(context, data, wcd_spi->reg_bytes, + data + wcd_spi->reg_bytes, + count - wcd_spi->reg_bytes); +} + +static int wcd_spi_bus_read(void *context, const void *reg, + size_t reg_len, void *val, + size_t val_len) +{ + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; + struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; + u8 tx_buf[WCD_SPI_CMD_IRR_LEN]; + + if (!reg || !val || reg_len != wcd_spi->reg_bytes || + val_len != wcd_spi->val_bytes) { + dev_err(&spi->dev, + "%s: Invalid input, reg_len = %zd, val_len = %zd", + __func__, reg_len, val_len); + return -EINVAL; + } + + memset(tx_buf, 0, WCD_SPI_OPCODE_LEN); + tx_buf[0] = WCD_SPI_CMD_IRR; + tx_buf[1] = *((u8 *)reg); + + wcd_spi_reinit_xfer(tx_xfer); + tx_xfer->tx_buf = tx_buf; + tx_xfer->rx_buf = NULL; + tx_xfer->len = WCD_SPI_CMD_IRR_LEN; + + wcd_spi_reinit_xfer(rx_xfer); + rx_xfer->tx_buf = NULL; + rx_xfer->rx_buf = val; + rx_xfer->len = val_len; + + return spi_sync(spi, &wcd_spi->msg2); +} + +static struct regmap_bus wcd_spi_regmap_bus = { + .write = wcd_spi_bus_write, + .gather_write = wcd_spi_bus_gwrite, + .read = wcd_spi_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_BIG, +}; + +static int wcd_spi_state_show(struct seq_file *f, void *ptr) +{ + struct spi_device *spi = f->private; + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + const char *clk_state, *clk_mutex, *xfer_mutex; + + if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask)) + clk_state = "enabled"; + else + clk_state = "disabled"; + + clk_mutex = mutex_is_locked(&wcd_spi->clk_mutex) ? + "locked" : "unlocked"; + + xfer_mutex = mutex_is_locked(&wcd_spi->xfer_mutex) ? + "locked" : "unlocked"; + + seq_printf(f, "clk_state = %s\nclk_users = %d\n" + "clk_mutex = %s\nxfer_mutex = %s\n", + clk_state, wcd_spi->clk_users, clk_mutex, + xfer_mutex); + return 0; +} + +static int wcd_spi_state_open(struct inode *inode, struct file *file) +{ + return single_open(file, wcd_spi_state_show, inode->i_private); +} + +static const struct file_operations state_fops = { + .open = wcd_spi_state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t wcd_spi_debugfs_mem_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct spi_device *spi = file->private_data; + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data; + struct wcd_spi_msg msg; + ssize_t buf_size, read_count = 0; + char *buf; + int ret; + + if (*ppos < 0 || !count) + return -EINVAL; + + if (dbg_data->size == 0 || dbg_data->addr == 0) { + dev_err(&spi->dev, + "%s: Invalid request, size = %u, addr = 0x%x\n", + __func__, dbg_data->size, dbg_data->addr); + return 0; + } + + buf_size = count < dbg_data->size ? count : dbg_data->size; + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + msg.data = buf; + msg.remote_addr = dbg_data->addr; + msg.len = buf_size; + msg.flags = 0; + + ret = wcd_spi_data_read(spi, &msg); + if (ret < 0) { + dev_err(&spi->dev, + "%s: Failed to read %zu bytes from addr 0x%x\n", + __func__, buf_size, msg.remote_addr); + goto done; + } + + read_count = simple_read_from_buffer(ubuf, count, ppos, buf, buf_size); + +done: + kfree(buf); + if (ret < 0) + return ret; + else + return read_count; +} + +static const struct file_operations mem_read_fops = { + .open = simple_open, + .read = wcd_spi_debugfs_mem_read, +}; + +static int wcd_spi_debugfs_init(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data; + int rc = 0; + + dbg_data->dir = debugfs_create_dir("wcd_spi", NULL); + if (IS_ERR_OR_NULL(dbg_data->dir)) { + dbg_data->dir = NULL; + rc = -ENODEV; + goto done; + } + + debugfs_create_file("state", 0444, dbg_data->dir, spi, &state_fops); + debugfs_create_u32("addr", 0644, dbg_data->dir, + &dbg_data->addr); + debugfs_create_u32("size", 0644, dbg_data->dir, + &dbg_data->size); + + debugfs_create_file("mem_read", 0444, dbg_data->dir, + spi, &mem_read_fops); +done: + return rc; +} + + +static const struct reg_default wcd_spi_defaults[] = { + {WCD_SPI_SLAVE_SANITY, 0xDEADBEEF}, + {WCD_SPI_SLAVE_DEVICE_ID, 0x00500000}, + {WCD_SPI_SLAVE_STATUS, 0x80100000}, + {WCD_SPI_SLAVE_CONFIG, 0x0F200808}, + {WCD_SPI_SLAVE_SW_RESET, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_STATUS, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_EN, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_CLR, 0x00000000}, + {WCD_SPI_SLAVE_IRQ_FORCE, 0x00000000}, + {WCD_SPI_SLAVE_TX, 0x00000000}, + {WCD_SPI_SLAVE_TEST_BUS_DATA, 0x00000000}, + {WCD_SPI_SLAVE_TEST_BUS_CTRL, 0x00000000}, + {WCD_SPI_SLAVE_SW_RST_IRQ, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_CFG, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_MOSI, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_CS_N, 0x00000000}, + {WCD_SPI_SLAVE_CHAR_DATA_MISO, 0x00000000}, + {WCD_SPI_SLAVE_TRNS_BYTE_CNT, 0x00000000}, + {WCD_SPI_SLAVE_TRNS_LEN, 0x00000000}, + {WCD_SPI_SLAVE_FIFO_LEVEL, 0x00000000}, + {WCD_SPI_SLAVE_GENERICS, 0x80000000}, + {WCD_SPI_SLAVE_EXT_BASE_ADDR, 0x00000000}, +}; + +static bool wcd_spi_is_volatile_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case WCD_SPI_SLAVE_SANITY: + case WCD_SPI_SLAVE_STATUS: + case WCD_SPI_SLAVE_IRQ_STATUS: + case WCD_SPI_SLAVE_TX: + case WCD_SPI_SLAVE_SW_RST_IRQ: + case WCD_SPI_SLAVE_TRNS_BYTE_CNT: + case WCD_SPI_SLAVE_FIFO_LEVEL: + case WCD_SPI_SLAVE_GENERICS: + return true; + } + + return false; +} + +static bool wcd_spi_is_readable_reg(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case WCD_SPI_SLAVE_SW_RESET: + case WCD_SPI_SLAVE_IRQ_CLR: + case WCD_SPI_SLAVE_IRQ_FORCE: + return false; + } + + return true; +} + +static struct regmap_config wcd_spi_regmap_cfg = { + .reg_bits = 8, + .val_bits = 32, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wcd_spi_defaults, + .num_reg_defaults = ARRAY_SIZE(wcd_spi_defaults), + .max_register = WCD_SPI_MAX_REGISTER, + .volatile_reg = wcd_spi_is_volatile_reg, + .readable_reg = wcd_spi_is_readable_reg, +}; + +static int wdsp_spi_init(struct device *dev, void *priv_data) +{ + struct spi_device *spi = to_spi_device(dev); + int ret; + + ret = wcd_spi_init(spi); + if (ret < 0) + dev_err(&spi->dev, "%s: Init failed, err = %d\n", + __func__, ret); + return ret; +} + +static int wdsp_spi_deinit(struct device *dev, void *priv_data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + /* + * Deinit means the hardware is reset. Mark the cache + * as dirty here, so init will sync the cache + */ + regcache_mark_dirty(wcd_spi->regmap); + + return 0; +} + +static struct wdsp_cmpnt_ops wdsp_spi_ops = { + .init = wdsp_spi_init, + .deinit = wdsp_spi_deinit, + .event_handler = wdsp_spi_event_handler, +}; + +static int wcd_spi_component_bind(struct device *dev, + struct device *master, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int ret = 0; + + wcd_spi->m_dev = master; + wcd_spi->m_ops = data; + + if (wcd_spi->m_ops && + wcd_spi->m_ops->register_cmpnt_ops) + ret = wcd_spi->m_ops->register_cmpnt_ops(master, dev, + wcd_spi, + &wdsp_spi_ops); + if (ret) { + dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", + __func__, ret); + goto done; + } + + wcd_spi->reg_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.reg_bits, 8); + wcd_spi->val_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.val_bits, 8); + + wcd_spi->regmap = devm_regmap_init(&spi->dev, &wcd_spi_regmap_bus, + &spi->dev, &wcd_spi_regmap_cfg); + if (IS_ERR(wcd_spi->regmap)) { + ret = PTR_ERR(wcd_spi->regmap); + dev_err(&spi->dev, "%s: Failed to allocate regmap, err = %d\n", + __func__, ret); + goto done; + } + + if (wcd_spi_debugfs_init(spi)) + dev_err(&spi->dev, "%s: Failed debugfs init\n", __func__); + + spi_message_init(&wcd_spi->msg1); + spi_message_add_tail(&wcd_spi->xfer1, &wcd_spi->msg1); + + spi_message_init(&wcd_spi->msg2); + spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2); + spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2); + + /* Pre-allocate the buffers */ + wcd_spi->tx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE, + GFP_KERNEL | GFP_DMA); + if (!wcd_spi->tx_buf) { + ret = -ENOMEM; + goto done; + } + + wcd_spi->rx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE, + GFP_KERNEL | GFP_DMA); + if (!wcd_spi->rx_buf) { + kfree(wcd_spi->tx_buf); + wcd_spi->tx_buf = NULL; + ret = -ENOMEM; + goto done; + } +done: + return ret; +} + +static void wcd_spi_component_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + wcd_spi->m_dev = NULL; + wcd_spi->m_ops = NULL; + + spi_transfer_del(&wcd_spi->xfer1); + spi_transfer_del(&wcd_spi->xfer2[0]); + spi_transfer_del(&wcd_spi->xfer2[1]); + + kfree(wcd_spi->tx_buf); + kfree(wcd_spi->rx_buf); + wcd_spi->tx_buf = NULL; + wcd_spi->rx_buf = NULL; +} + +static const struct component_ops wcd_spi_component_ops = { + .bind = wcd_spi_component_bind, + .unbind = wcd_spi_component_unbind, +}; + +static int wcd_spi_probe(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi; + int ret = 0; + + wcd_spi = devm_kzalloc(&spi->dev, sizeof(*wcd_spi), + GFP_KERNEL); + if (!wcd_spi) + return -ENOMEM; + + ret = of_property_read_u32(spi->dev.of_node, + "qcom,mem-base-addr", + &wcd_spi->mem_base_addr); + if (ret < 0) { + dev_err(&spi->dev, "%s: Missing %s DT entry", + __func__, "qcom,mem-base-addr"); + goto err_ret; + } + + dev_dbg(&spi->dev, + "%s: mem_base_addr 0x%x\n", __func__, wcd_spi->mem_base_addr); + + mutex_init(&wcd_spi->clk_mutex); + mutex_init(&wcd_spi->xfer_mutex); + INIT_DELAYED_WORK(&wcd_spi->clk_dwork, wcd_spi_clk_work); + init_completion(&wcd_spi->resume_comp); + + wcd_spi->spi = spi; + spi_set_drvdata(spi, wcd_spi); + + ret = component_add(&spi->dev, &wcd_spi_component_ops); + if (ret) { + dev_err(&spi->dev, "%s: component_add failed err = %d\n", + __func__, ret); + goto err_component_add; + } + + return ret; + +err_component_add: + mutex_destroy(&wcd_spi->clk_mutex); + mutex_destroy(&wcd_spi->xfer_mutex); +err_ret: + devm_kfree(&spi->dev, wcd_spi); + spi_set_drvdata(spi, NULL); + return ret; +} + +static int wcd_spi_remove(struct spi_device *spi) +{ + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + component_del(&spi->dev, &wcd_spi_component_ops); + + mutex_destroy(&wcd_spi->clk_mutex); + mutex_destroy(&wcd_spi->xfer_mutex); + + devm_kfree(&spi->dev, wcd_spi); + spi_set_drvdata(spi, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int wcd_spi_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + int rc = 0; + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + if (!wcd_spi_can_suspend(wcd_spi)) { + rc = -EBUSY; + goto done; + } + + /* + * If we are here, it is okay to let the suspend go + * through for this driver. But, still need to notify + * the master to make sure all other components can suspend + * as well. + */ + if (wcd_spi->m_dev && wcd_spi->m_ops && + wcd_spi->m_ops->suspend) { + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + rc = wcd_spi->m_ops->suspend(wcd_spi->m_dev); + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + } + + if (rc == 0) + set_bit(WCD_SPI_IS_SUSPENDED, &wcd_spi->status_mask); + else + dev_dbg(&spi->dev, "%s: cannot suspend, err = %d\n", + __func__, rc); +done: + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + return rc; +} + +static int wcd_spi_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + + WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); + clear_bit(WCD_SPI_IS_SUSPENDED, &wcd_spi->status_mask); + complete(&wcd_spi->resume_comp); + WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); + + return 0; +} + +static const struct dev_pm_ops wcd_spi_pm_ops = { + .suspend = wcd_spi_suspend, + .resume = wcd_spi_resume, +}; +#endif + +static const struct of_device_id wcd_spi_of_match[] = { + { .compatible = "qcom,wcd-spi-v2", }, + { } +}; +MODULE_DEVICE_TABLE(of, wcd_spi_of_match); + +static struct spi_driver wcd_spi_driver = { + .driver = { + .name = "wcd-spi-v2", + .of_match_table = wcd_spi_of_match, +#ifdef CONFIG_PM + .pm = &wcd_spi_pm_ops, +#endif + }, + .probe = wcd_spi_probe, + .remove = wcd_spi_remove, +}; + +module_spi_driver(wcd_spi_driver); + +MODULE_DESCRIPTION("WCD SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c new file mode 100644 index 000000000000..329aa7a4c466 --- /dev/null +++ b/sound/soc/codecs/wcd9335.c @@ -0,0 +1,14178 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd9335.h" +#include "wcd-mbhc-v2.h" +#include "wcd9xxx-common-v2.h" +#include "wcd9xxx-resmgr-v2.h" +#include "wcd_cpe_core.h" +#include "wcdcal-hwdep.h" +#include "wcd-mbhc-v2-api.h" + +#define TASHA_RX_PORT_START_NUMBER 16 + +#define WCD9335_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +/* Fractional Rates */ +#define WCD9335_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100) + +#define WCD9335_MIX_RATES_MASK (SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) + +#define TASHA_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define TASHA_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define TASHA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) + +/* + * Timeout in milli seconds and it is the wait time for + * slim channel removal interrupt to receive. + */ +#define TASHA_SLIM_CLOSE_TIMEOUT 1000 +#define TASHA_SLIM_IRQ_OVERFLOW (1 << 0) +#define TASHA_SLIM_IRQ_UNDERFLOW (1 << 1) +#define TASHA_SLIM_IRQ_PORT_CLOSED (1 << 2) +#define TASHA_MCLK_CLK_12P288MHZ 12288000 +#define TASHA_MCLK_CLK_9P6MHZ 9600000 + +#define TASHA_SLIM_PGD_PORT_INT_TX_EN0 (TASHA_SLIM_PGD_PORT_INT_EN0 + 2) + +#define TASHA_NUM_INTERPOLATORS 9 +#define TASHA_NUM_DECIMATORS 9 + +#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE)) +#define TASHA_MAD_AUDIO_FIRMWARE_PATH "wcd9335/wcd9335_mad_audio.bin" +#define TASHA_CPE_SS_ERR_STATUS_MEM_ACCESS (1 << 0) +#define TASHA_CPE_SS_ERR_STATUS_WDOG_BITE (1 << 1) + +#define TASHA_CPE_FATAL_IRQS \ + (TASHA_CPE_SS_ERR_STATUS_WDOG_BITE | \ + TASHA_CPE_SS_ERR_STATUS_MEM_ACCESS) + +#define SLIM_BW_CLK_GEAR_9 6200000 +#define SLIM_BW_UNVOTE 0 + +#define CPE_FLL_CLK_75MHZ 75000000 +#define CPE_FLL_CLK_150MHZ 150000000 +#define WCD9335_REG_BITS 8 + +#define WCD9335_MAX_VALID_ADC_MUX 13 +#define WCD9335_INVALID_ADC_MUX 9 + +#define TASHA_DIG_CORE_REG_MIN WCD9335_CDC_ANC0_CLK_RESET_CTL +#define TASHA_DIG_CORE_REG_MAX 0xDFF + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) + +#define TASHA_ZDET_NUM_MEASUREMENTS 150 +#define TASHA_MBHC_GET_C1(c) ((c & 0xC000) >> 14) +#define TASHA_MBHC_GET_X1(x) (x & 0x3FFF) +/* z value compared in milliOhm */ +#define TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000)) +#define TASHA_MBHC_ZDET_CONST (86 * 16384) +#define TASHA_MBHC_MOISTURE_VREF V_45_MV +#define TASHA_MBHC_MOISTURE_IREF I_3P0_UA + +#define TASHA_VERSION_ENTRY_SIZE 17 + +#define WCD9335_AMIC_PWR_LEVEL_LP 0 +#define WCD9335_AMIC_PWR_LEVEL_DEFAULT 1 +#define WCD9335_AMIC_PWR_LEVEL_HP 2 +#define WCD9335_AMIC_PWR_LVL_MASK 0x60 +#define WCD9335_AMIC_PWR_LVL_SHIFT 0x5 + +#define WCD9335_DEC_PWR_LVL_MASK 0x06 +#define WCD9335_DEC_PWR_LVL_LP 0x02 +#define WCD9335_DEC_PWR_LVL_HP 0x04 +#define WCD9335_DEC_PWR_LVL_DF 0x00 +#define WCD9335_STRING_LEN 100 + +#define CALCULATE_VOUT_D(req_mv) (((req_mv - 650) * 10) / 25) + +static int cpe_debug_mode; + +#define TASHA_MAX_MICBIAS 4 +#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" +#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone" +#define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone" +#define DAPM_MICBIAS4_STANDALONE "MIC BIAS4 Standalone" + +#define DAPM_LDO_H_STANDALONE "LDO_H" +module_param(cpe_debug_mode, int, 0664); +MODULE_PARM_DESC(cpe_debug_mode, "boot cpe in debug mode"); + +#define TASHA_DIG_CORE_COLLAPSE_TIMER_MS (5 * 1000) + +#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 + +static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = { + "cdc-vdd-mic-bias", +}; + +enum { + POWER_COLLAPSE, + POWER_RESUME, +}; + +enum tasha_sido_voltage { + SIDO_VOLTAGE_SVS_MV = 950, + SIDO_VOLTAGE_NOMINAL_MV = 1100, +}; + +static enum codec_variant codec_ver; + +static int dig_core_collapse_enable = 1; +module_param(dig_core_collapse_enable, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating"); + +/* dig_core_collapse timer in seconds */ +static int dig_core_collapse_timer = (TASHA_DIG_CORE_COLLAPSE_TIMER_MS/1000); +module_param(dig_core_collapse_timer, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating"); + +/* SVS Scaling enable/disable */ +static int svs_scaling_enabled = 1; +module_param(svs_scaling_enabled, int, 0664); +MODULE_PARM_DESC(svs_scaling_enabled, "enable/disable svs scaling"); + +/* SVS buck setting */ +static int sido_buck_svs_voltage = SIDO_VOLTAGE_SVS_MV; +module_param(sido_buck_svs_voltage, int, 0664); +MODULE_PARM_DESC(sido_buck_svs_voltage, + "setting for SVS voltage for SIDO BUCK"); + +#define TASHA_TX_UNMUTE_DELAY_MS 40 + +static int tx_unmute_delay = TASHA_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + +static struct afe_param_slimbus_slave_port_cfg tasha_slimbus_slave_port_cfg = { + .minor_version = 1, + .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1, + .slave_dev_pgd_la = 0, + .slave_dev_intfdev_la = 0, + .bit_width = 16, + .data_format = 0, + .num_channels = 1 +}; + +struct tasha_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static struct afe_param_cdc_reg_page_cfg tasha_cdc_reg_page_cfg = { + .minor_version = AFE_API_VERSION_CDC_REG_PAGE_CFG, + .enable = 1, + .proc_id = AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1, +}; + +static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = { + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_MAIN_CTL_1), + HW_MAD_AUDIO_ENABLE, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_AUDIO_CTL_3), + HW_MAD_AUDIO_SLEEP_TIME, 0xF, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_AUDIO_CTL_4), + HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG), + MAD_AUDIO_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3), + MAD_AUDIO_INT_MASK_REG, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3), + MAD_AUDIO_INT_STATUS_REG, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3), + MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG), + VBAT_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3), + VBAT_INT_MASK_REG, 0x08, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3), + VBAT_INT_STATUS_REG, 0x08, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3), + VBAT_INT_CLEAR_REG, 0x08, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG), + VBAT_RELEASE_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3), + VBAT_RELEASE_INT_MASK_REG, 0x10, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3), + VBAT_RELEASE_INT_STATUS_REG, 0x10, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3), + VBAT_RELEASE_INT_CLEAR_REG, 0x10, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_WATERMARK_N, 0x1E, WCD9335_REG_BITS, 0x1 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_ENABLE_N, 0x1, WCD9335_REG_BITS, 0x1 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_WATERMARK_N, 0x1E, WCD9335_REG_BITS, 0x1 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD9335_REG_BITS, 0x1 + }, + { 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FF_GAIN_ADAPTIVE, 0x4, WCD9335_REG_BITS, 0 + }, + { 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD9335_REG_BITS, 0 + }, + { + 1, + (TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_FF_A_GAIN_CTL), + AANC_GAIN_CONTROL, 0xFF, WCD9335_REG_BITS, 0 + }, +}; + +static struct afe_param_cdc_reg_cfg_data tasha_audio_reg_cfg = { + .num_registers = ARRAY_SIZE(audio_reg_cfg), + .reg_data = audio_reg_cfg, +}; + +static struct afe_param_id_cdc_aanc_version tasha_cdc_aanc_version = { + .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION, + .aanc_hw_version = AANC_HW_BLOCK_VERSION_2, +}; + +enum { + VI_SENSE_1, + VI_SENSE_2, + AIF4_SWITCH_VALUE, + AUDIO_NOMINAL, + CPE_NOMINAL, + HPH_PA_DELAY, + SB_CLK_GEAR, + ANC_MIC_AMIC1, + ANC_MIC_AMIC2, + ANC_MIC_AMIC3, + ANC_MIC_AMIC4, + ANC_MIC_AMIC5, + ANC_MIC_AMIC6, + CLASSH_CONFIG, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_PB, + AIF2_CAP, + AIF3_PB, + AIF3_CAP, + AIF4_PB, + AIF_MIX1_PB, + AIF4_MAD_TX, + AIF4_VIFEED, + AIF5_CPE_TX, + NUM_CODEC_DAIS, +}; + +enum { + INTn_1_MIX_INP_SEL_ZERO = 0, + INTn_1_MIX_INP_SEL_DEC0, + INTn_1_MIX_INP_SEL_DEC1, + INTn_1_MIX_INP_SEL_IIR0, + INTn_1_MIX_INP_SEL_IIR1, + INTn_1_MIX_INP_SEL_RX0, + INTn_1_MIX_INP_SEL_RX1, + INTn_1_MIX_INP_SEL_RX2, + INTn_1_MIX_INP_SEL_RX3, + INTn_1_MIX_INP_SEL_RX4, + INTn_1_MIX_INP_SEL_RX5, + INTn_1_MIX_INP_SEL_RX6, + INTn_1_MIX_INP_SEL_RX7, + +}; + +#define IS_VALID_NATIVE_FIFO_PORT(inp) \ + ((inp >= INTn_1_MIX_INP_SEL_RX0) && \ + (inp <= INTn_1_MIX_INP_SEL_RX3)) + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, + INTn_2_INP_SEL_RX6, + INTn_2_INP_SEL_RX7, + INTn_2_INP_SEL_PROXIMITY, +}; + +enum { + INTERP_EAR = 0, + INTERP_HPHL, + INTERP_HPHR, + INTERP_LO1, + INTERP_LO2, + INTERP_LO3, + INTERP_LO4, + INTERP_SPKR1, + INTERP_SPKR2, +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate int_prim_sample_rate_val[] = { + {8000, 0x0}, /* 8K */ + {16000, 0x1}, /* 16K */ + {24000, -EINVAL},/* 24K */ + {32000, 0x3}, /* 32K */ + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ + {384000, 0x7}, /* 384K */ + {44100, 0x8}, /* 44.1K */ +}; + +static struct interp_sample_rate int_mix_sample_rate_val[] = { + {48000, 0x4}, /* 48K */ + {96000, 0x5}, /* 96K */ + {192000, 0x6}, /* 192K */ +}; + +static const struct wcd9xxx_ch tasha_rx_chs[TASHA_RX_MAX] = { + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER, 0), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 1, 1), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 2, 2), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 3, 3), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 4, 4), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 5, 5), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 6, 6), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 7, 7), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 8, 8), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 9, 9), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 10, 10), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 11, 11), + WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 12, 12), +}; + +static const struct wcd9xxx_ch tasha_tx_chs[TASHA_TX_MAX] = { + WCD9XXX_CH(0, 0), + WCD9XXX_CH(1, 1), + WCD9XXX_CH(2, 2), + WCD9XXX_CH(3, 3), + WCD9XXX_CH(4, 4), + WCD9XXX_CH(5, 5), + WCD9XXX_CH(6, 6), + WCD9XXX_CH(7, 7), + WCD9XXX_CH(8, 8), + WCD9XXX_CH(9, 9), + WCD9XXX_CH(10, 10), + WCD9XXX_CH(11, 11), + WCD9XXX_CH(12, 12), + WCD9XXX_CH(13, 13), + WCD9XXX_CH(14, 14), + WCD9XXX_CH(15, 15), +}; + +static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { + /* Needs to define in the same order of DAI enum definitions */ + 0, + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX), + 0, + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF5_CPE_TX), + 0, + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), +}; + +static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = { + 0, /* AIF1_PB */ + BIT(AIF2_CAP), /* AIF1_CAP */ + 0, /* AIF2_PB */ + BIT(AIF1_CAP), /* AIF2_CAP */ +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR1, + IIR_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +enum { + COMPANDER_1, /* HPH_L */ + COMPANDER_2, /* HPH_R */ + COMPANDER_3, /* LO1_DIFF */ + COMPANDER_4, /* LO2_DIFF */ + COMPANDER_5, /* LO3_SE */ + COMPANDER_6, /* LO4_SE */ + COMPANDER_7, /* SWR SPK CH1 */ + COMPANDER_8, /* SWR SPK CH2 */ + COMPANDER_MAX, +}; + +enum { + SRC_IN_HPHL, + SRC_IN_LO1, + SRC_IN_HPHR, + SRC_IN_LO2, + SRC_IN_SPKRL, + SRC_IN_LO3, + SRC_IN_SPKRR, + SRC_IN_LO4, +}; + +enum { + SPLINE_SRC0, + SPLINE_SRC1, + SPLINE_SRC2, + SPLINE_SRC3, + SPLINE_SRC_MAX, +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); + +static struct snd_soc_dai_driver tasha_dai[]; +static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv); + +static int tasha_config_compander(struct snd_soc_codec *, int, int); +static void tasha_codec_set_tx_hold(struct snd_soc_codec *, u16, bool); +static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable); + +/* Hold instance to soundwire platform device */ +struct tasha_swr_ctrl_data { + struct platform_device *swr_pdev; + struct ida swr_ida; +}; + +struct wcd_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action); +}; + +static struct wcd_mbhc_register + wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", + WCD9335_ANA_MBHC_MECH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + WCD9335_ANA_MBHC_MECH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + WCD9335_ANA_MBHC_MECH, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + WCD9335_MBHC_PLUG_DETECT_CTL, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + WCD9335_ANA_MBHC_ELECT, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + WCD9335_MBHC_PLUG_DETECT_CTL, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + WCD9335_ANA_MBHC_MECH, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + WCD9335_ANA_MBHC_MECH, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + WCD9335_ANA_MBHC_MECH, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + WCD9335_ANA_MBHC_MECH, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + WCD9335_ANA_MBHC_ELECT, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + WCD9335_ANA_MBHC_ELECT, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + WCD9335_MBHC_PLUG_DETECT_CTL, 0x0F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + WCD9335_MBHC_CTL_1, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + WCD9335_MBHC_CTL_2, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + WCD9335_HPH_OCP_CTL, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0x07, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + WCD9335_ANA_MBHC_ELECT, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + WCD9335_ANA_MBHC_RESULT_3, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + WCD9335_ANA_MICB2, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + WCD9335_HPH_CNP_WG_TIME, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + WCD9335_ANA_HPH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + WCD9335_ANA_HPH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + WCD9335_ANA_HPH, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + WCD9335_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", + WCD9335_ANA_MBHC_ZDET, 0x01, 0, 0), + /* + * MBHC FSM status register is only available in Tasha 2.0. + * So, init with 0 later once the version is known, then values + * will be updated. + */ + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", + WCD9335_MBHC_CTL_2, 0x70, 4, 0), +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = WCD9335_IRQ_MBHC_SW_DET, + .mbhc_btn_press_intr = WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, + .mbhc_btn_release_intr = WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, + .mbhc_hs_ins_intr = WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + .mbhc_hs_rem_intr = WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, + .hph_left_ocp = WCD9335_IRQ_HPH_PA_OCPL_FAULT, + .hph_right_ocp = WCD9335_IRQ_HPH_PA_OCPR_FAULT, +}; + +struct wcd_vbat { + bool is_enabled; + bool adc_config; + /* Variables to cache Vbat ADC output values */ + u16 dcp1; + u16 dcp2; +}; + +struct hpf_work { + struct tasha_priv *tasha; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +#define WCD9335_SPK_ANC_EN_DELAY_MS 350 +static int spk_anc_en_delay = WCD9335_SPK_ANC_EN_DELAY_MS; +module_param(spk_anc_en_delay, int, 0664); +MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path"); + +struct spk_anc_work { + struct tasha_priv *tasha; + struct delayed_work dwork; +}; + +struct tx_mute_work { + struct tasha_priv *tasha; + u8 decimator; + struct delayed_work dwork; +}; + +struct tasha_priv { + struct device *dev; + struct wcd9xxx *wcd9xxx; + + struct snd_soc_codec *codec; + u32 adc_count; + u32 rx_bias_count; + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 ldo_h_users; + s32 micb_ref[TASHA_MAX_MICBIAS]; + s32 pullup_ref[TASHA_MAX_MICBIAS]; + + u32 anc_slot; + bool anc_func; + + /* Vbat module */ + struct wcd_vbat vbat; + + /* cal info for codec */ + struct fw_info *fw_data; + + /*track tasha interface type*/ + u8 intf_type; + + /* num of slim ports required */ + struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS]; + + /* SoundWire data structure */ + struct tasha_swr_ctrl_data *swr_ctrl_data; + int nr; + + /*compander*/ + int comp_enabled[COMPANDER_MAX]; + + /* Maintain the status of AUX PGA */ + int aux_pga_cnt; + u8 aux_l_gain; + u8 aux_r_gain; + + bool spkr_pa_widget_on; + struct regulator *spkdrv_reg; + struct regulator *spkdrv2_reg; + + bool mbhc_started; + /* class h specific data */ + struct wcd_clsh_cdc_data clsh_d; + + struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg; + + /* + * list used to save/restore registers at start and + * end of impedance measurement + */ + struct list_head reg_save_restore; + + /* handle to cpe core */ + struct wcd_cpe_core *cpe_core; + u32 current_cpe_clk_freq; + enum tasha_sido_voltage sido_voltage; + int sido_ccl_cnt; + + u32 ana_rx_supplies; + /* Multiplication factor used for impedance detection */ + int zdet_gain_mul_fact; + + /* to track the status */ + unsigned long status_mask; + + struct work_struct tasha_add_child_devices_work; + struct wcd_swr_ctrl_platform_data swr_plat_data; + + /* Port values for Rx and Tx codec_dai */ + unsigned int rx_port_value[TASHA_RX_MAX]; + unsigned int tx_port_value; + + unsigned int vi_feed_value; + /* Tasha Interpolator Mode Select for EAR, HPH_L and HPH_R */ + u32 hph_mode; + + u16 prim_int_users[TASHA_NUM_INTERPOLATORS]; + int spl_src_users[SPLINE_SRC_MAX]; + + struct wcd9xxx_resmgr_v2 *resmgr; + struct delayed_work power_gate_work; + struct mutex power_lock; + struct mutex sido_lock; + + /* mbhc module */ + struct wcd_mbhc mbhc; + struct blocking_notifier_head notifier; + struct mutex micb_lock; + + struct clk *wcd_ext_clk; + struct clk *wcd_native_clk; + struct mutex swr_read_lock; + struct mutex swr_write_lock; + struct mutex swr_clk_lock; + int swr_clk_users; + int native_clk_users; + int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high); + + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + int power_active_ref; + + struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX]; + + int (*machine_codec_event_cb)(struct snd_soc_codec *codec, + enum wcd9335_codec_event); + int spkr_gain_offset; + int spkr_mode; + int ear_spkr_gain; + struct hpf_work tx_hpf_work[TASHA_NUM_DECIMATORS]; + struct tx_mute_work tx_mute_dwork[TASHA_NUM_DECIMATORS]; + struct spk_anc_work spk_anc_dwork; + struct mutex codec_mutex; + int hph_l_gain; + int hph_r_gain; + int rx_7_count; + int rx_8_count; + bool clk_mode; + bool clk_internal; + + /* Lock to protect mclk enablement */ + struct mutex mclk_lock; +}; + +static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec, + bool vote); + +static const struct tasha_reg_mask_val tasha_spkr_default[] = { + {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50}, +}; + +static const struct tasha_reg_mask_val tasha_spkr_mode1[] = { + {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x00}, + {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x00}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x00}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x00}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44}, +}; + +/** + * tasha_set_spkr_gain_offset - offset the speaker path + * gain with the given offset value. + * + * @codec: codec instance + * @offset: Indicates speaker path gain offset value. + * + * Returns 0 on success or -EINVAL on error. + */ +int tasha_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (!priv) + return -EINVAL; + + priv->spkr_gain_offset = offset; + return 0; +} +EXPORT_SYMBOL(tasha_set_spkr_gain_offset); + +/** + * tasha_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @codec: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int tasha_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + int i; + const struct tasha_reg_mask_val *regs; + int size; + + if (!priv) + return -EINVAL; + + switch (mode) { + case SPKR_MODE_1: + regs = tasha_spkr_mode1; + size = ARRAY_SIZE(tasha_spkr_mode1); + break; + default: + regs = tasha_spkr_default; + size = ARRAY_SIZE(tasha_spkr_default); + break; + } + + priv->spkr_mode = mode; + for (i = 0; i < size; i++) + snd_soc_update_bits(codec, regs[i].reg, + regs[i].mask, regs[i].val); + return 0; +} +EXPORT_SYMBOL(tasha_set_spkr_mode); + +static void tasha_enable_sido_buck(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + snd_soc_update_bits(codec, WCD9335_ANA_RCO, 0x80, 0x80); + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x02, 0x02); + /* 100us sleep needed after IREF settings */ + usleep_range(100, 110); + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x04, 0x04); + /* 100us sleep needed after VREF settings */ + usleep_range(100, 110); + tasha->resmgr->sido_input_src = SIDO_SOURCE_RCO_BG; +} + +static void tasha_cdc_sido_ccl_enable(struct tasha_priv *tasha, bool ccl_flag) +{ + struct snd_soc_codec *codec = tasha->codec; + + if (!codec) + return; + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) { + dev_dbg(codec->dev, "%s: tasha version < 2p0, return\n", + __func__); + return; + } + dev_dbg(codec->dev, "%s: sido_ccl_cnt=%d, ccl_flag:%d\n", + __func__, tasha->sido_ccl_cnt, ccl_flag); + if (ccl_flag) { + if (++tasha->sido_ccl_cnt == 1) + snd_soc_update_bits(codec, + WCD9335_SIDO_SIDO_CCL_10, 0xFF, 0x6E); + } else { + if (tasha->sido_ccl_cnt == 0) { + dev_dbg(codec->dev, "%s: sido_ccl already disabled\n", + __func__); + return; + } + if (--tasha->sido_ccl_cnt == 0) + snd_soc_update_bits(codec, + WCD9335_SIDO_SIDO_CCL_10, 0xFF, 0x02); + } +} + +static bool tasha_cdc_is_svs_enabled(struct tasha_priv *tasha) +{ + if (TASHA_IS_2_0(tasha->wcd9xxx) && + svs_scaling_enabled) + return true; + + return false; +} + +static int tasha_cdc_req_mclk_enable(struct tasha_priv *tasha, + bool enable) +{ + int ret = 0; + + mutex_lock(&tasha->mclk_lock); + if (enable) { + tasha_cdc_sido_ccl_enable(tasha, true); + ret = clk_prepare_enable(tasha->wcd_ext_clk); + if (ret) { + dev_err(tasha->dev, "%s: ext clk enable failed\n", + __func__); + goto unlock_mutex; + } + /* get BG */ + wcd_resmgr_enable_master_bias(tasha->resmgr); + /* get MCLK */ + wcd_resmgr_enable_clk_block(tasha->resmgr, WCD_CLK_MCLK); + } else { + /* put MCLK */ + wcd_resmgr_disable_clk_block(tasha->resmgr, WCD_CLK_MCLK); + /* put BG */ + wcd_resmgr_disable_master_bias(tasha->resmgr); + clk_disable_unprepare(tasha->wcd_ext_clk); + tasha_cdc_sido_ccl_enable(tasha, false); + } +unlock_mutex: + mutex_unlock(&tasha->mclk_lock); + return ret; +} + +static int tasha_cdc_check_sido_value(enum tasha_sido_voltage req_mv) +{ + if ((req_mv != SIDO_VOLTAGE_SVS_MV) && + (req_mv != SIDO_VOLTAGE_NOMINAL_MV)) + return -EINVAL; + + return 0; +} + +static void tasha_codec_apply_sido_voltage( + struct tasha_priv *tasha, + enum tasha_sido_voltage req_mv) +{ + u32 vout_d_val; + struct snd_soc_codec *codec = tasha->codec; + int ret; + + if (!codec) + return; + + if (!tasha_cdc_is_svs_enabled(tasha)) + return; + + if ((sido_buck_svs_voltage != SIDO_VOLTAGE_SVS_MV) && + (sido_buck_svs_voltage != SIDO_VOLTAGE_NOMINAL_MV)) + sido_buck_svs_voltage = SIDO_VOLTAGE_SVS_MV; + + ret = tasha_cdc_check_sido_value(req_mv); + if (ret < 0) { + dev_dbg(codec->dev, "%s: requested mv=%d not in range\n", + __func__, req_mv); + return; + } + if (req_mv == tasha->sido_voltage) { + dev_dbg(codec->dev, "%s: Already at requested mv=%d\n", + __func__, req_mv); + return; + } + if (req_mv == sido_buck_svs_voltage) { + if (test_bit(AUDIO_NOMINAL, &tasha->status_mask) || + test_bit(CPE_NOMINAL, &tasha->status_mask)) { + dev_dbg(codec->dev, + "%s: nominal client running, status_mask=%lu\n", + __func__, tasha->status_mask); + return; + } + } + /* compute the vout_d step value */ + vout_d_val = CALCULATE_VOUT_D(req_mv); + snd_soc_write(codec, WCD9335_ANA_BUCK_VOUT_D, vout_d_val & 0xFF); + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x80, 0x80); + + /* 1 msec sleep required after SIDO Vout_D voltage change */ + usleep_range(1000, 1100); + tasha->sido_voltage = req_mv; + dev_dbg(codec->dev, + "%s: updated SIDO buck Vout_D to %d, vout_d step = %u\n", + __func__, tasha->sido_voltage, vout_d_val); + + snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, + 0x80, 0x00); +} + +static int tasha_codec_update_sido_voltage( + struct tasha_priv *tasha, + enum tasha_sido_voltage req_mv) +{ + int ret = 0; + + if (!tasha_cdc_is_svs_enabled(tasha)) + return ret; + + mutex_lock(&tasha->sido_lock); + /* enable mclk before setting SIDO voltage */ + ret = tasha_cdc_req_mclk_enable(tasha, true); + if (ret) { + dev_err(tasha->dev, "%s: ext clk enable failed\n", + __func__); + goto err; + } + tasha_codec_apply_sido_voltage(tasha, req_mv); + tasha_cdc_req_mclk_enable(tasha, false); + +err: + mutex_unlock(&tasha->sido_lock); + return ret; +} + +int tasha_enable_efuse_sensing(struct snd_soc_codec *codec) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + tasha_cdc_mclk_enable(codec, true, false); + + if (!TASHA_IS_2_0(priv->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, + 0x1E, 0x02); + snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, + 0x01, 0x01); + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + if (!(snd_soc_read(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS) & 0x01)) + WARN(1, "%s: Efuse sense is not complete\n", __func__); + + if (TASHA_IS_2_0(priv->wcd9xxx)) { + if (!(snd_soc_read(codec, + WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0) & 0x40)) + snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, + 0x04, 0x00); + tasha_enable_sido_buck(codec); + } + + tasha_cdc_mclk_enable(codec, false, false); + + return 0; +} +EXPORT_SYMBOL(tasha_enable_efuse_sensing); + +void *tasha_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (config_type) { + case AFE_SLIMBUS_SLAVE_CONFIG: + return &priv->slimbus_slave_cfg; + case AFE_CDC_REGISTERS_CONFIG: + return &tasha_audio_reg_cfg; + case AFE_SLIMBUS_SLAVE_PORT_CONFIG: + return &tasha_slimbus_slave_port_cfg; + case AFE_AANC_VERSION: + return &tasha_cdc_aanc_version; + case AFE_CLIP_BANK_SEL: + return NULL; + case AFE_CDC_CLIP_REGISTERS_CONFIG: + return NULL; + case AFE_CDC_REGISTER_PAGE_CONFIG: + return &tasha_cdc_reg_page_cfg; + default: + dev_err(codec->dev, "%s: Unknown config_type 0x%x\n", + __func__, config_type); + return NULL; + } +} +EXPORT_SYMBOL(tasha_get_afe_config); + +/* + * tasha_event_register: Registers a machine driver callback + * function with codec private data for post ADSP sub-system + * restart (SSR). This callback function will be called from + * codec driver once codec comes out of reset after ADSP SSR. + * + * @machine_event_cb: callback function from machine driver + * @codec: Codec instance + * + * Return: none + */ +void tasha_event_register( + int (*machine_event_cb)(struct snd_soc_codec *codec, + enum wcd9335_codec_event), + struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (tasha) + tasha->machine_codec_event_cb = machine_event_cb; + else + dev_dbg(codec->dev, "%s: Invalid tasha_priv data\n", __func__); +} +EXPORT_SYMBOL(tasha_event_register); + +static int tasha_mbhc_request_irq(struct snd_soc_codec *codec, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + return wcd9xxx_request_irq(core_res, irq, handler, name, data); +} + +static void tasha_mbhc_irq_control(struct snd_soc_codec *codec, + int irq, bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + if (enable) + wcd9xxx_enable_irq(core_res, irq); + else + wcd9xxx_disable_irq(core_res, irq); +} + +static int tasha_mbhc_free_irq(struct snd_soc_codec *codec, + int irq, void *data) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, irq, data); + return 0; +} + +static void tasha_mbhc_clk_setup(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1, + 0x80, 0x80); + else + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1, + 0x80, 0x00); +} + +static int tasha_mbhc_btn_to_num(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, WCD9335_ANA_MBHC_RESULT_3) & 0x7; +} + +static void tasha_mbhc_mbhc_bias_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_ELECT, + 0x01, 0x01); + else + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_ELECT, + 0x01, 0x00); +} + +static void tasha_mbhc_program_btn_thr(struct snd_soc_codec *codec, + s16 *btn_low, s16 *btn_high, + int num_btn, bool is_micbias) +{ + int i; + int vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(codec->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + /* + * Tasha just needs one set of thresholds for button detection + * due to micbias voltage ramp to pullup upon button press. So + * btn_low and is_micbias are ignored and always program button + * thresholds using btn_high. + */ + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN0 + i, + 0xFC, vth << 2); + dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool tasha_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + if (lock) + return wcd9xxx_lock_sleep(core_res); + else { + wcd9xxx_unlock_sleep(core_res); + return 0; + } +} + +static int tasha_mbhc_register_notifier(struct wcd_mbhc *mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (enable) + return blocking_notifier_chain_register(&tasha->notifier, + nblock); + else + return blocking_notifier_chain_unregister(&tasha->notifier, + nblock); +} + +static bool tasha_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_2) { + val = (snd_soc_read(mbhc->codec, WCD9335_ANA_MICB2) >> 6); + if (val == 0x01) + return true; + } + return false; +} + +static bool tasha_mbhc_hph_pa_on_status(struct snd_soc_codec *codec) +{ + return (snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) ? true : false; +} + +static void tasha_mbhc_hph_l_pull_up_control(struct snd_soc_codec *codec, + enum mbhc_hs_pullup_iref pull_up_cur) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (!tasha) + return; + + /* Default pull up current to 2uA */ + if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA || + pull_up_cur == I_DEFAULT) + pull_up_cur = I_2P0_UA; + + dev_dbg(codec->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + if (TASHA_IS_2_0(tasha->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL, + 0xC0, pull_up_cur << 6); + else + snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL, + 0xC0, 0x40); +} + +static int tasha_enable_ext_mb_source(struct wcd_mbhc *mbhc, + bool turn_on) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + struct on_demand_supply *supply; + + if (!tasha) + return -EINVAL; + + supply = &tasha->on_demand_list[ON_DEMAND_MICBIAS]; + if (!supply->supply) { + dev_dbg(codec->dev, "%s: warning supply not present ond for %s\n", + __func__, "onDemand Micbias"); + return ret; + } + + dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on, + supply->ondemand_supply_count); + + if (turn_on) { + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + supply->ondemand_supply_count++; + } else { + if (supply->ondemand_supply_count > 0) + supply->ondemand_supply_count--; + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_disable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + } + + if (ret) + dev_err(codec->dev, "%s: Failed to %s external micbias source\n", + __func__, turn_on ? "enable" : "disabled"); + else + dev_dbg(codec->dev, "%s: %s external micbias source\n", + __func__, turn_on ? "Enabled" : "Disabled"); + + return ret; +} + +static int tasha_micbias_control(struct snd_soc_codec *codec, + int micb_num, + int req, bool is_dapm) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int micb_index = micb_num - 1; + u16 micb_reg; + int pre_off_event = 0, post_off_event = 0; + int post_on_event = 0, post_dapm_off = 0; + int post_dapm_on = 0; + + if ((micb_index < 0) || (micb_index > TASHA_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD9335_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD9335_ANA_MICB2; + pre_off_event = WCD_EVENT_PRE_MICBIAS_2_OFF; + post_off_event = WCD_EVENT_POST_MICBIAS_2_OFF; + post_on_event = WCD_EVENT_POST_MICBIAS_2_ON; + post_dapm_on = WCD_EVENT_POST_DAPM_MICBIAS_2_ON; + post_dapm_off = WCD_EVENT_POST_DAPM_MICBIAS_2_OFF; + break; + case MIC_BIAS_3: + micb_reg = WCD9335_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD9335_ANA_MICB4; + break; + default: + dev_err(codec->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + mutex_lock(&tasha->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: + tasha->pullup_ref[micb_index]++; + if ((tasha->pullup_ref[micb_index] == 1) && + (tasha->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + break; + case MICB_PULLUP_DISABLE: + if (tasha->pullup_ref[micb_index] > 0) + tasha->pullup_ref[micb_index]--; + if ((tasha->pullup_ref[micb_index] == 0) && + (tasha->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + break; + case MICB_ENABLE: + tasha->micb_ref[micb_index]++; + if (tasha->micb_ref[micb_index] == 1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + if (post_on_event) + blocking_notifier_call_chain(&tasha->notifier, + post_on_event, &tasha->mbhc); + } + if (is_dapm && post_dapm_on) + blocking_notifier_call_chain(&tasha->notifier, + post_dapm_on, &tasha->mbhc); + break; + case MICB_DISABLE: + if (tasha->micb_ref[micb_index] > 0) + tasha->micb_ref[micb_index]--; + if ((tasha->micb_ref[micb_index] == 0) && + (tasha->pullup_ref[micb_index] > 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + else if ((tasha->micb_ref[micb_index] == 0) && + (tasha->pullup_ref[micb_index] == 0)) { + if (pre_off_event) + blocking_notifier_call_chain(&tasha->notifier, + pre_off_event, &tasha->mbhc); + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + if (post_off_event) + blocking_notifier_call_chain(&tasha->notifier, + post_off_event, &tasha->mbhc); + } + if (is_dapm && post_dapm_off) + blocking_notifier_call_chain(&tasha->notifier, + post_dapm_off, &tasha->mbhc); + break; + }; + + dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n", + __func__, micb_num, tasha->micb_ref[micb_index], + tasha->pullup_ref[micb_index]); + + mutex_unlock(&tasha->micb_lock); + + return 0; +} + +static int tasha_mbhc_request_micbias(struct snd_soc_codec *codec, + int micb_num, int req) +{ + int ret; + + /* + * If micbias is requested, make sure that there + * is vote to enable mclk + */ + if (req == MICB_ENABLE) + tasha_cdc_mclk_enable(codec, true, false); + + ret = tasha_micbias_control(codec, micb_num, req, false); + + /* + * Release vote for mclk while requesting for + * micbias disable + */ + if (req == MICB_DISABLE) + tasha_cdc_mclk_enable(codec, false, false); + + return ret; +} + +static void tasha_mbhc_micb_ramp_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x1C, 0x0C); + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP, + 0x1C, 0x00); + } +} + +static struct firmware_cal *tasha_get_hwdep_fw_cal(struct wcd_mbhc *mbhc, + enum wcd_cal_type type) +{ + struct tasha_priv *tasha; + struct firmware_cal *hwdep_cal; + struct snd_soc_codec *codec = mbhc->codec; + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + tasha = snd_soc_codec_get_drvdata(codec); + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, type); + if (!hwdep_cal) + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + + return hwdep_cal; +} + +static int tasha_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, + int micb_num) +{ + int cur_vout_ctl, req_vout_ctl; + int micb_reg, micb_val, micb_en; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD9335_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD9335_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD9335_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD9335_ANA_MICB4; + break; + default: + return -EINVAL; + } + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_val = snd_soc_read(codec, micb_reg); + micb_en = (micb_val & 0xC0) >> 6; + cur_vout_ctl = micb_val & 0x3F; + + req_vout_ctl = wcd9335_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) + return -EINVAL; + if (cur_vout_ctl == req_vout_ctl) + return 0; + + dev_dbg(codec->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == 0x1) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + + snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl); + + if (micb_en == 0x1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } + + return 0; +} + +static int tasha_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec, + int micb_num, bool req_en) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + int rc, micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (pdata->micbias.micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : pdata->micbias.micb2_mv; + + mutex_lock(&tasha->micb_lock); + rc = tasha_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_2); + mutex_unlock(&tasha->micb_lock); + + return rc; +} + +static inline void tasha_mbhc_get_result_params(struct wcd9xxx *wcd9xxx, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + int i; + int val, val1; + s16 c1; + s32 x1, d1; + int32_t denom; + int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd9xxx->regmap, WCD9335_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < TASHA_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd9xxx->regmap, WCD9335_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd9xxx->regmap, WCD9335_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd9xxx->regmap, WCD9335_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = TASHA_MBHC_GET_X1(val); + c1 = TASHA_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if ((c1 < 2) && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_dbg(wcd9xxx->dev, + "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (TASHA_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = TASHA_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(wcd9xxx->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + regmap_bulk_read(wcd9xxx->regmap, + WCD9335_ANA_MBHC_RESULT_1, (u8 *)&val, 2); + x1 = TASHA_MBHC_GET_X1(val); + i++; + if (i == TASHA_ZDET_NUM_MEASUREMENTS) + break; + } +} + +/* + * tasha_mbhc_zdet_gpio_ctrl: Register callback function for + * controlling the switch on hifi amps. Default switch state + * will put a 51ohm load in parallel to the hph load. So, + * impedance detection function will pull the gpio high + * to make the switch open. + * + * @zdet_gpio_cb: callback function from machine driver + * @codec: Codec instance + * + * Return: none + */ +void tasha_mbhc_zdet_gpio_ctrl( + int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high), + struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha->zdet_gpio_cb = zdet_gpio_cb; +} +EXPORT_SYMBOL(tasha_mbhc_zdet_gpio_ctrl); + +static void tasha_mbhc_zdet_ramp(struct snd_soc_codec *codec, + struct tasha_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int32_t zdet = 0; + + snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_ANA_CTL, 0x70, + zdet_param->ldo_ctl << 4); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN5, 0xFC, + zdet_param->btn5); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN6, 0xFC, + zdet_param->btn6); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN7, 0xFC, + zdet_param->btn7); + snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_ANA_CTL, 0x0F, + zdet_param->noff); + snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x0F, + zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x80, 0x80); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + tasha_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x40, 0x40); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + tasha_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static inline void tasha_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (TASHA_ZDET_VAL_400/1000)) + q1 = snd_soc_read(codec, + WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r)); + else + q1 = snd_soc_read(codec, + WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void tasha_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + bool is_change = false; + struct tasha_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct tasha_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + if (!TASHA_IS_2_0(wcd9xxx)) { + dev_dbg(codec->dev, "%s: Z-det is not supported for this codec version\n", + __func__); + *zl = 0; + *zr = 0; + return; + } + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + if (tasha->zdet_gpio_cb) + is_change = tasha->zdet_gpio_cb(codec, true); + + reg0 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN5); + reg1 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN6); + reg2 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN7); + reg3 = snd_soc_read(codec, WCD9335_MBHC_CTL_1); + reg4 = snd_soc_read(codec, WCD9335_MBHC_ZDET_ANA_CTL); + + if (snd_soc_read(codec, WCD9335_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x80, 0x00); + + /* Enable AZ */ + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1, 0x0C, 0x04); + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x01, 0x00); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + + if (!TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* second ramp for left ch */ + if (z1L < TASHA_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > TASHA_ZDET_VAL_400) && (z1L <= TASHA_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > TASHA_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == TASHA_ZDET_FLOATING_IMPEDANCE) || + (z1L > TASHA_ZDET_VAL_100K)) { + *zl = TASHA_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + tasha_wcd_mbhc_qfuse_cal(codec, zl, 0); + } + dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* start of right impedance ramp and calculation */ + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + if (TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > TASHA_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != TASHA_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* second ramp for right ch */ + if (z1R < TASHA_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > TASHA_ZDET_VAL_400) && + (z1R <= TASHA_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > TASHA_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + } +right_ch_impedance: + if ((z1R == TASHA_ZDET_FLOATING_IMPEDANCE) || + (z1R > TASHA_ZDET_VAL_100K)) { + *zr = TASHA_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + tasha_wcd_mbhc_qfuse_cal(codec, zr, 1); + } + dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* mono/stereo detection */ + if ((*zl == TASHA_ZDET_FLOATING_IMPEDANCE) && + (*zr == TASHA_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(codec->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == TASHA_ZDET_FLOATING_IMPEDANCE) || + (*zr == TASHA_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + dev_dbg(codec->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + goto zdet_complete; + } + snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, 0x02, 0x02); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x01); + if (*zl < (TASHA_ZDET_VAL_32/1000)) + tasha_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1); + else + tasha_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, 0x02, 0x00); + z1Ls /= 1000; + tasha_wcd_mbhc_qfuse_cal(codec, &z1Ls, 0); + /* parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls); + z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl)); + if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) { + dev_dbg(codec->dev, "%s: stereo plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + dev_dbg(codec->dev, "%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } + +zdet_complete: + snd_soc_write(codec, WCD9335_ANA_MBHC_BTN5, reg0); + snd_soc_write(codec, WCD9335_ANA_MBHC_BTN6, reg1); + snd_soc_write(codec, WCD9335_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_write(codec, WCD9335_MBHC_ZDET_ANA_CTL, reg4); + snd_soc_write(codec, WCD9335_MBHC_CTL_1, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd9xxx->regmap, + WCD9335_ANA_MBHC_ELECT, 0x80, 0x80); + if (tasha->zdet_gpio_cb && is_change) + tasha->zdet_gpio_cb(codec, false); +} + +static void tasha_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x40, 0x40); + } else { + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH, + 0x02, 0x00); + } +} + +static void tasha_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (enable) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x40, 0x40); + if (TASHA_IS_2_0(tasha->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x10, 0x10); + } else { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x40, 0x00); + if (TASHA_IS_2_0(tasha->wcd9xxx)) + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, + 0x10, 0x00); + } +} + +static void tasha_mbhc_moisture_config(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + if (mbhc->moist_vref == V_OFF) + return; + + /* Donot enable moisture detection if jack type is NC */ + if (!mbhc->hphl_swh) { + dev_dbg(codec->dev, "%s: disable moisture detection for NC\n", + __func__); + return; + } + + snd_soc_update_bits(codec, WCD9335_MBHC_CTL_2, + 0x0C, mbhc->moist_vref << 2); + tasha_mbhc_hph_l_pull_up_control(codec, mbhc->moist_iref); +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .request_irq = tasha_mbhc_request_irq, + .irq_control = tasha_mbhc_irq_control, + .free_irq = tasha_mbhc_free_irq, + .clk_setup = tasha_mbhc_clk_setup, + .map_btn_code_to_num = tasha_mbhc_btn_to_num, + .enable_mb_source = tasha_enable_ext_mb_source, + .mbhc_bias = tasha_mbhc_mbhc_bias_control, + .set_btn_thr = tasha_mbhc_program_btn_thr, + .lock_sleep = tasha_mbhc_lock_sleep, + .register_notifier = tasha_mbhc_register_notifier, + .micbias_enable_status = tasha_mbhc_micb_en_status, + .hph_pa_on_status = tasha_mbhc_hph_pa_on_status, + .hph_pull_up_control = tasha_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = tasha_mbhc_request_micbias, + .mbhc_micb_ramp_control = tasha_mbhc_micb_ramp_control, + .get_hwdep_fw_cal = tasha_get_hwdep_fw_cal, + .mbhc_micb_ctrl_thr_mic = tasha_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = tasha_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = tasha_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = tasha_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = tasha_mbhc_moisture_config, +}; + +static int tasha_get_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->anc_slot; + return 0; +} + +static int tasha_put_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha->anc_slot = ucontrol->value.integer.value[0]; + return 0; +} + +static int tasha_get_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = (tasha->anc_func == true ? 1 : 0); + return 0; +} + +static int tasha_put_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + mutex_lock(&tasha->codec_mutex); + tasha->anc_func = (!ucontrol->value.integer.value[0] ? false : true); + + dev_dbg(codec->dev, "%s: anc_func %x", __func__, tasha->anc_func); + + if (tasha->anc_func == true) { + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT2 PA"); + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT2"); + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT1 PA"); + snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT1"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT2"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT2 PA"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT1"); + snd_soc_dapm_disable_pin(dapm, "LINEOUT1 PA"); + snd_soc_dapm_disable_pin(dapm, "HPHR"); + snd_soc_dapm_disable_pin(dapm, "HPHL"); + snd_soc_dapm_disable_pin(dapm, "HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "EAR PA"); + snd_soc_dapm_disable_pin(dapm, "EAR"); + } else { + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT2"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT2 PA"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT1"); + snd_soc_dapm_enable_pin(dapm, "LINEOUT1 PA"); + snd_soc_dapm_enable_pin(dapm, "HPHR"); + snd_soc_dapm_enable_pin(dapm, "HPHL"); + snd_soc_dapm_enable_pin(dapm, "HPHR PA"); + snd_soc_dapm_enable_pin(dapm, "HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "EAR PA"); + snd_soc_dapm_enable_pin(dapm, "EAR"); + } + mutex_unlock(&tasha->codec_mutex); + snd_soc_dapm_sync(dapm); + return 0; +} + +static int tasha_get_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = tasha->clk_mode; + dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode); + + return 0; +} + +static int tasha_put_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha->clk_mode = ucontrol->value.enumerated.item[0]; + dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode); + + return 0; +} + +static int tasha_get_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + /* IIR filter band registers are at integer multiples of 16 */ + u16 iir_reg = WCD9335_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int tasha_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(&priv->mbhc, &zl, &zr); + dev_dbg(codec->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + tasha_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + tasha_hph_impedance_get, NULL), +}; + +static int tasha_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + struct wcd_mbhc *mbhc; + + if (!priv) { + dev_dbg(codec->dev, "%s: wcd9335 private data is NULL\n", + __func__); + return 0; + } + + mbhc = &priv->mbhc; + if (!mbhc) { + dev_dbg(codec->dev, "%s: mbhc not initialized\n", __func__); + return 0; + } + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + tasha_get_hph_type, NULL), +}; + +static int tasha_vi_feed_mixer_get(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_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha_p->vi_feed_value; + + return 0; +} + +static int tasha_vi_feed_mixer_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 snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = tasha_p->wcd9xxx; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n", + __func__, enable, port_id, dai_id); + + tasha_p->vi_feed_value = ucontrol->value.integer.value[0]; + + mutex_lock(&tasha_p->codec_mutex); + if (enable) { + if (port_id == TASHA_TX14 && !test_bit(VI_SENSE_1, + &tasha_p->status_mask)) { + list_add_tail(&core->tx_chs[TASHA_TX14].list, + &tasha_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_1, &tasha_p->status_mask); + } + if (port_id == TASHA_TX15 && !test_bit(VI_SENSE_2, + &tasha_p->status_mask)) { + list_add_tail(&core->tx_chs[TASHA_TX15].list, + &tasha_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_2, &tasha_p->status_mask); + } + } else { + if (port_id == TASHA_TX14 && test_bit(VI_SENSE_1, + &tasha_p->status_mask)) { + list_del_init(&core->tx_chs[TASHA_TX14].list); + clear_bit(VI_SENSE_1, &tasha_p->status_mask); + } + if (port_id == TASHA_TX15 && test_bit(VI_SENSE_2, + &tasha_p->status_mask)) { + list_del_init(&core->tx_chs[TASHA_TX15].list); + clear_bit(VI_SENSE_2, &tasha_p->status_mask); + } + } + mutex_unlock(&tasha_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +/* virtual port entries */ +static int slim_tx_mixer_get(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_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha_p->tx_port_value; + return 0; +} + +static int slim_tx_mixer_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 snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + u32 vtable; + + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, + widget->name, ucontrol->id.name, tasha_p->tx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tasha_p->codec_mutex); + + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + if (dai_id != AIF1_CAP) { + dev_err(codec->dev, "%s: invalid AIF for I2C mode\n", + __func__); + mutex_unlock(&tasha_p->codec_mutex); + return -EINVAL; + } + vtable = vport_slim_check_table[dai_id]; + } else { + if (dai_id >= ARRAY_SIZE(vport_i2s_check_table)) { + dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n", + __func__, dai_id); + return -EINVAL; + } + vtable = vport_i2s_check_table[dai_id]; + } + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && !(tasha_p->tx_port_value & 1 << port_id)) { + + if (wcd9xxx_tx_vport_validation(vtable, port_id, + tasha_p->dai, NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n", + __func__, port_id); + mutex_unlock(&tasha_p->codec_mutex); + return 0; + } + tasha_p->tx_port_value |= 1 << port_id; + list_add_tail(&core->tx_chs[port_id].list, + &tasha_p->dai[dai_id].wcd9xxx_ch_list + ); + } else if (!enable && (tasha_p->tx_port_value & + 1 << port_id)) { + tasha_p->tx_port_value &= ~(1 << port_id); + list_del_init(&core->tx_chs[port_id].list); + } else { + if (enable) + dev_dbg(codec->dev, "%s: TX%u port is used by\n" + "this virtual port\n", + __func__, port_id); + else + dev_dbg(codec->dev, "%s: TX%u port is not used by\n" + "this virtual port\n", + __func__, port_id); + /* avoid update power function */ + mutex_unlock(&tasha_p->codec_mutex); + return 0; + } + break; + case AIF4_MAD_TX: + case AIF5_CPE_TX: + break; + default: + pr_err("Unknown AIF %d\n", dai_id); + mutex_unlock(&tasha_p->codec_mutex); + return -EINVAL; + } + pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__, + widget->name, widget->sname, tasha_p->tx_port_value, + widget->shift); + + mutex_unlock(&tasha_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int slim_rx_mux_get(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_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = + tasha_p->rx_port_value[widget->shift]; + return 0; +} + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB", "AIF_MIX1_PB" +}; + +static int slim_rx_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 snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + unsigned int rx_port_value; + u32 port_id = widget->shift; + + tasha_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + rx_port_value = tasha_p->rx_port_value[port_id]; + + pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__, + widget->name, ucontrol->id.name, rx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tasha_p->codec_mutex); + + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + if (rx_port_value > 2) { + dev_err(codec->dev, "%s: invalid AIF for I2C mode\n", + __func__); + goto err; + } + } + /* value need to match the Virtual port and AIF number */ + switch (rx_port_value) { + case 0: + list_del_init(&core->rx_chs[port_id].list); + break; + case 1: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF1_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF1_PB].wcd9xxx_ch_list); + break; + case 2: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF2_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF2_PB].wcd9xxx_ch_list); + break; + case 3: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF3_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF3_PB].wcd9xxx_ch_list); + break; + case 4: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF4_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF4_PB].wcd9xxx_ch_list); + break; + case 5: + if (wcd9xxx_rx_vport_validation(port_id + + TASHA_RX_PORT_START_NUMBER, + &tasha_p->dai[AIF_MIX1_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tasha_p->dai[AIF_MIX1_PB].wcd9xxx_ch_list); + break; + default: + pr_err("Unknown AIF %d\n", rx_port_value); + goto err; + } +rtn: + mutex_unlock(&tasha_p->codec_mutex); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + + return 0; +err: + mutex_unlock(&tasha_p->codec_mutex); + return -EINVAL; +} + +static const struct soc_enum slim_rx_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text); + +static const struct snd_kcontrol_new slim_rx_mux[TASHA_RX_MAX] = { + SOC_DAPM_ENUM_EXT("SLIM RX0 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), + SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum, + slim_rx_mux_get, slim_rx_mux_put), +}; + +static const struct snd_kcontrol_new aif4_vi_mixer[] = { + SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, TASHA_TX14, 1, 0, + tasha_vi_feed_mixer_get, tasha_vi_feed_mixer_put), + SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, TASHA_TX15, 1, 0, + tasha_vi_feed_mixer_get, tasha_vi_feed_mixer_put), +}; + +static const struct snd_kcontrol_new aif1_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif4_mad_mixer[] = { + SOC_SINGLE_EXT("SLIM TX12", SND_SOC_NOPM, TASHA_TX12, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, 0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + +}; + +static const struct snd_kcontrol_new rx_int1_spline_mix_switch[] = { + SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int2_spline_mix_switch[] = { + SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int3_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int4_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int5_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO3 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int6_spline_mix_switch[] = { + SOC_DAPM_SINGLE("LO4 Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int7_spline_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRL Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int8_spline_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRR Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int5_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("LO3 VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int6_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("LO4 VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int7_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRL VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new rx_int8_vbat_mix_switch[] = { + SOC_DAPM_SINGLE("SPKRR VBAT Enable", SND_SOC_NOPM, 0, 1, 0) +}; + +static const struct snd_kcontrol_new cpe_in_mix_switch[] = { + SOC_DAPM_SINGLE("MAD_BYPASS", SND_SOC_NOPM, 0, 1, 0) +}; + + + +static int tasha_put_iir_enable_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + bool iir_band_en_status; + int value = ucontrol->value.integer.value[0]; + u16 iir_reg = WCD9335_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, iir_reg, (1 << band_idx), + (value << band_idx)); + + iir_band_en_status = ((snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0); + pr_debug("%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, iir_band_en_status); + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx)); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 8); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 16); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) & 0x3F) << 24); + + return value; +} + +static int tasha_get_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 24) & 0x3F); +} + +static void tasha_codec_enable_int_port(struct wcd9xxx_codec_dai_data *dai, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_ch *ch; + int port_num = 0; + unsigned short reg = 0; + u8 val = 0; + struct tasha_priv *tasha_p; + + if (!dai || !codec) { + pr_err("%s: Invalid params\n", __func__); + return; + } + + tasha_p = snd_soc_codec_get_drvdata(codec); + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + if (ch->port >= TASHA_RX_PORT_START_NUMBER) { + port_num = ch->port - TASHA_RX_PORT_START_NUMBER; + reg = TASHA_SLIM_PGD_PORT_INT_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tasha_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write( + tasha_p->wcd9xxx, reg, val); + val = wcd9xxx_interface_reg_read( + tasha_p->wcd9xxx, reg); + } + } else { + port_num = ch->port; + reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tasha_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write(tasha_p->wcd9xxx, + reg, val); + val = wcd9xxx_interface_reg_read( + tasha_p->wcd9xxx, reg); + } + } + } +} + +static int tasha_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai, + bool up) +{ + int ret = 0; + struct wcd9xxx_ch *ch; + + if (up) { + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + ret = wcd9xxx_get_slave_port(ch->ch_num); + if (ret < 0) { + pr_err("%s: Invalid slave port ID: %d\n", + __func__, ret); + ret = -EINVAL; + } else { + set_bit(ret, &dai->ch_mask); + } + } + } else { + ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0), + msecs_to_jiffies( + TASHA_SLIM_CLOSE_TIMEOUT)); + if (!ret) { + pr_err("%s: Slim close tx/rx wait timeout, ch_mask:0x%lx\n", + __func__, dai->ch_mask); + ret = -ETIMEDOUT; + } else { + ret = 0; + } + } + return ret; +} + +static int tasha_codec_enable_slimrx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + int ret = 0; + struct wcd9xxx_codec_dai_data *dai; + + core = dev_get_drvdata(codec->dev->parent); + + dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d\n" + "stream name %s event %d\n", + __func__, codec->component.name, + codec->component.num_dai, w->sname, event); + + /* Execute the callback only if interface type is slimbus */ + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) + return 0; + + dai = &tasha_p->dai[w->shift]; + dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n", + __func__, w->name, w->shift, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tasha_codec_enable_int_port(dai, codec); + (void) tasha_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_PRE_PMD: + if (!test_bit(SB_CLK_GEAR, &tasha_p->status_mask)) { + tasha_codec_vote_max_bw(codec, true); + set_bit(SB_CLK_GEAR, &tasha_p->status_mask); + } + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n", + __func__, ret); + + if (!dai->bus_down_in_recovery) + ret = tasha_codec_enable_slim_chmask(dai, false); + else + dev_dbg(codec->dev, + "%s: bus in recovery skip enable slim_chmask", + __func__); + ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->grph); + break; + } + return ret; +} + +static int tasha_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core = NULL; + struct snd_soc_codec *codec = NULL; + struct tasha_priv *tasha_p = NULL; + int ret = 0; + struct wcd9xxx_codec_dai_data *dai = NULL; + + if (!w) { + pr_err("%s invalid params\n", __func__); + return -EINVAL; + } + codec = snd_soc_dapm_to_codec(w->dapm); + tasha_p = snd_soc_codec_get_drvdata(codec); + core = tasha_p->wcd9xxx; + + dev_dbg(codec->dev, "%s: num_dai %d stream name %s\n", + __func__, codec->component.num_dai, w->sname); + + /* Execute the callback only if interface type is slimbus */ + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + dev_err(codec->dev, "%s Interface is not correct", __func__); + return 0; + } + + dev_dbg(codec->dev, "%s(): w->name %s event %d w->shift %d\n", + __func__, w->name, event, w->shift); + if (w->shift != AIF4_VIFEED) { + pr_err("%s Error in enabling the tx path\n", __func__); + ret = -EINVAL; + goto out_vi; + } + dai = &tasha_p->dai[w->shift]; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (test_bit(VI_SENSE_1, &tasha_p->status_mask)) { + dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + if (test_bit(VI_SENSE_2, &tasha_p->status_mask)) { + pr_debug("%s: spkr2 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + dai->bus_down_in_recovery = false; + tasha_codec_enable_int_port(dai, codec); + (void) tasha_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (ret) + dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n", + __func__, ret); + if (!dai->bus_down_in_recovery) + ret = tasha_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + if (test_bit(VI_SENSE_1, &tasha_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + if (test_bit(VI_SENSE_2, &tasha_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + break; + } +out_vi: + return ret; +} + +/* + * __tasha_codec_enable_slimtx: Enable the slimbus slave port + * for TX path + * @codec: Handle to the codec for which the slave port is to be + * enabled. + * @dai_data: The dai specific data for dai which is enabled. + */ +static int __tasha_codec_enable_slimtx(struct snd_soc_codec *codec, + int event, struct wcd9xxx_codec_dai_data *dai) +{ + struct wcd9xxx *core; + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + /* Execute the callback only if interface type is slimbus */ + if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) + return 0; + + dev_dbg(codec->dev, + "%s: event = %d\n", __func__, event); + core = dev_get_drvdata(codec->dev->parent); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tasha_codec_enable_int_port(dai, codec); + (void) tasha_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (!dai->bus_down_in_recovery) + ret = tasha_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + pr_debug("%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + + break; + } + + return ret; +} + +static int tasha_codec_enable_slimtx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + + dev_dbg(codec->dev, + "%s: w->name %s, w->shift = %d, num_dai %d stream name %s\n", + __func__, w->name, w->shift, + codec->component.num_dai, w->sname); + + dai = &tasha_p->dai[w->shift]; + return __tasha_codec_enable_slimtx(codec, event, dai); +} + +static void tasha_codec_cpe_pp_set_cfg(struct snd_soc_codec *codec, int event) +{ + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + u8 bit_width, rate, buf_period; + + dai = &tasha_p->dai[AIF4_MAD_TX]; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + switch (dai->bit_width) { + case 32: + bit_width = 0xF; + break; + case 24: + bit_width = 0xE; + break; + case 20: + bit_width = 0xD; + break; + case 16: + default: + bit_width = 0x0; + break; + } + snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x0F, + bit_width); + + switch (dai->rate) { + case 384000: + rate = 0x30; + break; + case 192000: + rate = 0x20; + break; + case 48000: + rate = 0x10; + break; + case 16000: + default: + rate = 0x00; + break; + } + snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x70, + rate); + + buf_period = (dai->rate * (dai->bit_width/8)) / (16*1000); + snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD, + 0xFF, buf_period); + dev_dbg(codec->dev, "%s: PP buffer period= 0x%x\n", + __func__, buf_period); + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_write(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x3C); + snd_soc_write(codec, WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD, 0x60); + break; + + default: + break; + } +} + +/* + * tasha_codec_get_mad_port_id: Callback function that will be invoked + * to get the port ID for MAD. + * @codec: Handle to the codec + * @port_id: cpe port_id needs to enable + */ +static int tasha_codec_get_mad_port_id(struct snd_soc_codec *codec, + u16 *port_id) +{ + struct tasha_priv *tasha_p; + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx_ch *ch; + + if (!port_id || !codec) + return -EINVAL; + + tasha_p = snd_soc_codec_get_drvdata(codec); + if (!tasha_p) + return -EINVAL; + + dai = &tasha_p->dai[AIF4_MAD_TX]; + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + if (ch->port == TASHA_TX12) + *port_id = WCD_CPE_AFE_OUT_PORT_2; + else if (ch->port == TASHA_TX13) + *port_id = WCD_CPE_AFE_OUT_PORT_4; + else { + dev_err(codec->dev, "%s: invalid mad_port = %d\n", + __func__, ch->port); + return -EINVAL; + } + } + dev_dbg(codec->dev, "%s: port_id = %d\n", __func__, *port_id); + + return 0; +} + +/* + * tasha_codec_enable_slimtx_mad: Callback function that will be invoked + * to setup the slave port for MAD. + * @codec: Handle to the codec + * @event: Indicates whether to enable or disable the slave port + */ +static int tasha_codec_enable_slimtx_mad(struct snd_soc_codec *codec, + u8 event) +{ + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx_ch *ch; + int dapm_event = SND_SOC_DAPM_POST_PMU; + u16 port = 0; + int ret = 0; + + dai = &tasha_p->dai[AIF4_MAD_TX]; + + if (event == 0) + dapm_event = SND_SOC_DAPM_POST_PMD; + + dev_dbg(codec->dev, + "%s: mad_channel, event = 0x%x\n", + __func__, event); + + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + dev_dbg(codec->dev, "%s: mad_port = %d, event = 0x%x\n", + __func__, ch->port, event); + if (ch->port == TASHA_TX13) { + tasha_codec_cpe_pp_set_cfg(codec, dapm_event); + port = TASHA_TX13; + break; + } + } + + ret = __tasha_codec_enable_slimtx(codec, dapm_event, dai); + + if (port == TASHA_TX13) { + switch (dapm_event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, + 0x20, 0x00); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, + 0x03, 0x02); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, + 0x80, 0x80); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, + 0x20, 0x20); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, + 0x03, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, + 0x80, 0x00); + break; + } + } + + return ret; +} + +static int tasha_put_iir_band_audio_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + /* + * Mask top bit it is reserved + * Updates addr automatically for each B2 write + */ + snd_soc_write(codec, + (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[0]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[1]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[2]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[3]); + set_iir_band_coeff(codec, iir_idx, band_idx, + ucontrol->value.integer.value[4]); + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static int tasha_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->comp_enabled[comp]; + return 0; +} + +static int tasha_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + pr_debug("%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, tasha->comp_enabled[comp], value); + tasha->comp_enabled[comp] = value; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_2: + snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_3: + break; + case COMPANDER_4: + break; + case COMPANDER_5: + snd_soc_update_bits(codec, WCD9335_SE_LO_LO3_GAIN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_6: + snd_soc_update_bits(codec, WCD9335_SE_LO_LO4_GAIN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_7: + break; + case COMPANDER_8: + break; + default: + /* + * if compander is not enabled for any interpolator, + * it does not cause any audio failure, so do not + * return error in this case, but just print a log + */ + dev_warn(codec->dev, "%s: unknown compander: %d\n", + __func__, comp); + }; + return 0; +} + +static void tasha_codec_init_flyback(struct snd_soc_codec *codec) +{ + snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0xC0, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0xC0, 0x00); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_FLYB_BUFF, 0x0F, 0x00); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_FLYB_BUFF, 0xF0, 0x00); +} + +static int tasha_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tasha->rx_bias_count++; + if (tasha->rx_bias_count == 1) { + if (TASHA_IS_2_0(tasha->wcd9xxx)) + tasha_codec_init_flyback(codec); + snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES, + 0x01, 0x01); + } + break; + case SND_SOC_DAPM_POST_PMD: + tasha->rx_bias_count--; + if (!tasha->rx_bias_count) + snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES, + 0x01, 0x00); + break; + }; + dev_dbg(codec->dev, "%s: Current RX BIAS user count: %d\n", __func__, + tasha->rx_bias_count); + + return 0; +} + +static void tasha_realign_anc_coeff(struct snd_soc_codec *codec, + u16 reg1, u16 reg2) +{ + u8 val1, val2, tmpval1, tmpval2; + + snd_soc_write(codec, reg1, 0x00); + tmpval1 = snd_soc_read(codec, reg2); + tmpval2 = snd_soc_read(codec, reg2); + snd_soc_write(codec, reg1, 0x00); + snd_soc_write(codec, reg2, 0xFF); + snd_soc_write(codec, reg1, 0x01); + snd_soc_write(codec, reg2, 0xFF); + + snd_soc_write(codec, reg1, 0x00); + val1 = snd_soc_read(codec, reg2); + val2 = snd_soc_read(codec, reg2); + + if (val1 == 0x0F && val2 == 0xFF) { + dev_dbg(codec->dev, "%s: ANC0 co-eff index re-aligned\n", + __func__); + snd_soc_read(codec, reg2); + snd_soc_write(codec, reg1, 0x00); + snd_soc_write(codec, reg2, tmpval2); + snd_soc_write(codec, reg1, 0x01); + snd_soc_write(codec, reg2, tmpval1); + } else if (val1 == 0xFF && val2 == 0x0F) { + dev_dbg(codec->dev, "%s: ANC1 co-eff index already aligned\n", + __func__); + snd_soc_write(codec, reg1, 0x00); + snd_soc_write(codec, reg2, tmpval1); + snd_soc_write(codec, reg1, 0x01); + snd_soc_write(codec, reg2, tmpval2); + } else { + dev_err(codec->dev, "%s: ANC0 co-eff index not aligned\n", + __func__); + } +} + +static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + const char *filename; + const struct firmware *fw; + int i; + int ret = 0; + int num_anc_slots; + struct wcd9xxx_anc_header *anc_head; + struct firmware_cal *hwdep_cal = NULL; + u32 anc_writes_size = 0; + u32 anc_cal_size = 0; + int anc_size_remaining; + u32 *anc_ptr; + u16 reg; + u8 mask, val; + size_t cal_size; + const void *data; + + if (!tasha->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_ANC_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + filename = "wcd9335/wcd9335_anc.bin"; + ret = request_firmware(&fw, filename, codec->dev); + if (ret != 0) { + dev_err(codec->dev, + "Failed to acquire ANC data: %d\n", ret); + return -ENODEV; + } + if (!fw) { + dev_err(codec->dev, "failed to get anc fw"); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, + "%s: using request_firmware calibration\n", __func__); + } + if (cal_size < sizeof(struct wcd9xxx_anc_header)) { + dev_err(codec->dev, "Not enough data\n"); + ret = -ENOMEM; + goto err; + } + /* First number is the number of register writes */ + anc_head = (struct wcd9xxx_anc_header *)(data); + anc_ptr = (u32 *)(data + + sizeof(struct wcd9xxx_anc_header)); + anc_size_remaining = cal_size - + sizeof(struct wcd9xxx_anc_header); + num_anc_slots = anc_head->num_anc_slots; + + if (tasha->anc_slot >= num_anc_slots) { + dev_err(codec->dev, "Invalid ANC slot selected\n"); + ret = -EINVAL; + goto err; + } + for (i = 0; i < num_anc_slots; i++) { + if (anc_size_remaining < TASHA_PACKED_REG_SIZE) { + dev_err(codec->dev, + "Invalid register format\n"); + ret = -EINVAL; + goto err; + } + anc_writes_size = (u32)(*anc_ptr); + anc_size_remaining -= sizeof(u32); + anc_ptr += 1; + + if (anc_writes_size * TASHA_PACKED_REG_SIZE + > anc_size_remaining) { + dev_err(codec->dev, + "Invalid register format\n"); + ret = -EINVAL; + goto err; + } + + if (tasha->anc_slot == i) + break; + + anc_size_remaining -= (anc_writes_size * + TASHA_PACKED_REG_SIZE); + anc_ptr += anc_writes_size; + } + if (i == num_anc_slots) { + dev_err(codec->dev, "Selected ANC slot not present\n"); + ret = -EINVAL; + goto err; + } + + i = 0; + anc_cal_size = anc_writes_size; + + if (!strcmp(w->name, "RX INT0 DAC") || + !strcmp(w->name, "ANC SPK1 PA")) + tasha_realign_anc_coeff(codec, + WCD9335_CDC_ANC0_IIR_COEFF_1_CTL, + WCD9335_CDC_ANC0_IIR_COEFF_2_CTL); + + if (!strcmp(w->name, "RX INT1 DAC") || + !strcmp(w->name, "RX INT3 DAC")) { + tasha_realign_anc_coeff(codec, + WCD9335_CDC_ANC0_IIR_COEFF_1_CTL, + WCD9335_CDC_ANC0_IIR_COEFF_2_CTL); + anc_writes_size = anc_cal_size / 2; + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x39, 0x39); + } else if (!strcmp(w->name, "RX INT2 DAC") || + !strcmp(w->name, "RX INT4 DAC")) { + tasha_realign_anc_coeff(codec, + WCD9335_CDC_ANC1_IIR_COEFF_1_CTL, + WCD9335_CDC_ANC1_IIR_COEFF_2_CTL); + i = anc_cal_size / 2; + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x39, 0x39); + } + + for (; i < anc_writes_size; i++) { + TASHA_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val); + snd_soc_write(codec, reg, (val & mask)); + } + if (!strcmp(w->name, "RX INT1 DAC") || + !strcmp(w->name, "RX INT3 DAC")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x08, 0x08); + } else if (!strcmp(w->name, "RX INT2 DAC") || + !strcmp(w->name, "RX INT4 DAC")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x08, 0x08); + } + + if (!hwdep_cal) + release_firmware(fw); + break; + case SND_SOC_DAPM_POST_PMU: + /* Remove ANC Rx from reset */ + snd_soc_update_bits(codec, WCD9335_CDC_ANC0_CLK_RESET_CTL, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_ANC1_CLK_RESET_CTL, + 0x08, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + if (!strcmp(w->name, "ANC HPHL PA") || + !strcmp(w->name, "ANC EAR PA") || + !strcmp(w->name, "ANC SPK1 PA") || + !strcmp(w->name, "ANC LINEOUT1 PA")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_MODE_1_CTL, 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_MODE_1_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x38); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x07, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x00); + } else if (!strcmp(w->name, "ANC HPHR PA") || + !strcmp(w->name, "ANC LINEOUT2 PA")) { + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_MODE_1_CTL, 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_MODE_1_CTL, 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x38); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x07, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x00); + } + break; + } + + return 0; +err: + if (!hwdep_cal) + release_firmware(fw); + return ret; +} + +static void tasha_codec_clear_anc_tx_hold(struct tasha_priv *tasha) +{ + if (test_and_clear_bit(ANC_MIC_AMIC1, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC1, false); + if (test_and_clear_bit(ANC_MIC_AMIC2, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC2, false); + if (test_and_clear_bit(ANC_MIC_AMIC3, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC3, false); + if (test_and_clear_bit(ANC_MIC_AMIC4, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC4, false); + if (test_and_clear_bit(ANC_MIC_AMIC5, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC5, false); + if (test_and_clear_bit(ANC_MIC_AMIC6, &tasha->status_mask)) + tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC6, false); +} + +static void tasha_codec_hph_post_pa_config(struct tasha_priv *tasha, + int mode, int event) +{ + u8 scale_val = 0; + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) + return; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + switch (mode) { + case CLS_H_HIFI: + scale_val = 0x3; + break; + case CLS_H_LOHIFI: + scale_val = 0x1; + break; + } + if (tasha->anc_func) { + /* Clear Tx FE HOLD if both PAs are enabled */ + if ((snd_soc_read(tasha->codec, WCD9335_ANA_HPH) & + 0xC0) == 0xC0) { + tasha_codec_clear_anc_tx_hold(tasha); + } + } + break; + case SND_SOC_DAPM_PRE_PMD: + scale_val = 0x6; + break; + } + + if (scale_val) + snd_soc_update_bits(tasha->codec, WCD9335_HPH_PA_CTL1, 0x0E, + scale_val << 1); + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (tasha->comp_enabled[COMPANDER_1] || + tasha->comp_enabled[COMPANDER_2]) { + snd_soc_update_bits(tasha->codec, WCD9335_HPH_L_EN, + 0x20, 0x00); + snd_soc_update_bits(tasha->codec, WCD9335_HPH_R_EN, + 0x20, 0x00); + snd_soc_update_bits(tasha->codec, WCD9335_HPH_AUTO_CHOP, + 0x20, 0x20); + } + snd_soc_update_bits(tasha->codec, WCD9335_HPH_L_EN, 0x1F, + tasha->hph_l_gain); + snd_soc_update_bits(tasha->codec, WCD9335_HPH_R_EN, 0x1F, + tasha->hph_r_gain); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(tasha->codec, WCD9335_HPH_AUTO_CHOP, 0x20, + 0x00); + } +} + +static void tasha_codec_override(struct snd_soc_codec *codec, + int mode, + int event) +{ + if (mode == CLS_AB) { + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!(snd_soc_read(codec, + WCD9335_CDC_RX2_RX_PATH_CTL) & 0x10) && + (!(snd_soc_read(codec, + WCD9335_CDC_RX1_RX_PATH_CTL) & 0x10))) + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00); + break; + } + } +} + +static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int hph_mode = tasha->hph_mode; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((!(strcmp(w->name, "ANC HPHR PA"))) && + (test_bit(HPH_PA_DELAY, &tasha->status_mask))) { + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0); + } + set_bit(HPH_PA_DELAY, &tasha->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (!(strcmp(w->name, "ANC HPHR PA"))) { + if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) + * then do nothing for POST_PMU and let left + * channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + if (test_bit(HPH_PA_DELAY, &tasha->status_mask)) { + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tasha->status_mask); + } + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + + if (!(strcmp(w->name, "ANC HPHR PA"))) { + /* Do everything needed for left channel */ + snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + /* Remove ANC Rx from reset */ + ret = tasha_codec_enable_anc(w, kcontrol, event); + } + tasha_codec_override(codec, hph_mode, event); + break; + + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_PRE_HPHR_PA_OFF, + &tasha->mbhc); + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + if (!(strcmp(w->name, "ANC HPHR PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x40, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tasha_codec_override(codec, hph_mode, event); + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_POST_HPHR_PA_OFF, + &tasha->mbhc); + + if (!(strcmp(w->name, "ANC HPHR PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_CFG0, 0x10, 0x00); + } + break; + }; + + return ret; +} + +static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int hph_mode = tasha->hph_mode; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((!(strcmp(w->name, "ANC HPHL PA"))) && + (test_bit(HPH_PA_DELAY, &tasha->status_mask))) { + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0); + } + set_bit(HPH_PA_DELAY, &tasha->status_mask); + break; + case SND_SOC_DAPM_POST_PMU: + if (!(strcmp(w->name, "ANC HPHL PA"))) { + if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) + * then do nothing for POST_PMU and let right + * channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement + */ + if (test_bit(HPH_PA_DELAY, &tasha->status_mask)) { + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tasha->status_mask); + } + + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + + if (!(strcmp(w->name, "ANC HPHL PA"))) { + /* Do everything needed for right channel */ + snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + + /* Remove ANC Rx from reset */ + ret = tasha_codec_enable_anc(w, kcontrol, event); + } + tasha_codec_override(codec, hph_mode, event); + break; + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_PRE_HPHL_PA_OFF, + &tasha->mbhc); + tasha_codec_hph_post_pa_config(tasha, hph_mode, event); + if (!(strcmp(w->name, "ANC HPHL PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x80, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tasha_codec_override(codec, hph_mode, event); + blocking_notifier_call_chain(&tasha->notifier, + WCD_EVENT_POST_HPHL_PA_OFF, + &tasha->mbhc); + + if (!(strcmp(w->name, "ANC HPHL PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_CFG0, 0x10, 0x00); + } + break; + }; + + return ret; +} + +static int tasha_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 lineout_vol_reg = 0, lineout_mix_vol_reg = 0; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (w->reg == WCD9335_ANA_LO_1_2) { + if (w->shift == 7) { + lineout_vol_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX3_RX_PATH_MIX_CTL; + } else if (w->shift == 6) { + lineout_vol_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX4_RX_PATH_MIX_CTL; + } + } else if (w->reg == WCD9335_ANA_LO_3_4) { + if (w->shift == 7) { + lineout_vol_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX5_RX_PATH_MIX_CTL; + } else if (w->shift == 6) { + lineout_vol_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + lineout_mix_vol_reg = WCD9335_CDC_RX6_RX_PATH_MIX_CTL; + } + } else { + dev_err(codec->dev, "%s: Error enabling lineout PA\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, lineout_vol_reg, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, lineout_mix_vol_reg)) & 0x10) + snd_soc_update_bits(codec, + lineout_mix_vol_reg, + 0x10, 0x00); + if (!(strcmp(w->name, "ANC LINEOUT1 PA")) || + !(strcmp(w->name, "ANC LINEOUT2 PA"))) + ret = tasha_codec_enable_anc(w, kcontrol, event); + tasha_codec_override(codec, CLS_AB, event); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tasha_codec_override(codec, CLS_AB, event); + if (!(strcmp(w->name, "ANC LINEOUT1 PA")) || + !(strcmp(w->name, "ANC LINEOUT2 PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + if (!(strcmp(w->name, "ANC LINEOUT1 PA"))) + snd_soc_update_bits(codec, + WCD9335_CDC_RX3_RX_PATH_CFG0, 0x10, 0x10); + else + snd_soc_update_bits(codec, + WCD9335_CDC_RX4_RX_PATH_CFG0, 0x10, 0x10); + } + break; + }; + + return ret; +} + +static void tasha_spk_anc_update_callback(struct work_struct *work) +{ + struct spk_anc_work *spk_anc_dwork; + struct tasha_priv *tasha; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + + delayed_work = to_delayed_work(work); + spk_anc_dwork = container_of(delayed_work, struct spk_anc_work, dwork); + tasha = spk_anc_dwork->tasha; + codec = tasha->codec; + + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0, 0x10, 0x10); +} + +static int tasha_codec_enable_spk_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d %d\n", __func__, w->name, event, + tasha->anc_func); + + if (!tasha->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tasha_codec_enable_anc(w, kcontrol, event); + schedule_delayed_work(&tasha->spk_anc_dwork.dwork, + msecs_to_jiffies(spk_anc_en_delay)); + break; + case SND_SOC_DAPM_POST_PMD: + cancel_delayed_work_sync(&tasha->spk_anc_dwork.dwork); + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0, + 0x10, 0x00); + ret = tasha_codec_enable_anc(w, kcontrol, event); + break; + } + return ret; +} + +static int tasha_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, WCD9335_CDC_RX0_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD9335_CDC_RX0_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD9335_CDC_RX0_RX_PATH_MIX_CTL, + 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + + if (!(strcmp(w->name, "ANC EAR PA"))) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD9335_CDC_RX0_RX_PATH_CFG0, 0x10, 0x00); + } + break; + }; + + return ret; +} + +static void tasha_codec_hph_mode_gain_opt(struct snd_soc_codec *codec, + u8 gain) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u8 hph_l_en, hph_r_en; + u8 l_val, r_val; + u8 hph_pa_status; + bool is_hphl_pa, is_hphr_pa; + + hph_pa_status = snd_soc_read(codec, WCD9335_ANA_HPH); + is_hphl_pa = hph_pa_status >> 7; + is_hphr_pa = (hph_pa_status & 0x40) >> 6; + + hph_l_en = snd_soc_read(codec, WCD9335_HPH_L_EN); + hph_r_en = snd_soc_read(codec, WCD9335_HPH_R_EN); + + l_val = (hph_l_en & 0xC0) | 0x20 | gain; + r_val = (hph_r_en & 0xC0) | 0x20 | gain; + + /* + * Set HPH_L & HPH_R gain source selection to REGISTER + * for better click and pop only if corresponding PAs are + * not enabled. Also cache the values of the HPHL/R + * PA gains to be applied after PAs are enabled + */ + if ((l_val != hph_l_en) && !is_hphl_pa) { + snd_soc_write(codec, WCD9335_HPH_L_EN, l_val); + tasha->hph_l_gain = hph_l_en & 0x1F; + } + + if ((r_val != hph_r_en) && !is_hphr_pa) { + snd_soc_write(codec, WCD9335_HPH_R_EN, r_val); + tasha->hph_r_gain = hph_r_en & 0x1F; + } +} + +static void tasha_codec_hph_lohifi_config(struct snd_soc_codec *codec, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_PA, 0x0F, 0x06); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, + 0xF0, 0x40); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C); + tasha_codec_hph_mode_gain_opt(codec, 0x11); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02); + snd_soc_write(codec, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8A); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_PA, 0x0F, 0x0A); + } +} + +static void tasha_codec_hph_lp_config(struct snd_soc_codec *codec, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C); + tasha_codec_hph_mode_gain_opt(codec, 0x10); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x04, 0x04); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x20, 0x20); + snd_soc_update_bits(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x07, + 0x01); + snd_soc_update_bits(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x70, + 0x10); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO, + 0x0F, 0x01); + snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO, + 0xF0, 0x10); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_write(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO, 0x88); + snd_soc_write(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x33); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x20, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x04, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02); + snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0xC0, 0x80); + snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0xC0, 0x80); + } +} + +static void tasha_codec_hph_hifi_config(struct snd_soc_codec *codec, + int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C); + tasha_codec_hph_mode_gain_opt(codec, 0x11); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02); + } +} + +static void tasha_codec_hph_mode_config(struct snd_soc_codec *codec, + int event, int mode) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) + return; + + switch (mode) { + case CLS_H_LP: + tasha_codec_hph_lp_config(codec, event); + break; + case CLS_H_LOHIFI: + tasha_codec_hph_lohifi_config(codec, event); + break; + case CLS_H_HIFI: + tasha_codec_hph_hifi_config(codec, event); + break; + } +} + +static int tasha_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int hph_mode = tasha->hph_mode; + u8 dem_inp; + int ret = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, WCD9335_CDC_RX2_RX_PATH_SEC0) & + 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + tasha_codec_hph_mode_config(codec, event, hph_mode); + + if (tasha->anc_func) + snd_soc_update_bits(codec, + WCD9335_CDC_RX2_RX_PATH_CFG0, 0x10, 0x10); + + break; + case SND_SOC_DAPM_POST_PMU: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x03); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x00); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + if (!(wcd_clsh_get_clsh_state(&tasha->clsh_d) & + WCD_CLSH_STATE_HPHL)) + tasha_codec_hph_mode_config(codec, event, hph_mode); + + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + break; + }; + + return ret; +} + +static int tasha_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int hph_mode = tasha->hph_mode; + u8 dem_inp; + int ret = 0; + uint32_t impedl = 0, impedr = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func) { + ret = tasha_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, WCD9335_CDC_RX1_RX_PATH_SEC0) & + 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + tasha_codec_hph_mode_config(codec, event, hph_mode); + + if (tasha->anc_func) + snd_soc_update_bits(codec, + WCD9335_CDC_RX1_RX_PATH_CFG0, 0x10, 0x10); + + ret = wcd_mbhc_get_impedance(&tasha->mbhc, + &impedl, &impedr); + if (!ret) { + wcd_clsh_imped_config(codec, impedl, false); + set_bit(CLASSH_CONFIG, &tasha->status_mask); + } else { + dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n", + __func__, ret); + ret = 0; + } + + + break; + case SND_SOC_DAPM_POST_PMU: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x03); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if ((hph_mode == CLS_H_LP) && + (TASHA_IS_1_1(wcd9xxx))) { + snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, + 0x03, 0x00); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + + if (!(wcd_clsh_get_clsh_state(&tasha->clsh_d) & + WCD_CLSH_STATE_HPHR)) + tasha_codec_hph_mode_config(codec, event, hph_mode); + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + ((hph_mode == CLS_H_LOHIFI) ? + CLS_H_HIFI : hph_mode)); + + if (test_bit(CLASSH_CONFIG, &tasha->status_mask)) { + wcd_clsh_imped_config(codec, impedl, true); + clear_bit(CLASSH_CONFIG, &tasha->status_mask); + } else + dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n", + __func__, ret); + + + break; + }; + + return ret; +} + +static int tasha_codec_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func && + (!strcmp(w->name, "RX INT3 DAC") || + !strcmp(w->name, "RX INT4 DAC"))) + ret = tasha_codec_enable_anc(w, kcontrol, event); + + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_LO, + CLS_AB); + + if (tasha->anc_func) { + if (!strcmp(w->name, "RX INT3 DAC")) + snd_soc_update_bits(codec, + WCD9335_CDC_RX3_RX_PATH_CFG0, 0x10, 0x10); + else if (!strcmp(w->name, "RX INT4 DAC")) + snd_soc_update_bits(codec, + WCD9335_CDC_RX4_RX_PATH_CFG0, 0x10, 0x10); + } + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_LO, + CLS_AB); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget tasha_dapm_i2s_widgets[] = { + SND_SOC_DAPM_SUPPLY("RX_I2S_CTL", WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("TX_I2S_CTL", WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0, 0, NULL, 0), +}; + +static int tasha_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tasha->anc_func) + ret = tasha_codec_enable_anc(w, kcontrol, event); + + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + if (tasha->anc_func) + snd_soc_update_bits(codec, + WCD9335_CDC_RX0_RX_PATH_CFG0, 0x10, 0x10); + + break; + case SND_SOC_DAPM_POST_PMU: + break; + case SND_SOC_DAPM_PRE_PMD: + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tasha->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + break; + }; + + return ret; +} + +static int tasha_codec_spk_boost_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); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg, reg_mix; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (!strcmp(w->name, "RX INT7 CHAIN")) { + boost_path_ctl = WCD9335_CDC_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = WCD9335_CDC_RX7_RX_PATH_CFG1; + reg = WCD9335_CDC_RX7_RX_PATH_CTL; + reg_mix = WCD9335_CDC_RX7_RX_PATH_MIX_CTL; + } else if (!strcmp(w->name, "RX INT8 CHAIN")) { + boost_path_ctl = WCD9335_CDC_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = WCD9335_CDC_RX8_RX_PATH_CFG1; + reg = WCD9335_CDC_RX8_RX_PATH_CTL; + reg_mix = WCD9335_CDC_RX8_RX_PATH_MIX_CTL; + } else { + dev_err(codec->dev, "%s: unknown widget: %s\n", + __func__, w->name); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01); + snd_soc_update_bits(codec, reg, 0x10, 0x00); + if ((snd_soc_read(codec, reg_mix)) & 0x10) + snd_soc_update_bits(codec, reg_mix, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00); + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00); + break; + }; + + return 0; +} + +static u16 tasha_interp_get_primary_reg(u16 reg, u16 *ind) +{ + u16 prim_int_reg = 0; + + switch (reg) { + case WCD9335_CDC_RX0_RX_PATH_CTL: + case WCD9335_CDC_RX0_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX0_RX_PATH_CTL; + *ind = 0; + break; + case WCD9335_CDC_RX1_RX_PATH_CTL: + case WCD9335_CDC_RX1_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX1_RX_PATH_CTL; + *ind = 1; + break; + case WCD9335_CDC_RX2_RX_PATH_CTL: + case WCD9335_CDC_RX2_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX2_RX_PATH_CTL; + *ind = 2; + break; + case WCD9335_CDC_RX3_RX_PATH_CTL: + case WCD9335_CDC_RX3_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + *ind = 3; + break; + case WCD9335_CDC_RX4_RX_PATH_CTL: + case WCD9335_CDC_RX4_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + *ind = 4; + break; + case WCD9335_CDC_RX5_RX_PATH_CTL: + case WCD9335_CDC_RX5_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + *ind = 5; + break; + case WCD9335_CDC_RX6_RX_PATH_CTL: + case WCD9335_CDC_RX6_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + *ind = 6; + break; + case WCD9335_CDC_RX7_RX_PATH_CTL: + case WCD9335_CDC_RX7_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX7_RX_PATH_CTL; + *ind = 7; + break; + case WCD9335_CDC_RX8_RX_PATH_CTL: + case WCD9335_CDC_RX8_RX_PATH_MIX_CTL: + prim_int_reg = WCD9335_CDC_RX8_RX_PATH_CTL; + *ind = 8; + break; + }; + + return prim_int_reg; +} + +static void tasha_codec_hd2_control(struct snd_soc_codec *codec, + u16 prim_int_reg, int event) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + if (!TASHA_IS_2_0(tasha->wcd9xxx)) + return; + + if (prim_int_reg == WCD9335_CDC_RX1_RX_PATH_CTL) { + hd2_scale_reg = WCD9335_CDC_RX1_RX_PATH_SEC3; + hd2_enable_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + } + if (prim_int_reg == WCD9335_CDC_RX2_RX_PATH_CTL) { + hd2_scale_reg = WCD9335_CDC_RX2_RX_PATH_SEC3; + hd2_enable_reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int tasha_codec_enable_prim_interpolator( + struct snd_soc_codec *codec, + u16 reg, int event) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 prim_int_reg; + u16 ind = 0; + + prim_int_reg = tasha_interp_get_primary_reg(reg, &ind); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tasha->prim_int_users[ind]++; + if (tasha->prim_int_users[ind] == 1) { + snd_soc_update_bits(codec, prim_int_reg, + 0x10, 0x10); + tasha_codec_hd2_control(codec, prim_int_reg, event); + snd_soc_update_bits(codec, prim_int_reg, + 1 << 0x5, 1 << 0x5); + } + if ((reg != prim_int_reg) && + ((snd_soc_read(codec, prim_int_reg)) & 0x10)) + snd_soc_update_bits(codec, reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + tasha->prim_int_users[ind]--; + if (tasha->prim_int_users[ind] == 0) { + snd_soc_update_bits(codec, prim_int_reg, + 1 << 0x5, 0 << 0x5); + snd_soc_update_bits(codec, prim_int_reg, + 0x40, 0x40); + snd_soc_update_bits(codec, prim_int_reg, + 0x40, 0x00); + tasha_codec_hd2_control(codec, prim_int_reg, event); + } + break; + }; + + dev_dbg(codec->dev, "%s: primary interpolator: INT%d, users: %d\n", + __func__, ind, tasha->prim_int_users[ind]); + return 0; +} + +static int tasha_codec_enable_spline_src(struct snd_soc_codec *codec, + int src_num, + int event) +{ + u16 src_paired_reg = 0; + struct tasha_priv *tasha; + u16 rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + u16 rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL; + int *src_users, count, spl_src = SPLINE_SRC0; + u16 src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + + tasha = snd_soc_codec_get_drvdata(codec); + + switch (src_num) { + case SRC_IN_HPHL: + rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL; + spl_src = SPLINE_SRC0; + break; + case SRC_IN_LO1: + rx_path_cfg_reg = WCD9335_CDC_RX3_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX3_RX_PATH_CTL; + spl_src = SPLINE_SRC0; + break; + case SRC_IN_HPHR: + rx_path_cfg_reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX2_RX_PATH_CTL; + spl_src = SPLINE_SRC1; + break; + case SRC_IN_LO2: + rx_path_cfg_reg = WCD9335_CDC_RX4_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX4_RX_PATH_CTL; + spl_src = SPLINE_SRC1; + break; + case SRC_IN_SPKRL: + rx_path_cfg_reg = WCD9335_CDC_RX7_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX7_RX_PATH_CTL; + spl_src = SPLINE_SRC2; + break; + case SRC_IN_LO3: + rx_path_cfg_reg = WCD9335_CDC_RX5_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX5_RX_PATH_CTL; + spl_src = SPLINE_SRC2; + break; + case SRC_IN_SPKRR: + rx_path_cfg_reg = WCD9335_CDC_RX8_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX8_RX_PATH_CTL; + spl_src = SPLINE_SRC3; + break; + case SRC_IN_LO4: + rx_path_cfg_reg = WCD9335_CDC_RX6_RX_PATH_CFG0; + src_clk_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0; + src_paired_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0; + rx_path_ctl_reg = WCD9335_CDC_RX6_RX_PATH_CTL; + spl_src = SPLINE_SRC3; + break; + }; + + src_users = &tasha->spl_src_users[spl_src]; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + count = *src_users; + count++; + if (count == 1) { + if ((snd_soc_read(codec, src_clk_reg) & 0x02) || + (snd_soc_read(codec, src_paired_reg) & 0x02)) { + snd_soc_update_bits(codec, src_clk_reg, 0x02, + 0x00); + snd_soc_update_bits(codec, src_paired_reg, + 0x02, 0x00); + } + snd_soc_update_bits(codec, src_clk_reg, 0x01, 0x01); + snd_soc_update_bits(codec, rx_path_cfg_reg, 0x80, + 0x80); + } + *src_users = count; + break; + case SND_SOC_DAPM_POST_PMD: + count = *src_users; + count--; + if (count == 0) { + snd_soc_update_bits(codec, rx_path_cfg_reg, 0x80, + 0x00); + snd_soc_update_bits(codec, src_clk_reg, 0x03, 0x02); + /* default sample rate */ + snd_soc_update_bits(codec, rx_path_ctl_reg, 0x0f, + 0x04); + } + *src_users = count; + break; + }; + + dev_dbg(codec->dev, "%s: Spline SRC%d, users: %d\n", + __func__, spl_src, *src_users); + return 0; +} + +static int tasha_codec_enable_spline_resampler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + u8 src_in; + + src_in = snd_soc_read(codec, WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0); + if (!(src_in & 0xFF)) { + dev_err(codec->dev, "%s: Spline SRC%u input not selected\n", + __func__, w->shift); + return -EINVAL; + } + + switch (w->shift) { + case SPLINE_SRC0: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0x03) == 1) ? SRC_IN_HPHL : SRC_IN_LO1, + event); + break; + case SPLINE_SRC1: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0x0C) == 4) ? SRC_IN_HPHR : SRC_IN_LO2, + event); + break; + case SPLINE_SRC2: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0x30) == 0x10) ? SRC_IN_LO3 : SRC_IN_SPKRL, + event); + break; + case SPLINE_SRC3: + ret = tasha_codec_enable_spline_src(codec, + ((src_in & 0xC0) == 0x40) ? SRC_IN_LO4 : SRC_IN_SPKRR, + event); + break; + default: + dev_err(codec->dev, "%s: Invalid spline src:%u\n", __func__, + w->shift); + ret = -EINVAL; + }; + + return ret; +} + +static int tasha_codec_enable_swr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha; + int i, ch_cnt; + + tasha = snd_soc_codec_get_drvdata(codec); + + if (!tasha->nr) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) && + !tasha->rx_7_count) + tasha->rx_7_count++; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + !tasha->rx_8_count) + tasha->rx_8_count++; + ch_cnt = tasha->rx_7_count + tasha->rx_8_count; + + for (i = 0; i < tasha->nr; i++) { + swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev, + SWR_DEVICE_UP, NULL); + swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + } + break; + case SND_SOC_DAPM_POST_PMD: + if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) && + tasha->rx_7_count) + tasha->rx_7_count--; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + tasha->rx_8_count) + tasha->rx_8_count--; + ch_cnt = tasha->rx_7_count + tasha->rx_8_count; + + for (i = 0; i < tasha->nr; i++) + swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + + break; + } + dev_dbg(tasha->dev, "%s: current swr ch cnt: %d\n", + __func__, tasha->rx_7_count + tasha->rx_8_count); + + return 0; +} + +static int tasha_codec_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg) +{ + int comp_gain_offset, val; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + switch (tasha->spkr_mode) { + /* Compander gain in SPKR_MODE1 case is 12 dB */ + case SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (tasha->comp_enabled[COMPANDER_7] && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) && + (tasha->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + tasha->ear_spkr_gain - 1; + snd_soc_write(codec, gain_reg, val); + + dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX7 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (tasha->comp_enabled[COMPANDER_7] && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) && + (tasha->ear_spkr_gain != 0)) { + snd_soc_write(codec, gain_reg, 0x0); + + dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + +static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + int offset_val = 0; + int val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + switch (w->reg) { + case WCD9335_CDC_RX0_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX0_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX1_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX1_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX2_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX2_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX3_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX3_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX4_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX4_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX5_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX5_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX6_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX6_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX7_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX7_RX_VOL_MIX_CTL; + break; + case WCD9335_CDC_RX8_RX_PATH_MIX_CTL: + gain_reg = WCD9335_CDC_RX8_RX_VOL_MIX_CTL; + break; + default: + dev_err(codec->dev, "%s: No gain register avail for %s\n", + __func__, w->name); + return 0; + }; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int __tasha_cdc_native_clk_enable(struct tasha_priv *tasha, + bool enable) +{ + int ret = 0; + struct snd_soc_codec *codec = tasha->codec; + + if (!tasha->wcd_native_clk) { + dev_err(tasha->dev, "%s: wcd native clock is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(tasha->dev, "%s: native_clk_enable = %u\n", __func__, enable); + + if (enable) { + ret = clk_prepare_enable(tasha->wcd_native_clk); + if (ret) { + dev_err(tasha->dev, "%s: native clk enable failed\n", + __func__); + goto err; + } + if (++tasha->native_clk_users == 1) { + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x80, 0x80); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_GATE, + 0x04, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x02); + } + } else { + if (tasha->native_clk_users && + (--tasha->native_clk_users == 0)) { + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_GATE, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL, + 0x10, 0x00); + } + clk_disable_unprepare(tasha->wcd_native_clk); + } + + dev_dbg(codec->dev, "%s: native_clk_users: %d\n", __func__, + tasha->native_clk_users); +err: + return ret; +} + +static int tasha_codec_get_native_fifo_sync_mask(struct snd_soc_codec *codec, + int interp_n) +{ + int mask = 0; + u16 reg; + u8 val1, val2, inp0 = 0; + u8 inp1 = 0, inp2 = 0; + + reg = WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0 + (2 * interp_n) - 2; + + val1 = snd_soc_read(codec, reg); + val2 = snd_soc_read(codec, reg + 1); + + inp0 = val1 & 0x0F; + inp1 = (val1 >> 4) & 0x0F; + inp2 = (val2 >> 4) & 0x0F; + + if (IS_VALID_NATIVE_FIFO_PORT(inp0)) + mask |= (1 << (inp0 - 5)); + if (IS_VALID_NATIVE_FIFO_PORT(inp1)) + mask |= (1 << (inp1 - 5)); + if (IS_VALID_NATIVE_FIFO_PORT(inp2)) + mask |= (1 << (inp2 - 5)); + + dev_dbg(codec->dev, "%s: native fifo mask: 0x%x\n", __func__, mask); + if (!mask) + dev_err(codec->dev, "native fifo err,int:%d,inp0:%d,inp1:%d,inp2:%d\n", + interp_n, inp0, inp1, inp2); + return mask; +} + +static int tasha_enable_native_supply(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int mask; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 interp_reg; + + dev_dbg(codec->dev, "%s: event: %d, shift:%d\n", __func__, event, + w->shift); + + if (w->shift < INTERP_HPHL || w->shift > INTERP_LO2) + return -EINVAL; + + interp_reg = WCD9335_CDC_RX1_RX_PATH_CTL + 20 * (w->shift - 1); + + mask = tasha_codec_get_native_fifo_sync_mask(codec, w->shift); + if (!mask) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Adjust interpolator rate to 44P1_NATIVE */ + snd_soc_update_bits(codec, interp_reg, 0x0F, 0x09); + __tasha_cdc_native_clk_enable(tasha, true); + snd_soc_update_bits(codec, WCD9335_DATA_HUB_NATIVE_FIFO_SYNC, + mask, mask); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WCD9335_DATA_HUB_NATIVE_FIFO_SYNC, + mask, 0x0); + __tasha_cdc_native_clk_enable(tasha, false); + /* Adjust interpolator rate to default */ + snd_soc_update_bits(codec, interp_reg, 0x0F, 0x04); + break; + } + + return 0; +} + +static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (!(strcmp(w->name, "RX INT0 INTERP"))) { + reg = WCD9335_CDC_RX0_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX0_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT1 INTERP"))) { + reg = WCD9335_CDC_RX1_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX1_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT2 INTERP"))) { + reg = WCD9335_CDC_RX2_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX2_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT3 INTERP"))) { + reg = WCD9335_CDC_RX3_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX3_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT4 INTERP"))) { + reg = WCD9335_CDC_RX4_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX4_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT5 INTERP"))) { + reg = WCD9335_CDC_RX5_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX5_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT6 INTERP"))) { + reg = WCD9335_CDC_RX6_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX6_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT7 INTERP"))) { + reg = WCD9335_CDC_RX7_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX7_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT8 INTERP"))) { + reg = WCD9335_CDC_RX8_RX_PATH_CTL; + gain_reg = WCD9335_CDC_RX8_RX_VOL_CTL; + } else { + dev_err(codec->dev, "%s: Interpolator reg not found\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!test_bit(SB_CLK_GEAR, &tasha->status_mask)) { + tasha_codec_vote_max_bw(codec, true); + set_bit(SB_CLK_GEAR, &tasha->status_mask); + } + /* Reset if needed */ + tasha_codec_enable_prim_interpolator(codec, reg, event); + break; + case SND_SOC_DAPM_POST_PMU: + tasha_config_compander(codec, w->shift, event); + /* apply gain after int clk is enabled */ + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + tasha_config_compander(codec, w->shift, event); + tasha_codec_enable_prim_interpolator(codec, reg, event); + if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (tasha->comp_enabled[COMPANDER_7] || + tasha->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD9335_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tasha_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int tasha_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "IIR0", sizeof("IIR0"))) { + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)); + } else { + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)); + } + break; + } + return 0; +} + +static int tasha_codec_enable_on_demand_supply( + struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct on_demand_supply *supply; + + if (w->shift >= ON_DEMAND_SUPPLIES_MAX) { + dev_err(codec->dev, "%s: error index > MAX Demand supplies", + __func__); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, "%s: supply: %s event: %d\n", + __func__, on_demand_supply_name[w->shift], event); + + supply = &tasha->on_demand_list[w->shift]; + WARN_ONCE(!supply->supply, "%s isn't defined\n", + on_demand_supply_name[w->shift]); + if (!supply->supply) { + dev_err(codec->dev, "%s: err supply not present ond for %d", + __func__, w->shift); + goto out; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = regulator_enable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to enable %s\n", + __func__, + on_demand_supply_name[w->shift]); + break; + case SND_SOC_DAPM_POST_PMD: + ret = regulator_disable(supply->supply); + if (ret) + dev_err(codec->dev, "%s: Failed to disable %s\n", + __func__, + on_demand_supply_name[w->shift]); + break; + default: + break; + }; + +out: + return ret; +} + +static int tasha_codec_find_amic_input(struct snd_soc_codec *codec, + int adc_mux_n) +{ + u16 mask, shift, adc_mux_in_reg; + u16 amic_mux_sel_reg; + bool is_amic; + + if (adc_mux_n < 0 || adc_mux_n > WCD9335_MAX_VALID_ADC_MUX || + adc_mux_n == WCD9335_INVALID_ADC_MUX) + return 0; + + /* Check whether adc mux input is AMIC or DMIC */ + if (adc_mux_n < 4) { + adc_mux_in_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + 2 * adc_mux_n; + amic_mux_sel_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + mask = 0x03; + shift = 0; + } else { + adc_mux_in_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + amic_mux_sel_reg = adc_mux_in_reg; + mask = 0xC0; + shift = 6; + } + is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask) >> shift) + == 1); + if (!is_amic) + return 0; + + return snd_soc_read(codec, amic_mux_sel_reg) & 0x07; +} + +static void tasha_codec_set_tx_hold(struct snd_soc_codec *codec, + u16 amic_reg, bool set) +{ + u8 mask = 0x20; + u8 val; + + if (amic_reg == WCD9335_ANA_AMIC1 || + amic_reg == WCD9335_ANA_AMIC3 || + amic_reg == WCD9335_ANA_AMIC5) + mask = 0x40; + + val = set ? mask : 0x00; + + switch (amic_reg) { + case WCD9335_ANA_AMIC1: + case WCD9335_ANA_AMIC2: + snd_soc_update_bits(codec, WCD9335_ANA_AMIC2, mask, val); + break; + case WCD9335_ANA_AMIC3: + case WCD9335_ANA_AMIC4: + snd_soc_update_bits(codec, WCD9335_ANA_AMIC4, mask, val); + break; + case WCD9335_ANA_AMIC5: + case WCD9335_ANA_AMIC6: + snd_soc_update_bits(codec, WCD9335_ANA_AMIC6, mask, val); + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic_reg); + break; + } +} + +static int tasha_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int adc_mux_n = w->shift; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int amic_n; + + dev_dbg(codec->dev, "%s: event: %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + amic_n = tasha_codec_find_amic_input(codec, adc_mux_n); + if (amic_n) { + /* + * Prevent ANC Rx pop by leaving Tx FE in HOLD + * state until PA is up. Track AMIC being used + * so we can release the HOLD later. + */ + set_bit(ANC_MIC_AMIC1 + amic_n - 1, + &tasha->status_mask); + } + break; + default: + break; + } + + return 0; +} + +static u16 tasha_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = WCD9335_ANA_AMIC1; + break; + + case 3: + case 4: + pwr_level_reg = WCD9335_ANA_AMIC3; + break; + + case 5: + case 6: + pwr_level_reg = WCD9335_ANA_AMIC5; + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic); + break; + } + + return pwr_level_reg; +} + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +static void tasha_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct tasha_priv *tasha; + struct snd_soc_codec *codec; + u16 dec_cfg_reg, amic_reg; + u8 hpf_cut_off_freq; + int amic_n; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + tasha = hpf_work->tasha; + codec = tasha->codec; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator; + + dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, hpf_cut_off_freq); + + amic_n = tasha_codec_find_amic_input(codec, hpf_work->decimator); + if (amic_n) { + amic_reg = WCD9335_ANA_AMIC1 + amic_n - 1; + tasha_codec_set_tx_hold(codec, amic_reg, false); + } + tasha_codec_vote_max_bw(codec, true); + snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + tasha_codec_vote_max_bw(codec, false); +} + +static void tasha_tx_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct tasha_priv *tasha; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + u16 tx_vol_ctl_reg, hpf_gate_reg; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + tasha = tx_mute_dwork->tasha; + codec = tasha->codec; + + tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + + 16 * tx_mute_dwork->decimator; + hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 + + 16 * tx_mute_dwork->decimator; + snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x01); + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); +} + +static int tasha_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + unsigned int decimator; + char *dec_adc_mux_name = NULL; + char *widget_name = NULL; + char *wname; + int ret = 0, amic_n; + u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + char *dec; + u8 hpf_cut_off_freq; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + wname = widget_name; + dec_adc_mux_name = strsep(&widget_name, " "); + if (!dec_adc_mux_name) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + dec_adc_mux_name = widget_name; + + dec = strpbrk(dec_adc_mux_name, "012345678"); + if (!dec) { + dev_err(codec->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__, + w->name, decimator); + + tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + 16 * decimator; + hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; + dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD9335_CDC_TX0_TX_VOL_CTL + 16 * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + amic_n = tasha_codec_find_amic_input(codec, decimator); + if (amic_n) + pwr_level_reg = tasha_codec_get_amic_pwlvl_reg(codec, + amic_n); + + if (pwr_level_reg) { + switch ((snd_soc_read(codec, pwr_level_reg) & + WCD9335_AMIC_PWR_LVL_MASK) >> + WCD9335_AMIC_PWR_LVL_SHIFT) { + case WCD9335_AMIC_PWR_LEVEL_LP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_LP); + break; + + case WCD9335_AMIC_PWR_LEVEL_HP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_HP); + break; + case WCD9335_AMIC_PWR_LEVEL_DEFAULT: + default: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD9335_DEC_PWR_LVL_MASK, + WCD9335_DEC_PWR_LVL_DF); + break; + } + } + hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + tasha->tx_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + /* Enable TX PGA Mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x00); + + if (decimator == 0) { + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x83); + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0xA3); + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x83); + snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x03); + } + /* schedule work queue to Remove Mute */ + schedule_delayed_work(&tasha->tx_mute_dwork[decimator].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (tasha->tx_hpf_work[decimator].hpf_cut_off_freq != + CF_MIN_3DB_150HZ) + schedule_delayed_work( + &tasha->tx_hpf_work[decimator].dwork, + msecs_to_jiffies(300)); + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + tasha->tx_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + if (cancel_delayed_work_sync( + &tasha->tx_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + tasha_codec_vote_max_bw(codec, true); + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + tasha_codec_vote_max_bw(codec, false); + } + } + cancel_delayed_work_sync( + &tasha->tx_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + break; + }; +out: + kfree(wname); + return ret; +} + +static u32 tasha_get_dmic_sample_rate(struct snd_soc_codec *codec, + unsigned int dmic, struct wcd9xxx_pdata *pdata) +{ + u8 tx_stream_fs; + u8 adc_mux_index = 0, adc_mux_sel = 0; + bool dec_found = false; + u16 adc_mux_ctl_reg, tx_fs_reg; + u32 dmic_fs; + + while (dec_found == 0 && adc_mux_index < WCD9335_MAX_VALID_ADC_MUX) { + if (adc_mux_index < 4) { + adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + (adc_mux_index * 2); + adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) & + 0x78) >> 3) - 1; + } else if (adc_mux_index < 9) { + adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + ((adc_mux_index - 4) * 1); + adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) & + 0x38) >> 3) - 1; + } else if (adc_mux_index == 9) { + ++adc_mux_index; + continue; + } + if (adc_mux_sel == dmic) + dec_found = true; + else + ++adc_mux_index; + } + + if (dec_found == true && adc_mux_index <= 8) { + tx_fs_reg = WCD9335_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index); + tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F; + dmic_fs = tx_stream_fs <= 4 ? WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ : + WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + + /* + * Check for ECPP path selection and DEC1 not connected to + * any other audio path to apply ECPP DMIC sample rate + */ + if ((adc_mux_index == 1) && + ((snd_soc_read(codec, WCD9335_CPE_SS_US_EC_MUX_CFG) + & 0x0F) == 0x0A) && + ((snd_soc_read(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0) + & 0x0C) == 0x00)) { + dmic_fs = pdata->ecpp_dmic_sample_rate; + } + } else { + dmic_fs = pdata->dmic_sample_rate; + } + + return dmic_fs; +} + +static u8 tasha_get_dmic_clk_val(struct snd_soc_codec *codec, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + dev_dbg(codec->dev, + "%s: mclk_rate = %d, dmic_sample_rate = %d\n", + __func__, mclk_rate, dmic_clk_rate); + + /* Default value to return in case of error */ + if (mclk_rate == TASHA_MCLK_CLK_9P6MHZ) + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; + else + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; + + if (dmic_clk_rate == 0) { + dev_err(codec->dev, + "%s: dmic_sample_rate cannot be 0\n", + __func__); + goto done; + } + + div_factor = mclk_rate / dmic_clk_rate; + switch (div_factor) { + case 2: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2; + break; + case 3: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3; + break; + case 4: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_4; + break; + case 6: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_6; + break; + case 8: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_8; + break; + case 16: + dmic_ctl_val = WCD9335_DMIC_CLK_DIV_16; + break; + default: + dev_err(codec->dev, + "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n", + __func__, div_factor, mclk_rate, dmic_clk_rate); + break; + } + +done: + return dmic_ctl_val; +} + +static int tasha_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event:%d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tasha_codec_set_tx_hold(codec, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static int tasha_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 dmic_rate_val, dmic_rate_shift = 1; + unsigned int dmic; + u32 dmic_sample_rate; + int ret; + char *wname; + + wname = strpbrk(w->name, "012345"); + if (!wname) { + dev_err(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(tasha->dmic_0_1_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(tasha->dmic_2_3_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(tasha->dmic_4_5_clk_cnt); + dmic_clk_reg = WCD9335_CPE_SS_DMIC2_CTL; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_sample_rate = tasha_get_dmic_sample_rate(codec, dmic, + pdata); + dmic_rate_val = + tasha_get_dmic_clk_val(codec, + pdata->mclk_rate, + dmic_sample_rate); + + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + dmic_rate_val = + tasha_get_dmic_clk_val(codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + } + break; + }; + + return 0; +} + +static int __tasha_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int micb_num; + + dev_dbg(codec->dev, "%s: wname: %s, event: %d\n", + __func__, w->name, event); + + if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) + micb_num = MIC_BIAS_2; + else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) + micb_num = MIC_BIAS_3; + else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) + micb_num = MIC_BIAS_4; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * MIC BIAS can also be requested by MBHC, + * so use ref count to handle micbias pullup + * and enable requests + */ + tasha_micbias_control(codec, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* wait for cnp time */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + tasha_micbias_control(codec, micb_num, MICB_DISABLE, true); + break; + }; + + return 0; +} + +static int tasha_codec_ldo_h_control(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + tasha->ldo_h_users++; + + if (tasha->ldo_h_users == 1) + snd_soc_update_bits(codec, WCD9335_LDOH_MODE, + 0x80, 0x80); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + tasha->ldo_h_users--; + + if (tasha->ldo_h_users < 0) + tasha->ldo_h_users = 0; + + if (tasha->ldo_h_users == 0) + snd_soc_update_bits(codec, WCD9335_LDOH_MODE, + 0x80, 0x00); + } + + return 0; +} + +static int tasha_codec_force_enable_ldo_h(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_resmgr_enable_master_bias(tasha->resmgr); + tasha_codec_ldo_h_control(w, event); + break; + case SND_SOC_DAPM_POST_PMD: + tasha_codec_ldo_h_control(w, event); + wcd_resmgr_disable_master_bias(tasha->resmgr); + break; + } + + return 0; +} + +static int tasha_codec_force_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_resmgr_enable_master_bias(tasha->resmgr); + tasha_cdc_mclk_enable(codec, true, true); + ret = __tasha_codec_enable_micbias(w, SND_SOC_DAPM_PRE_PMU); + /* Wait for 1ms for better cnp */ + usleep_range(1000, 1100); + tasha_cdc_mclk_enable(codec, false, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = __tasha_codec_enable_micbias(w, SND_SOC_DAPM_POST_PMD); + wcd_resmgr_disable_master_bias(tasha->resmgr); + break; + } + + return ret; +} + +static int tasha_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __tasha_codec_enable_micbias(w, event); +} + +static int tasha_codec_enable_standalone_ldo_h(struct snd_soc_codec *codec, + bool enable) +{ + int rc; + + if (enable) + rc = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + DAPM_LDO_H_STANDALONE); + else + rc = snd_soc_dapm_disable_pin( + snd_soc_codec_get_dapm(codec), + DAPM_LDO_H_STANDALONE); + + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + else + dev_err(codec->dev, "%s: ldo_h force %s pin failed\n", + __func__, (enable ? "enable" : "disable")); + + return rc; +} + +/* + * tasha_codec_enable_standalone_micbias - enable micbias standalone + * @codec: pointer to codec instance + * @micb_num: number of micbias to be enabled + * @enable: true to enable micbias or false to disable + * + * This function is used to enable micbias (1, 2, 3 or 4) during + * standalone independent of whether TX use-case is running or not + * + * Return: error code in case of failure or 0 for success + */ +int tasha_codec_enable_standalone_micbias(struct snd_soc_codec *codec, + int micb_num, + bool enable) +{ + const char * const micb_names[] = { + DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE, + DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE + }; + int micb_index = micb_num - 1; + int rc; + + if (!codec) { + pr_err("%s: Codec memory is NULL\n", __func__); + return -EINVAL; + } + + if ((micb_index < 0) || (micb_index > TASHA_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + if (enable) + rc = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + else + rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + else + dev_err(codec->dev, "%s: micbias%d force %s pin failed\n", + __func__, micb_num, (enable ? "enable" : "disable")); + + return rc; +} +EXPORT_SYMBOL(tasha_codec_enable_standalone_micbias); + +static const char *const tasha_anc_func_text[] = {"OFF", "ON"}; +static const struct soc_enum tasha_anc_func_enum = + SOC_ENUM_SINGLE_EXT(2, tasha_anc_func_text); + +static const char *const tasha_clkmode_text[] = {"EXTERNAL", "INTERNAL"}; +static SOC_ENUM_SINGLE_EXT_DECL(tasha_clkmode_enum, tasha_clkmode_text); + +/* Cutoff frequency for high pass filter */ +static const char * const cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" +}; + +static const char * const rx_cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ", + "CF_NEG_3DB_0P48HZ" +}; + +static const struct soc_enum cf_dec0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX0_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX1_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX2_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX3_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX4_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX5_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX6_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX7_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_dec8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX8_TX_PATH_CFG0, 5, 3, cf_text); + +static const struct soc_enum cf_int0_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int1_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int2_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int3_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int4_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int5_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int5_2_enum, WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int6_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int6_2_enum, WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int7_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct soc_enum cf_int8_1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CFG2, 0, 4, rx_cf_text); + +static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct snd_soc_dapm_route audio_i2s_map[] = { + {"SLIM RX0 MUX", NULL, "RX_I2S_CTL"}, + {"SLIM RX1 MUX", NULL, "RX_I2S_CTL"}, + {"SLIM RX2 MUX", NULL, "RX_I2S_CTL"}, + {"SLIM RX3 MUX", NULL, "RX_I2S_CTL"}, + + {"SLIM TX6 MUX", NULL, "TX_I2S_CTL"}, + {"SLIM TX7 MUX", NULL, "TX_I2S_CTL"}, + {"SLIM TX8 MUX", NULL, "TX_I2S_CTL"}, + {"SLIM TX11 MUX", NULL, "TX_I2S_CTL"}, +}; + +static const struct snd_soc_dapm_route audio_map[] = { + + /* MAD */ + {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"}, + {"MAD_SEL MUX", "MSM", "MADINPUT"}, + {"MADONOFF", "Switch", "MAD_SEL MUX"}, + {"MAD_BROADCAST", "Switch", "MAD_SEL MUX"}, + {"TX13 INP MUX", "CPE_TX_PP", "MADONOFF"}, + + /* CPE HW MAD bypass */ + {"CPE IN Mixer", "MAD_BYPASS", "SLIM TX1 MUX"}, + + {"AIF4_MAD Mixer", "SLIM TX1", "CPE IN Mixer"}, + {"AIF4_MAD Mixer", "SLIM TX12", "MADONOFF"}, + {"AIF4_MAD Mixer", "SLIM TX13", "TX13 INP MUX"}, + {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, + {"AIF4 MAD", NULL, "AIF4"}, + + {"EC BUF MUX INP", "DEC1", "ADC MUX1"}, + {"AIF5 CPE", NULL, "EC BUF MUX INP"}, + + /* SLIMBUS Connections */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + + /* VI Feedback */ + {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"}, + {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"}, + {"AIF4 VI", NULL, "AIF4_VI Mixer"}, + + /* SLIM_MIXER("AIF1_CAP Mixer"),*/ + {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"}, + {"AIF1_CAP Mixer", "SLIM TX13", "TX13 INP MUX"}, + /* SLIM_MIXER("AIF2_CAP Mixer"),*/ + {"AIF2_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"}, + {"AIF2_CAP Mixer", "SLIM TX13", "TX13 INP MUX"}, + /* SLIM_MIXER("AIF3_CAP Mixer"),*/ + {"AIF3_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"}, + {"AIF3_CAP Mixer", "SLIM TX13", "TX13 INP MUX"}, + + {"SLIM TX0 MUX", "DEC0", "ADC MUX0"}, + {"SLIM TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, + {"SLIM TX0 MUX", "DEC0_192", "ADC US MUX0"}, + + {"SLIM TX1 MUX", "DEC1", "ADC MUX1"}, + {"SLIM TX1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"}, + {"SLIM TX1 MUX", "DEC1_192", "ADC US MUX1"}, + + {"SLIM TX2 MUX", "DEC2", "ADC MUX2"}, + {"SLIM TX2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"}, + {"SLIM TX2 MUX", "DEC2_192", "ADC US MUX2"}, + + {"SLIM TX3 MUX", "DEC3", "ADC MUX3"}, + {"SLIM TX3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"}, + {"SLIM TX3 MUX", "DEC3_192", "ADC US MUX3"}, + + {"SLIM TX4 MUX", "DEC4", "ADC MUX4"}, + {"SLIM TX4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"}, + {"SLIM TX4 MUX", "DEC4_192", "ADC US MUX4"}, + + {"SLIM TX5 MUX", "DEC5", "ADC MUX5"}, + {"SLIM TX5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + {"SLIM TX5 MUX", "DEC5_192", "ADC US MUX5"}, + + {"SLIM TX6 MUX", "DEC6", "ADC MUX6"}, + {"SLIM TX6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"}, + {"SLIM TX6 MUX", "DEC6_192", "ADC US MUX6"}, + + {"SLIM TX7 MUX", "DEC7", "ADC MUX7"}, + {"SLIM TX7 MUX", "RX_MIX_TX7", "RX MIX TX7 MUX"}, + {"SLIM TX7 MUX", "DEC7_192", "ADC US MUX7"}, + + {"SLIM TX8 MUX", "DEC8", "ADC MUX8"}, + {"SLIM TX8 MUX", "RX_MIX_TX8", "RX MIX TX8 MUX"}, + {"SLIM TX8 MUX", "DEC8_192", "ADC US MUX8"}, + + {"SLIM TX9 MUX", "DEC7", "ADC MUX7"}, + {"SLIM TX9 MUX", "DEC7_192", "ADC US MUX7"}, + {"SLIM TX10 MUX", "DEC6", "ADC MUX6"}, + {"SLIM TX10 MUX", "DEC6_192", "ADC US MUX6"}, + + {"SLIM TX11 MUX", "DEC_0_5", "SLIM TX11 INP1 MUX"}, + {"SLIM TX11 MUX", "DEC_9_12", "SLIM TX11 INP1 MUX"}, + {"SLIM TX11 INP1 MUX", "DEC0", "ADC MUX0"}, + {"SLIM TX11 INP1 MUX", "DEC1", "ADC MUX1"}, + {"SLIM TX11 INP1 MUX", "DEC2", "ADC MUX2"}, + {"SLIM TX11 INP1 MUX", "DEC3", "ADC MUX3"}, + {"SLIM TX11 INP1 MUX", "DEC4", "ADC MUX4"}, + {"SLIM TX11 INP1 MUX", "DEC5", "ADC MUX5"}, + {"SLIM TX11 INP1 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + + {"TX13 INP MUX", "MAD_BRDCST", "MAD_BROADCAST"}, + {"TX13 INP MUX", "CDC_DEC_5", "SLIM TX13 MUX"}, + {"SLIM TX13 MUX", "DEC5", "ADC MUX5"}, + + {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX0 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX1 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX2 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX3 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX4 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX5 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX6 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX7 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX5", "RX INT5 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX6", "RX INT6 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"}, + {"RX MIX TX8 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"}, + + {"ADC US MUX0", "US_Switch", "ADC MUX0"}, + {"ADC US MUX1", "US_Switch", "ADC MUX1"}, + {"ADC US MUX2", "US_Switch", "ADC MUX2"}, + {"ADC US MUX3", "US_Switch", "ADC MUX3"}, + {"ADC US MUX4", "US_Switch", "ADC MUX4"}, + {"ADC US MUX5", "US_Switch", "ADC MUX5"}, + {"ADC US MUX6", "US_Switch", "ADC MUX6"}, + {"ADC US MUX7", "US_Switch", "ADC MUX7"}, + {"ADC US MUX8", "US_Switch", "ADC MUX8"}, + {"ADC MUX0", "DMIC", "DMIC MUX0"}, + {"ADC MUX0", "AMIC", "AMIC MUX0"}, + {"ADC MUX1", "DMIC", "DMIC MUX1"}, + {"ADC MUX1", "AMIC", "AMIC MUX1"}, + {"ADC MUX2", "DMIC", "DMIC MUX2"}, + {"ADC MUX2", "AMIC", "AMIC MUX2"}, + {"ADC MUX3", "DMIC", "DMIC MUX3"}, + {"ADC MUX3", "AMIC", "AMIC MUX3"}, + {"ADC MUX4", "DMIC", "DMIC MUX4"}, + {"ADC MUX4", "AMIC", "AMIC MUX4"}, + {"ADC MUX5", "DMIC", "DMIC MUX5"}, + {"ADC MUX5", "AMIC", "AMIC MUX5"}, + {"ADC MUX6", "DMIC", "DMIC MUX6"}, + {"ADC MUX6", "AMIC", "AMIC MUX6"}, + {"ADC MUX7", "DMIC", "DMIC MUX7"}, + {"ADC MUX7", "AMIC", "AMIC MUX7"}, + {"ADC MUX8", "DMIC", "DMIC MUX8"}, + {"ADC MUX8", "AMIC", "AMIC MUX8"}, + {"ADC MUX10", "DMIC", "DMIC MUX10"}, + {"ADC MUX10", "AMIC", "AMIC MUX10"}, + {"ADC MUX11", "DMIC", "DMIC MUX11"}, + {"ADC MUX11", "AMIC", "AMIC MUX11"}, + {"ADC MUX12", "DMIC", "DMIC MUX12"}, + {"ADC MUX12", "AMIC", "AMIC MUX12"}, + {"ADC MUX13", "DMIC", "DMIC MUX13"}, + {"ADC MUX13", "AMIC", "AMIC MUX13"}, + + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"}, + + {"DMIC MUX0", "DMIC0", "DMIC0"}, + {"DMIC MUX0", "DMIC1", "DMIC1"}, + {"DMIC MUX0", "DMIC2", "DMIC2"}, + {"DMIC MUX0", "DMIC3", "DMIC3"}, + {"DMIC MUX0", "DMIC4", "DMIC4"}, + {"DMIC MUX0", "DMIC5", "DMIC5"}, + {"AMIC MUX0", "ADC1", "ADC1"}, + {"AMIC MUX0", "ADC2", "ADC2"}, + {"AMIC MUX0", "ADC3", "ADC3"}, + {"AMIC MUX0", "ADC4", "ADC4"}, + {"AMIC MUX0", "ADC5", "ADC5"}, + {"AMIC MUX0", "ADC6", "ADC6"}, + + {"DMIC MUX1", "DMIC0", "DMIC0"}, + {"DMIC MUX1", "DMIC1", "DMIC1"}, + {"DMIC MUX1", "DMIC2", "DMIC2"}, + {"DMIC MUX1", "DMIC3", "DMIC3"}, + {"DMIC MUX1", "DMIC4", "DMIC4"}, + {"DMIC MUX1", "DMIC5", "DMIC5"}, + {"AMIC MUX1", "ADC1", "ADC1"}, + {"AMIC MUX1", "ADC2", "ADC2"}, + {"AMIC MUX1", "ADC3", "ADC3"}, + {"AMIC MUX1", "ADC4", "ADC4"}, + {"AMIC MUX1", "ADC5", "ADC5"}, + {"AMIC MUX1", "ADC6", "ADC6"}, + + {"DMIC MUX2", "DMIC0", "DMIC0"}, + {"DMIC MUX2", "DMIC1", "DMIC1"}, + {"DMIC MUX2", "DMIC2", "DMIC2"}, + {"DMIC MUX2", "DMIC3", "DMIC3"}, + {"DMIC MUX2", "DMIC4", "DMIC4"}, + {"DMIC MUX2", "DMIC5", "DMIC5"}, + {"AMIC MUX2", "ADC1", "ADC1"}, + {"AMIC MUX2", "ADC2", "ADC2"}, + {"AMIC MUX2", "ADC3", "ADC3"}, + {"AMIC MUX2", "ADC4", "ADC4"}, + {"AMIC MUX2", "ADC5", "ADC5"}, + {"AMIC MUX2", "ADC6", "ADC6"}, + + {"DMIC MUX3", "DMIC0", "DMIC0"}, + {"DMIC MUX3", "DMIC1", "DMIC1"}, + {"DMIC MUX3", "DMIC2", "DMIC2"}, + {"DMIC MUX3", "DMIC3", "DMIC3"}, + {"DMIC MUX3", "DMIC4", "DMIC4"}, + {"DMIC MUX3", "DMIC5", "DMIC5"}, + {"AMIC MUX3", "ADC1", "ADC1"}, + {"AMIC MUX3", "ADC2", "ADC2"}, + {"AMIC MUX3", "ADC3", "ADC3"}, + {"AMIC MUX3", "ADC4", "ADC4"}, + {"AMIC MUX3", "ADC5", "ADC5"}, + {"AMIC MUX3", "ADC6", "ADC6"}, + + {"DMIC MUX4", "DMIC0", "DMIC0"}, + {"DMIC MUX4", "DMIC1", "DMIC1"}, + {"DMIC MUX4", "DMIC2", "DMIC2"}, + {"DMIC MUX4", "DMIC3", "DMIC3"}, + {"DMIC MUX4", "DMIC4", "DMIC4"}, + {"DMIC MUX4", "DMIC5", "DMIC5"}, + {"AMIC MUX4", "ADC1", "ADC1"}, + {"AMIC MUX4", "ADC2", "ADC2"}, + {"AMIC MUX4", "ADC3", "ADC3"}, + {"AMIC MUX4", "ADC4", "ADC4"}, + {"AMIC MUX4", "ADC5", "ADC5"}, + {"AMIC MUX4", "ADC6", "ADC6"}, + + {"DMIC MUX5", "DMIC0", "DMIC0"}, + {"DMIC MUX5", "DMIC1", "DMIC1"}, + {"DMIC MUX5", "DMIC2", "DMIC2"}, + {"DMIC MUX5", "DMIC3", "DMIC3"}, + {"DMIC MUX5", "DMIC4", "DMIC4"}, + {"DMIC MUX5", "DMIC5", "DMIC5"}, + {"AMIC MUX5", "ADC1", "ADC1"}, + {"AMIC MUX5", "ADC2", "ADC2"}, + {"AMIC MUX5", "ADC3", "ADC3"}, + {"AMIC MUX5", "ADC4", "ADC4"}, + {"AMIC MUX5", "ADC5", "ADC5"}, + {"AMIC MUX5", "ADC6", "ADC6"}, + + {"DMIC MUX6", "DMIC0", "DMIC0"}, + {"DMIC MUX6", "DMIC1", "DMIC1"}, + {"DMIC MUX6", "DMIC2", "DMIC2"}, + {"DMIC MUX6", "DMIC3", "DMIC3"}, + {"DMIC MUX6", "DMIC4", "DMIC4"}, + {"DMIC MUX6", "DMIC5", "DMIC5"}, + {"AMIC MUX6", "ADC1", "ADC1"}, + {"AMIC MUX6", "ADC2", "ADC2"}, + {"AMIC MUX6", "ADC3", "ADC3"}, + {"AMIC MUX6", "ADC4", "ADC4"}, + {"AMIC MUX6", "ADC5", "ADC5"}, + {"AMIC MUX6", "ADC6", "ADC6"}, + + {"DMIC MUX7", "DMIC0", "DMIC0"}, + {"DMIC MUX7", "DMIC1", "DMIC1"}, + {"DMIC MUX7", "DMIC2", "DMIC2"}, + {"DMIC MUX7", "DMIC3", "DMIC3"}, + {"DMIC MUX7", "DMIC4", "DMIC4"}, + {"DMIC MUX7", "DMIC5", "DMIC5"}, + {"AMIC MUX7", "ADC1", "ADC1"}, + {"AMIC MUX7", "ADC2", "ADC2"}, + {"AMIC MUX7", "ADC3", "ADC3"}, + {"AMIC MUX7", "ADC4", "ADC4"}, + {"AMIC MUX7", "ADC5", "ADC5"}, + {"AMIC MUX7", "ADC6", "ADC6"}, + + {"DMIC MUX8", "DMIC0", "DMIC0"}, + {"DMIC MUX8", "DMIC1", "DMIC1"}, + {"DMIC MUX8", "DMIC2", "DMIC2"}, + {"DMIC MUX8", "DMIC3", "DMIC3"}, + {"DMIC MUX8", "DMIC4", "DMIC4"}, + {"DMIC MUX8", "DMIC5", "DMIC5"}, + {"AMIC MUX8", "ADC1", "ADC1"}, + {"AMIC MUX8", "ADC2", "ADC2"}, + {"AMIC MUX8", "ADC3", "ADC3"}, + {"AMIC MUX8", "ADC4", "ADC4"}, + {"AMIC MUX8", "ADC5", "ADC5"}, + {"AMIC MUX8", "ADC6", "ADC6"}, + + {"DMIC MUX10", "DMIC0", "DMIC0"}, + {"DMIC MUX10", "DMIC1", "DMIC1"}, + {"DMIC MUX10", "DMIC2", "DMIC2"}, + {"DMIC MUX10", "DMIC3", "DMIC3"}, + {"DMIC MUX10", "DMIC4", "DMIC4"}, + {"DMIC MUX10", "DMIC5", "DMIC5"}, + {"AMIC MUX10", "ADC1", "ADC1"}, + {"AMIC MUX10", "ADC2", "ADC2"}, + {"AMIC MUX10", "ADC3", "ADC3"}, + {"AMIC MUX10", "ADC4", "ADC4"}, + {"AMIC MUX10", "ADC5", "ADC5"}, + {"AMIC MUX10", "ADC6", "ADC6"}, + + {"DMIC MUX11", "DMIC0", "DMIC0"}, + {"DMIC MUX11", "DMIC1", "DMIC1"}, + {"DMIC MUX11", "DMIC2", "DMIC2"}, + {"DMIC MUX11", "DMIC3", "DMIC3"}, + {"DMIC MUX11", "DMIC4", "DMIC4"}, + {"DMIC MUX11", "DMIC5", "DMIC5"}, + {"AMIC MUX11", "ADC1", "ADC1"}, + {"AMIC MUX11", "ADC2", "ADC2"}, + {"AMIC MUX11", "ADC3", "ADC3"}, + {"AMIC MUX11", "ADC4", "ADC4"}, + {"AMIC MUX11", "ADC5", "ADC5"}, + {"AMIC MUX11", "ADC6", "ADC6"}, + + {"DMIC MUX12", "DMIC0", "DMIC0"}, + {"DMIC MUX12", "DMIC1", "DMIC1"}, + {"DMIC MUX12", "DMIC2", "DMIC2"}, + {"DMIC MUX12", "DMIC3", "DMIC3"}, + {"DMIC MUX12", "DMIC4", "DMIC4"}, + {"DMIC MUX12", "DMIC5", "DMIC5"}, + {"AMIC MUX12", "ADC1", "ADC1"}, + {"AMIC MUX12", "ADC2", "ADC2"}, + {"AMIC MUX12", "ADC3", "ADC3"}, + {"AMIC MUX12", "ADC4", "ADC4"}, + {"AMIC MUX12", "ADC5", "ADC5"}, + {"AMIC MUX12", "ADC6", "ADC6"}, + + {"DMIC MUX13", "DMIC0", "DMIC0"}, + {"DMIC MUX13", "DMIC1", "DMIC1"}, + {"DMIC MUX13", "DMIC2", "DMIC2"}, + {"DMIC MUX13", "DMIC3", "DMIC3"}, + {"DMIC MUX13", "DMIC4", "DMIC4"}, + {"DMIC MUX13", "DMIC5", "DMIC5"}, + {"AMIC MUX13", "ADC1", "ADC1"}, + {"AMIC MUX13", "ADC2", "ADC2"}, + {"AMIC MUX13", "ADC3", "ADC3"}, + {"AMIC MUX13", "ADC4", "ADC4"}, + {"AMIC MUX13", "ADC5", "ADC5"}, + {"AMIC MUX13", "ADC6", "ADC6"}, + /* ADC Connections */ + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4"}, + {"ADC5", NULL, "AMIC5"}, + {"ADC6", NULL, "AMIC6"}, + + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP0"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP1"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP2"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP0"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP1"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP2"}, + {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP0"}, + {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP1"}, + {"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP2"}, + {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP0"}, + {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP1"}, + {"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP2"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP1"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP2"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP0"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP1"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP2"}, + + {"RX INT0 SEC MIX", NULL, "RX INT0_1 MIX1"}, + {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"}, + {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"}, + {"RX INT0 INTERP", NULL, "RX INT0 MIX2"}, + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 INTERP"}, + {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"}, + {"RX INT0 DAC", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "RX INT0 DAC"}, + {"EAR", NULL, "EAR PA"}, + + {"SPL SRC0 MUX", "SRC_IN_HPHL", "RX INT1_1 MIX1"}, + {"RX INT1 SPLINE MIX", NULL, "RX INT1_1 MIX1"}, + {"RX INT1 SPLINE MIX", "HPHL Switch", "SPL SRC0 MUX"}, + {"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"}, + {"RX INT1 SPLINE MIX", NULL, "RX INT1_1 NATIVE MUX"}, + {"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, + {"RX INT1 SEC MIX", NULL, "RX INT1 SPLINE MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"}, + {"RX INT1 INTERP", NULL, "RX INT1 MIX2"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 INTERP"}, + {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"}, + {"RX INT1 DAC", NULL, "RX_BIAS"}, + {"HPHL PA", NULL, "RX INT1 DAC"}, + {"HPHL", NULL, "HPHL PA"}, + + {"SPL SRC1 MUX", "SRC_IN_HPHR", "RX INT2_1 MIX1"}, + {"RX INT2 SPLINE MIX", NULL, "RX INT2_1 MIX1"}, + {"RX INT2 SPLINE MIX", "HPHR Switch", "SPL SRC1 MUX"}, + {"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"}, + {"RX INT2 SPLINE MIX", NULL, "RX INT2_1 NATIVE MUX"}, + {"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, + {"RX INT2 SEC MIX", NULL, "RX INT2 SPLINE MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"}, + {"RX INT2 INTERP", NULL, "RX INT2 MIX2"}, + {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 INTERP"}, + {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"}, + {"RX INT2 DAC", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "RX INT2 DAC"}, + {"HPHR", NULL, "HPHR PA"}, + + {"SPL SRC0 MUX", "SRC_IN_LO1", "RX INT3_1 MIX1"}, + {"RX INT3 SPLINE MIX", NULL, "RX INT3_1 MIX1"}, + {"RX INT3 SPLINE MIX", "LO1 Switch", "SPL SRC0 MUX"}, + {"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"}, + {"RX INT3 SPLINE MIX", NULL, "RX INT3_1 NATIVE MUX"}, + {"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, + {"RX INT3 SEC MIX", NULL, "RX INT3 SPLINE MIX"}, + {"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"}, + {"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"}, + {"RX INT3 INTERP", NULL, "RX INT3 MIX2"}, + {"RX INT3 DAC", NULL, "RX INT3 INTERP"}, + {"RX INT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + + {"SPL SRC1 MUX", "SRC_IN_LO2", "RX INT4_1 MIX1"}, + {"RX INT4 SPLINE MIX", NULL, "RX INT4_1 MIX1"}, + {"RX INT4 SPLINE MIX", "LO2 Switch", "SPL SRC1 MUX"}, + {"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"}, + {"RX INT4 SPLINE MIX", NULL, "RX INT4_1 NATIVE MUX"}, + {"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, + {"RX INT4 SEC MIX", NULL, "RX INT4 SPLINE MIX"}, + {"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"}, + {"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"}, + {"RX INT4 INTERP", NULL, "RX INT4 MIX2"}, + {"RX INT4 DAC", NULL, "RX INT4 INTERP"}, + {"RX INT4 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + + {"SPL SRC2 MUX", "SRC_IN_LO3", "RX INT5_1 MIX1"}, + {"RX INT5 SPLINE MIX", NULL, "RX INT5_1 MIX1"}, + {"RX INT5 SPLINE MIX", "LO3 Switch", "SPL SRC2 MUX"}, + {"RX INT5 SEC MIX", NULL, "RX INT5 SPLINE MIX"}, + {"RX INT5 MIX2", NULL, "RX INT5 SEC MIX"}, + {"RX INT5 INTERP", NULL, "RX INT5 MIX2"}, + + {"RX INT5 VBAT", "LO3 VBAT Enable", "RX INT5 INTERP"}, + {"RX INT5 DAC", NULL, "RX INT5 VBAT"}, + + {"RX INT5 DAC", NULL, "RX INT5 INTERP"}, + {"RX INT5 DAC", NULL, "RX_BIAS"}, + {"LINEOUT3 PA", NULL, "RX INT5 DAC"}, + {"LINEOUT3", NULL, "LINEOUT3 PA"}, + + {"SPL SRC3 MUX", "SRC_IN_LO4", "RX INT6_1 MIX1"}, + {"RX INT6 SPLINE MIX", NULL, "RX INT6_1 MIX1"}, + {"RX INT6 SPLINE MIX", "LO4 Switch", "SPL SRC3 MUX"}, + {"RX INT6 SEC MIX", NULL, "RX INT6 SPLINE MIX"}, + {"RX INT6 MIX2", NULL, "RX INT6 SEC MIX"}, + {"RX INT6 INTERP", NULL, "RX INT6 MIX2"}, + + {"RX INT6 VBAT", "LO4 VBAT Enable", "RX INT6 INTERP"}, + {"RX INT6 DAC", NULL, "RX INT6 VBAT"}, + + {"RX INT6 DAC", NULL, "RX INT6 INTERP"}, + {"RX INT6 DAC", NULL, "RX_BIAS"}, + {"LINEOUT4 PA", NULL, "RX INT6 DAC"}, + {"LINEOUT4", NULL, "LINEOUT4 PA"}, + + {"SPL SRC2 MUX", "SRC_IN_SPKRL", "RX INT7_1 MIX1"}, + {"RX INT7 SPLINE MIX", NULL, "RX INT7_1 MIX1"}, + {"RX INT7 SPLINE MIX", "SPKRL Switch", "SPL SRC2 MUX"}, + {"RX INT7 SEC MIX", NULL, "RX INT7 SPLINE MIX"}, + {"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"}, + {"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"}, + + {"RX INT7 INTERP", NULL, "RX INT7 MIX2"}, + + {"RX INT7 VBAT", "SPKRL VBAT Enable", "RX INT7 INTERP"}, + {"RX INT7 CHAIN", NULL, "RX INT7 VBAT"}, + + {"RX INT7 CHAIN", NULL, "RX INT7 INTERP"}, + {"RX INT7 CHAIN", NULL, "RX_BIAS"}, + {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + + {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"}, + {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"}, + {"SPK1 OUT", NULL, "ANC SPK1 PA"}, + + {"SPL SRC3 MUX", "SRC_IN_SPKRR", "RX INT8_1 MIX1"}, + {"RX INT8 SPLINE MIX", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 SPLINE MIX", "SPKRR Switch", "SPL SRC3 MUX"}, + {"RX INT8 SEC MIX", NULL, "RX INT8 SPLINE MIX"}, + {"RX INT8 INTERP", NULL, "RX INT8 SEC MIX"}, + + {"RX INT8 VBAT", "SPKRR VBAT Enable", "RX INT8 INTERP"}, + {"RX INT8 CHAIN", NULL, "RX INT8 VBAT"}, + + {"RX INT8 CHAIN", NULL, "RX INT8 INTERP"}, + {"RX INT8 CHAIN", NULL, "RX_BIAS"}, + {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, + + {"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"}, + + {"ANC HPHL Enable", "Switch", "ADC MUX10"}, + {"ANC HPHL Enable", "Switch", "ADC MUX11"}, + {"RX INT1 MIX2", NULL, "ANC HPHL Enable"}, + + {"ANC HPHR Enable", "Switch", "ADC MUX12"}, + {"ANC HPHR Enable", "Switch", "ADC MUX13"}, + {"RX INT2 MIX2", NULL, "ANC HPHR Enable"}, + + {"ANC EAR Enable", "Switch", "ADC MUX10"}, + {"ANC EAR Enable", "Switch", "ADC MUX11"}, + {"RX INT0 MIX2", NULL, "ANC EAR Enable"}, + + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"}, + {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"}, + + {"ANC LINEOUT1 Enable", "Switch", "ADC MUX10"}, + {"ANC LINEOUT1 Enable", "Switch", "ADC MUX11"}, + {"RX INT3 MIX2", NULL, "ANC LINEOUT1 Enable"}, + + {"ANC LINEOUT2 Enable", "Switch", "ADC MUX12"}, + {"ANC LINEOUT2 Enable", "Switch", "ADC MUX13"}, + {"RX INT4 MIX2", NULL, "ANC LINEOUT2 Enable"}, + + {"ANC EAR PA", NULL, "RX INT0 DAC"}, + {"ANC EAR", NULL, "ANC EAR PA"}, + {"ANC HPHL PA", NULL, "RX INT1 DAC"}, + {"ANC HPHL", NULL, "ANC HPHL PA"}, + {"ANC HPHR PA", NULL, "RX INT2 DAC"}, + {"ANC HPHR", NULL, "ANC HPHR PA"}, + {"ANC LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"ANC LINEOUT1", NULL, "ANC LINEOUT1 PA"}, + {"ANC LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"ANC LINEOUT2", NULL, "ANC LINEOUT2 PA"}, + + /* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/ + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"}, + /* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/ + {"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"}, + /* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/ + {"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"}, + /* SLIM_MUX("AIF4_PB", "AIF4 PB"),*/ + {"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"}, + + /* SLIM_MUX("AIF_MIX1_PB", "AIF MIX1 PB"),*/ + {"SLIM RX0 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX1 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX2 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX3 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX4 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX5 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX6 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + {"SLIM RX7 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"}, + + {"SLIM RX0", NULL, "SLIM RX0 MUX"}, + {"SLIM RX1", NULL, "SLIM RX1 MUX"}, + {"SLIM RX2", NULL, "SLIM RX2 MUX"}, + {"SLIM RX3", NULL, "SLIM RX3 MUX"}, + {"SLIM RX4", NULL, "SLIM RX4 MUX"}, + {"SLIM RX5", NULL, "SLIM RX5 MUX"}, + {"SLIM RX6", NULL, "SLIM RX6 MUX"}, + {"SLIM RX7", NULL, "SLIM RX7 MUX"}, + + {"RX INT0_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT0_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT0_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT0_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT0_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT0_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT0_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT0_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT0_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT0_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT0_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT0_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT0_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT0_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT0_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT0_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT0_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT0_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT0_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT0_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT0_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT0_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"}, + + /* MIXing path INT0 */ + {"RX INT0_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT0_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT0_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT0_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT0_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT0_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT0_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT0_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_2 MUX"}, + + /* MIXing path INT1 */ + {"RX INT1_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT1_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT1_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT1_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT1_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT1_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT1_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT1_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_2 MUX"}, + + /* MIXing path INT2 */ + {"RX INT2_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT2_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT2_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT2_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT2_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT2_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT2_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT2_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_2 MUX"}, + + /* MIXing path INT3 */ + {"RX INT3_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT3_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT3_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT3_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT3_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT3_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT3_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT3_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT3 SEC MIX", NULL, "RX INT3_2 MUX"}, + + /* MIXing path INT4 */ + {"RX INT4_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT4_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT4_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT4_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT4_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT4_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT4_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT4_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_2 MUX"}, + + /* MIXing path INT5 */ + {"RX INT5_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT5_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT5_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT5_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT5_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT5_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT5_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT5_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT5 SEC MIX", NULL, "RX INT5_2 MUX"}, + + /* MIXing path INT6 */ + {"RX INT6_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT6_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT6_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT6_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT6_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT6_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT6_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT6_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT6 SEC MIX", NULL, "RX INT6_2 MUX"}, + + /* MIXing path INT7 */ + {"RX INT7_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT7_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT7_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT7_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT7_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT7_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT7_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT7_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_2 MUX"}, + + /* MIXing path INT8 */ + {"RX INT8_2 MUX", "RX0", "SLIM RX0"}, + {"RX INT8_2 MUX", "RX1", "SLIM RX1"}, + {"RX INT8_2 MUX", "RX2", "SLIM RX2"}, + {"RX INT8_2 MUX", "RX3", "SLIM RX3"}, + {"RX INT8_2 MUX", "RX4", "SLIM RX4"}, + {"RX INT8_2 MUX", "RX5", "SLIM RX5"}, + {"RX INT8_2 MUX", "RX6", "SLIM RX6"}, + {"RX INT8_2 MUX", "RX7", "SLIM RX7"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_2 MUX"}, + + {"RX INT1_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT1_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT1_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT1_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT1_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT1_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT1_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT1_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT1_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT1_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT1_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT1_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT1_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT1_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT1_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT1_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT1_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT1_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT1_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT1_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT1_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT1_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT2_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT2_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT2_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT2_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT2_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT2_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT2_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT2_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT2_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT2_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT2_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT2_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT2_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT2_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT2_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT2_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT2_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT2_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT2_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT2_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT2_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT3_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT3_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT3_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT3_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT3_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT3_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT3_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT3_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT3_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT3_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT3_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT3_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT3_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT3_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT3_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT3_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT3_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT3_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT3_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT3_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT3_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT3_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT3_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT3_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT3_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT4_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT4_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT4_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT4_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT4_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT4_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT4_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT4_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT4_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT4_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT4_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT4_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT4_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT4_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT4_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT4_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT4_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT4_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT4_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT4_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT4_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT4_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT4_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT4_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT4_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT5_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT5_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT5_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT5_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT5_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT5_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT5_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT5_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT5_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT5_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT5_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT5_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT5_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT5_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT5_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT5_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT5_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT5_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT5_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT5_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT5_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT5_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT5_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT5_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT5_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT5_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT5_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT5_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT5_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT5_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT6_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT6_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT6_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT6_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT6_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT6_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT6_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT6_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT6_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT6_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT6_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT6_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT6_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT6_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT6_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT6_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT6_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT6_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT6_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT6_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT6_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT6_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT6_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT6_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT6_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT6_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT6_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT6_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT6_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT6_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT7_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT7_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT7_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT7_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT7_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT7_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT7_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT7_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT7_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT7_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT7_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT7_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT7_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT7_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT7_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT7_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT7_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT7_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT7_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT7_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT7_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT7_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT7_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT7_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT7_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT8_1 MIX1 INP0", "RX0", "SLIM RX0"}, + {"RX INT8_1 MIX1 INP0", "RX1", "SLIM RX1"}, + {"RX INT8_1 MIX1 INP0", "RX2", "SLIM RX2"}, + {"RX INT8_1 MIX1 INP0", "RX3", "SLIM RX3"}, + {"RX INT8_1 MIX1 INP0", "RX4", "SLIM RX4"}, + {"RX INT8_1 MIX1 INP0", "RX5", "SLIM RX5"}, + {"RX INT8_1 MIX1 INP0", "RX6", "SLIM RX6"}, + {"RX INT8_1 MIX1 INP0", "RX7", "SLIM RX7"}, + {"RX INT8_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP1", "RX0", "SLIM RX0"}, + {"RX INT8_1 MIX1 INP1", "RX1", "SLIM RX1"}, + {"RX INT8_1 MIX1 INP1", "RX2", "SLIM RX2"}, + {"RX INT8_1 MIX1 INP1", "RX3", "SLIM RX3"}, + {"RX INT8_1 MIX1 INP1", "RX4", "SLIM RX4"}, + {"RX INT8_1 MIX1 INP1", "RX5", "SLIM RX5"}, + {"RX INT8_1 MIX1 INP1", "RX6", "SLIM RX6"}, + {"RX INT8_1 MIX1 INP1", "RX7", "SLIM RX7"}, + {"RX INT8_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP2", "RX0", "SLIM RX0"}, + {"RX INT8_1 MIX1 INP2", "RX1", "SLIM RX1"}, + {"RX INT8_1 MIX1 INP2", "RX2", "SLIM RX2"}, + {"RX INT8_1 MIX1 INP2", "RX3", "SLIM RX3"}, + {"RX INT8_1 MIX1 INP2", "RX4", "SLIM RX4"}, + {"RX INT8_1 MIX1 INP2", "RX5", "SLIM RX5"}, + {"RX INT8_1 MIX1 INP2", "RX6", "SLIM RX6"}, + {"RX INT8_1 MIX1 INP2", "RX7", "SLIM RX7"}, + {"RX INT8_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP2", "IIR1", "IIR1"}, + + /* SRC0, SRC1 inputs to Sidetone RX Mixer + * on RX0, RX1, RX2, RX3, RX4 and RX7 chains + */ + {"IIR0", NULL, "IIR0 INP0 MUX"}, + {"IIR0 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP0 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP0 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP0 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP0 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP0 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP0 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP0 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP0 MUX", "RX7", "SLIM RX7"}, + {"IIR0", NULL, "IIR0 INP1 MUX"}, + {"IIR0 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP1 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP1 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP1 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP1 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP1 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP1 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP1 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP1 MUX", "RX7", "SLIM RX7"}, + {"IIR0", NULL, "IIR0 INP2 MUX"}, + {"IIR0 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP2 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP2 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP2 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP2 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP2 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP2 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP2 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP2 MUX", "RX7", "SLIM RX7"}, + {"IIR0", NULL, "IIR0 INP3 MUX"}, + {"IIR0 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP3 MUX", "RX0", "SLIM RX0"}, + {"IIR0 INP3 MUX", "RX1", "SLIM RX1"}, + {"IIR0 INP3 MUX", "RX2", "SLIM RX2"}, + {"IIR0 INP3 MUX", "RX3", "SLIM RX3"}, + {"IIR0 INP3 MUX", "RX4", "SLIM RX4"}, + {"IIR0 INP3 MUX", "RX5", "SLIM RX5"}, + {"IIR0 INP3 MUX", "RX6", "SLIM RX6"}, + {"IIR0 INP3 MUX", "RX7", "SLIM RX7"}, + + {"IIR1", NULL, "IIR1 INP0 MUX"}, + {"IIR1 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP0 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP0 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP0 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP0 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP0 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP0 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP0 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP0 MUX", "RX7", "SLIM RX7"}, + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP1 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP1 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP1 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP1 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP1 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP1 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP1 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP1 MUX", "RX7", "SLIM RX7"}, + {"IIR1", NULL, "IIR1 INP2 MUX"}, + {"IIR1 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP2 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP2 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP2 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP2 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP2 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP2 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP2 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP2 MUX", "RX7", "SLIM RX7"}, + {"IIR1", NULL, "IIR1 INP3 MUX"}, + {"IIR1 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP3 MUX", "RX0", "SLIM RX0"}, + {"IIR1 INP3 MUX", "RX1", "SLIM RX1"}, + {"IIR1 INP3 MUX", "RX2", "SLIM RX2"}, + {"IIR1 INP3 MUX", "RX3", "SLIM RX3"}, + {"IIR1 INP3 MUX", "RX4", "SLIM RX4"}, + {"IIR1 INP3 MUX", "RX5", "SLIM RX5"}, + {"IIR1 INP3 MUX", "RX6", "SLIM RX6"}, + {"IIR1 INP3 MUX", "RX7", "SLIM RX7"}, + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, + {"RX INT0 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT0 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT1 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT1 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT2 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT2 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT3 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT3 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT4 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT4 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT7 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT7 MIX2 INP", "SRC1", "SRC1"}, +}; + +static int tasha_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 amic_reg; + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC3; + if (!strcmp(kcontrol->id.name, "AMIC_5_6 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC5; + + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, amic_reg) & WCD9335_AMIC_PWR_LVL_MASK) >> + WCD9335_AMIC_PWR_LVL_SHIFT; + + return 0; +} + +static int tasha_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u32 mode_val; + u16 amic_reg; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", + __func__, mode_val); + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC3; + if (!strcmp(kcontrol->id.name, "AMIC_5_6 PWR MODE")) + amic_reg = WCD9335_ANA_AMIC5; + + snd_soc_update_bits(codec, amic_reg, WCD9335_AMIC_PWR_LVL_MASK, + mode_val << WCD9335_AMIC_PWR_LVL_SHIFT); + + return 0; +} + +static int tasha_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->hph_mode; + return 0; +} + +static int tasha_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", + __func__, mode_val); + + if (mode_val == 0) { + dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H HiFi\n", + __func__); + mode_val = CLS_H_HIFI; + } + tasha->hph_mode = mode_val; + return 0; +} + +static const char *const tasha_conn_mad_text[] = { + "NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6", + "NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", + "DMIC5", "NOTUSED3", "NOTUSED4" +}; + +static const struct soc_enum tasha_conn_mad_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_conn_mad_text), + tasha_conn_mad_text); + +static int tasha_enable_ldo_h_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 val = 0; + + if (codec) + val = snd_soc_read(codec, WCD9335_LDOH_MODE) & 0x80; + + ucontrol->value.integer.value[0] = !!val; + + return 0; +} + +static int tasha_enable_ldo_h_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int value = ucontrol->value.integer.value[0]; + bool enable; + + enable = !!value; + if (codec) + tasha_codec_enable_standalone_ldo_h(codec, enable); + + return 0; +} + +static int tasha_mad_input_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 tasha_mad_input; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + tasha_mad_input = snd_soc_read(codec, + WCD9335_SOC_MAD_INP_SEL) & 0x0F; + ucontrol->value.integer.value[0] = tasha_mad_input; + + dev_dbg(codec->dev, + "%s: tasha_mad_input = %s\n", __func__, + tasha_conn_mad_text[tasha_mad_input]); + return 0; +} + +static int tasha_mad_input_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 tasha_mad_input; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + char mad_amic_input_widget[6]; + const char *mad_input_widget; + const char *source_widget = NULL; + u32 adc, i, mic_bias_found = 0; + int ret = 0; + char *mad_input; + + tasha_mad_input = ucontrol->value.integer.value[0]; + + if (tasha_mad_input >= ARRAY_SIZE(tasha_conn_mad_text)) { + dev_err(codec->dev, + "%s: tasha_mad_input = %d out of bounds\n", + __func__, tasha_mad_input); + return -EINVAL; + } + + if (!strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED1") || + !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED2") || + !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED3") || + !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED4")) { + dev_err(codec->dev, + "%s: Unsupported tasha_mad_input = %s\n", + __func__, tasha_conn_mad_text[tasha_mad_input]); + return -EINVAL; + } + + if (strnstr(tasha_conn_mad_text[tasha_mad_input], + "ADC", sizeof("ADC"))) { + mad_input = strpbrk(tasha_conn_mad_text[tasha_mad_input], + "123456"); + if (!mad_input) { + dev_err(codec->dev, "%s: Invalid MAD input %s\n", + __func__, + tasha_conn_mad_text[tasha_mad_input]); + return -EINVAL; + } + ret = kstrtouint(mad_input, 10, &adc); + if ((ret < 0) || (adc > 6)) { + dev_err(codec->dev, + "%s: Invalid ADC = %s\n", __func__, + tasha_conn_mad_text[tasha_mad_input]); + ret = -EINVAL; + } + + snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc); + + mad_input_widget = mad_amic_input_widget; + } else { + /* DMIC type input widget*/ + mad_input_widget = tasha_conn_mad_text[tasha_mad_input]; + } + + dev_dbg(codec->dev, + "%s: tasha input widget = %s\n", __func__, + mad_input_widget); + + for (i = 0; i < card->num_of_dapm_routes; i++) { + if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { + source_widget = card->of_dapm_routes[i].source; + if (!source_widget) { + dev_err(codec->dev, + "%s: invalid source widget\n", + __func__); + return -EINVAL; + } + + if (strnstr(source_widget, + "MIC BIAS1", sizeof("MIC BIAS1"))) { + mic_bias_found = 1; + break; + } else if (strnstr(source_widget, + "MIC BIAS2", sizeof("MIC BIAS2"))) { + mic_bias_found = 2; + break; + } else if (strnstr(source_widget, + "MIC BIAS3", sizeof("MIC BIAS3"))) { + mic_bias_found = 3; + break; + } else if (strnstr(source_widget, + "MIC BIAS4", sizeof("MIC BIAS4"))) { + mic_bias_found = 4; + break; + } + } + } + + if (!mic_bias_found) { + dev_err(codec->dev, + "%s: mic bias source not found for input = %s\n", + __func__, mad_input_widget); + return -EINVAL; + } + + dev_dbg(codec->dev, + "%s: mic_bias found = %d\n", __func__, + mic_bias_found); + + snd_soc_update_bits(codec, WCD9335_SOC_MAD_INP_SEL, + 0x0F, tasha_mad_input); + snd_soc_update_bits(codec, WCD9335_ANA_MAD_SETUP, + 0x07, mic_bias_found); + + return 0; +} + +static int tasha_pinctl_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 ctl_reg; + u8 reg_val, pinctl_position; + + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + switch (pinctl_position >> 3) { + case 0: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_0; + break; + case 1: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_1; + break; + case 2: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_2; + break; + case 3: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_3; + break; + default: + dev_err(codec->dev, "%s: Invalid pinctl position = %d\n", + __func__, pinctl_position); + return -EINVAL; + } + + reg_val = snd_soc_read(codec, ctl_reg); + reg_val = (reg_val >> (pinctl_position & 0x07)) & 0x1; + ucontrol->value.integer.value[0] = reg_val; + + return 0; +} + +static int tasha_pinctl_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 ctl_reg, cfg_reg; + u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask; + + /* 1- high or low; 0- high Z */ + pinctl_mode = ucontrol->value.integer.value[0]; + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + switch (pinctl_position >> 3) { + case 0: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_0; + break; + case 1: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_1; + break; + case 2: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_2; + break; + case 3: + ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_3; + break; + default: + dev_err(codec->dev, "%s: Invalid pinctl position = %d\n", + __func__, pinctl_position); + return -EINVAL; + } + + ctl_val = pinctl_mode << (pinctl_position & 0x07); + mask = 1 << (pinctl_position & 0x07); + snd_soc_update_bits(codec, ctl_reg, mask, ctl_val); + + cfg_reg = WCD9335_TLMM_BIST_MODE_PINCFG + pinctl_position; + if (!pinctl_mode) { + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + cfg_val = 0x4; + else + cfg_val = 0xC; + } else { + cfg_val = 0; + } + snd_soc_update_bits(codec, cfg_reg, 0x07, cfg_val); + + dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n", + __func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val); + + return 0; +} + +static void wcd_vbat_adc_out_config_2_0(struct wcd_vbat *vbat, + struct snd_soc_codec *codec) +{ + u8 val1, val2; + + /* + * Measure dcp1 by using "ALT" branch of band gap + * voltage(Vbg) and use it in FAST mode + */ + snd_soc_update_bits(codec, WCD9335_BIAS_CTL, 0x82, 0x82); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01, 0x01); + snd_soc_update_bits(codec, WCD9335_ANA_VBADC, 0x80, 0x80); + snd_soc_update_bits(codec, WCD9335_VBADC_SUBBLOCK_EN, 0x20, 0x00); + + snd_soc_update_bits(codec, WCD9335_VBADC_FE_CTRL, 0x20, 0x20); + /* Wait 100 usec after calibration select as Vbg */ + usleep_range(100, 110); + + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x40); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x00); + + vbat->dcp1 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + snd_soc_update_bits(codec, WCD9335_BIAS_CTL, 0x40, 0x40); + /* Wait 100 usec after selecting Vbg as 1.05V */ + usleep_range(100, 110); + + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x40); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x00); + + vbat->dcp2 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + dev_dbg(codec->dev, "%s: dcp1:0x%x, dcp2:0x%x\n", + __func__, vbat->dcp1, vbat->dcp2); + + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28); + /* Wait 100 usec after selecting Vbg as 0.85V */ + usleep_range(100, 110); + + snd_soc_update_bits(codec, WCD9335_VBADC_FE_CTRL, 0x20, 0x00); + snd_soc_update_bits(codec, WCD9335_VBADC_SUBBLOCK_EN, 0x20, 0x20); + snd_soc_update_bits(codec, WCD9335_ANA_VBADC, 0x80, 0x00); + + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01, 0x00); +} + +static void wcd_vbat_adc_out_config_1_x(struct wcd_vbat *vbat, + struct snd_soc_codec *codec) +{ + u8 val1, val2; + + /* + * Measure dcp1 by applying band gap voltage(Vbg) + * of 0.85V + */ + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x20); + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28); + snd_soc_write(codec, WCD9335_BIAS_VBG_FINE_ADJ, 0x05); + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0); + /* Wait 2 sec after enabling band gap bias */ + usleep_range(2000000, 2000100); + + snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x82); + snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x87); + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x10); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0D); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01); + + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80); + snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xDE); + snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x3C); + /* Wait 1 msec after calibration select as Vbg */ + usleep_range(1000, 1100); + + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80); + + vbat->dcp1 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + /* + * Measure dcp2 by applying band gap voltage(Vbg) + * of 1.05V + */ + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80); + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0); + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x68); + /* Wait 2 msec after selecting Vbg as 1.05V */ + usleep_range(2000, 2100); + + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80); + /* Wait 1 sec after enabling band gap bias */ + usleep_range(1000000, 1000100); + + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0); + val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB); + val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB); + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80); + + vbat->dcp2 = (((val1 & 0xFF) << 3) | (val2 & 0x07)); + + dev_dbg(codec->dev, "%s: dcp1:0x%x, dcp2:0x%x\n", + __func__, vbat->dcp1, vbat->dcp2); + + /* Reset the Vbat ADC configuration */ + snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80); + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0); + + snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28); + /* Wait 2 msec after selecting Vbg as 0.85V */ + usleep_range(2000, 2100); + + snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0); + /* Wait 1 sec after enabling band gap bias */ + usleep_range(1000000, 1000100); + + snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x1C); + snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xFE); + snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80); + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00); + + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x00); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x00); + snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0A); +} + +static void wcd_vbat_adc_out_config(struct wcd_vbat *vbat, + struct snd_soc_codec *codec) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!vbat->adc_config) { + tasha_cdc_mclk_enable(codec, true, false); + + if (TASHA_IS_2_0(wcd9xxx)) + wcd_vbat_adc_out_config_2_0(vbat, codec); + else + wcd_vbat_adc_out_config_1_x(vbat, codec); + + tasha_cdc_mclk_enable(codec, false, false); + vbat->adc_config = true; + } +} + +static int tasha_update_vbat_reg_config(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct firmware_cal *hwdep_cal = NULL; + struct vbat_monitor_reg *vbat_reg_ptr = NULL; + const void *data; + size_t cal_size, vbat_size_remaining; + int ret = 0, i; + u32 vbat_writes_size = 0; + u16 reg; + u8 mask, val, old_val; + + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_VBAT_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + dev_err(codec->dev, "%s: Vbat cal not received\n", + __func__); + ret = -EINVAL; + goto done; + } + + if (cal_size < sizeof(*vbat_reg_ptr)) { + dev_err(codec->dev, + "%s: Incorrect size %zd for Vbat Cal, expected %zd\n", + __func__, cal_size, sizeof(*vbat_reg_ptr)); + ret = -EINVAL; + goto done; + } + + vbat_reg_ptr = (struct vbat_monitor_reg *) (data); + + if (!vbat_reg_ptr) { + dev_err(codec->dev, + "%s: Invalid calibration data for Vbat\n", + __func__); + ret = -EINVAL; + goto done; + } + + vbat_writes_size = vbat_reg_ptr->size; + vbat_size_remaining = cal_size - sizeof(u32); + dev_dbg(codec->dev, "%s: vbat_writes_sz: %d, vbat_sz_remaining: %zd\n", + __func__, vbat_writes_size, vbat_size_remaining); + + if ((vbat_writes_size * TASHA_PACKED_REG_SIZE) + > vbat_size_remaining) { + pr_err("%s: Incorrect Vbat calibration data\n", __func__); + ret = -EINVAL; + goto done; + } + + for (i = 0 ; i < vbat_writes_size; i++) { + TASHA_CODEC_UNPACK_ENTRY(vbat_reg_ptr->writes[i], + reg, mask, val); + old_val = snd_soc_read(codec, reg); + snd_soc_write(codec, reg, (old_val & ~mask) | (val & mask)); + } + +done: + return ret; +} + +static int tasha_vbat_adc_data_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + wcd_vbat_adc_out_config(&tasha->vbat, codec); + + ucontrol->value.integer.value[0] = tasha->vbat.dcp1; + ucontrol->value.integer.value[1] = tasha->vbat.dcp2; + + dev_dbg(codec->dev, + "%s: Vbat ADC output values, Dcp1 : %lu, Dcp2: %lu\n", + __func__, ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]); + + return 0; +} + +static const char * const tasha_vbat_gsm_mode_text[] = { + "OFF", "ON"}; + +static const struct soc_enum tasha_vbat_gsm_mode_enum = + SOC_ENUM_SINGLE_EXT(2, tasha_vbat_gsm_mode_text); + +static int tasha_vbat_gsm_mode_func_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ucontrol->value.integer.value[0] = + ((snd_soc_read(codec, WCD9335_CDC_VBAT_VBAT_CFG) & 0x04) ? + 1 : 0); + + dev_dbg(codec->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int tasha_vbat_gsm_mode_func_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: value: %lu\n", __func__, + ucontrol->value.integer.value[0]); + + /* Set Vbat register configuration for GSM mode bit based on value */ + if (ucontrol->value.integer.value[0]) + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG, + 0x04, 0x04); + else + snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG, + 0x04, 0x00); + + return 0; +} + +static int tasha_codec_vbat_enable_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u16 vbat_path_ctl, vbat_cfg, vbat_path_cfg; + + vbat_path_ctl = WCD9335_CDC_VBAT_VBAT_PATH_CTL; + vbat_cfg = WCD9335_CDC_VBAT_VBAT_CFG; + vbat_path_cfg = WCD9335_CDC_RX8_RX_PATH_CFG1; + + if (!strcmp(w->name, "RX INT8 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX8_RX_PATH_CFG1; + else if (!strcmp(w->name, "RX INT7 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX7_RX_PATH_CFG1; + else if (!strcmp(w->name, "RX INT6 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX6_RX_PATH_CFG1; + else if (!strcmp(w->name, "RX INT5 VBAT")) + vbat_path_cfg = WCD9335_CDC_RX5_RX_PATH_CFG1; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tasha_update_vbat_reg_config(codec); + if (ret) { + dev_dbg(codec->dev, + "%s : VBAT isn't calibrated, So not enabling it\n", + __func__); + return 0; + } + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80); + snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x02); + snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x01); + tasha->vbat.is_enabled = true; + break; + case SND_SOC_DAPM_POST_PMD: + if (tasha->vbat.is_enabled) { + snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x00); + snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x00); + snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x00); + snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00); + tasha->vbat.is_enabled = false; + } + break; + }; + + return ret; +} + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI" +}; + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const char * const amic_pwr_lvl_text[] = { + "LOW_PWR", "DEFAULT", "HIGH_PERF" +}; + +static const struct soc_enum amic_pwr_lvl_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(amic_pwr_lvl_text), + amic_pwr_lvl_text); + +static const struct snd_kcontrol_new tasha_snd_controls[] = { + SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD9335_CDC_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX1 Digital Volume", WCD9335_CDC_RX1_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Digital Volume", WCD9335_CDC_RX2_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Digital Volume", WCD9335_CDC_RX3_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX4 Digital Volume", WCD9335_CDC_RX4_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX5 Digital Volume", WCD9335_CDC_RX5_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX6 Digital Volume", WCD9335_CDC_RX6_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Digital Volume", WCD9335_CDC_RX7_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Digital Volume", WCD9335_CDC_RX8_RX_VOL_CTL, + 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("RX0 Mix Digital Volume", + WCD9335_CDC_RX0_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX1 Mix Digital Volume", + WCD9335_CDC_RX1_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX2 Mix Digital Volume", + WCD9335_CDC_RX2_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX3 Mix Digital Volume", + WCD9335_CDC_RX3_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX4 Mix Digital Volume", + WCD9335_CDC_RX4_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX5 Mix Digital Volume", + WCD9335_CDC_RX5_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX6 Mix Digital Volume", + WCD9335_CDC_RX6_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume", + WCD9335_CDC_RX7_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume", + WCD9335_CDC_RX8_RX_VOL_MIX_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + + SOC_SINGLE_SX_TLV("DEC0 Volume", WCD9335_CDC_TX0_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC1 Volume", WCD9335_CDC_TX1_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", WCD9335_CDC_TX2_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC3 Volume", WCD9335_CDC_TX3_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC4 Volume", WCD9335_CDC_TX4_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC5 Volume", WCD9335_CDC_TX5_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC6 Volume", WCD9335_CDC_TX6_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC7 Volume", WCD9335_CDC_TX7_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC8 Volume", WCD9335_CDC_TX8_TX_VOL_CTL, 0, + -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR0 INP0 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP1 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP2 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP3 Volume", + WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP0 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84, + 40, digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, + 40, digital_gain), + + SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tasha_get_anc_slot, + tasha_put_anc_slot), + SOC_ENUM_EXT("ANC Function", tasha_anc_func_enum, tasha_get_anc_func, + tasha_put_anc_func), + + SOC_ENUM_EXT("CLK MODE", tasha_clkmode_enum, tasha_get_clkmode, + tasha_put_clkmode), + + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_ENUM("TX3 HPF cut off", cf_dec3_enum), + SOC_ENUM("TX4 HPF cut off", cf_dec4_enum), + SOC_ENUM("TX5 HPF cut off", cf_dec5_enum), + SOC_ENUM("TX6 HPF cut off", cf_dec6_enum), + SOC_ENUM("TX7 HPF cut off", cf_dec7_enum), + SOC_ENUM("TX8 HPF cut off", cf_dec8_enum), + + SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum), + SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum), + SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum), + SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum), + SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum), + SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum), + SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum), + SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum), + SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum), + SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum), + SOC_ENUM("RX INT5_1 HPF cut off", cf_int5_1_enum), + SOC_ENUM("RX INT5_2 HPF cut off", cf_int5_2_enum), + SOC_ENUM("RX INT6_1 HPF cut off", cf_int6_1_enum), + SOC_ENUM("RX INT6_2 HPF cut off", cf_int6_2_enum), + SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum), + SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum), + SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum), + SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum), + + SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer), + + SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer), + + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP5 Switch", SND_SOC_NOPM, COMPANDER_5, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP6 Switch", SND_SOC_NOPM, COMPANDER_6, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + tasha_get_compander, tasha_set_compander), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + tasha_get_compander, tasha_set_compander), + + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + tasha_rx_hph_mode_get, tasha_rx_hph_mode_put), + + SOC_ENUM_EXT("MAD Input", tasha_conn_mad_enum, + tasha_mad_input_get, tasha_mad_input_put), + SOC_SINGLE_EXT("LDO_H Enable", SND_SOC_NOPM, 0, 1, 0, + tasha_enable_ldo_h_get, tasha_enable_ldo_h_put), + + SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC1_DATA_PIN_MODE", SND_SOC_NOPM, 18, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC2_CLK_PIN_MODE", SND_SOC_NOPM, 19, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC2_DATA_PIN_MODE", SND_SOC_NOPM, 20, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC3_CLK_PIN_MODE", SND_SOC_NOPM, 21, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + + SOC_SINGLE_EXT("DMIC3_DATA_PIN_MODE", SND_SOC_NOPM, 22, 1, 0, + tasha_pinctl_mode_get, tasha_pinctl_mode_put), + SOC_ENUM_EXT("AMIC_1_2 PWR MODE", amic_pwr_lvl_enum, + tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_3_4 PWR MODE", amic_pwr_lvl_enum, + tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_5_6 PWR MODE", amic_pwr_lvl_enum, + tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put), + + SOC_SINGLE_MULTI_EXT("Vbat ADC data", SND_SOC_NOPM, 0, 0xFFFF, 0, 2, + tasha_vbat_adc_data_get, NULL), + + SOC_ENUM_EXT("GSM mode Enable", tasha_vbat_gsm_mode_enum, + tasha_vbat_gsm_mode_func_get, + tasha_vbat_gsm_mode_func_put), +}; + +static int tasha_put_dec_enum(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_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg; + u8 mic_sel; + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + switch (e->reg) { + case WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1: + mic_sel_reg = WCD9335_CDC_TX0_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1: + mic_sel_reg = WCD9335_CDC_TX1_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1: + mic_sel_reg = WCD9335_CDC_TX2_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1: + mic_sel_reg = WCD9335_CDC_TX3_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0: + mic_sel_reg = WCD9335_CDC_TX4_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0: + mic_sel_reg = WCD9335_CDC_TX5_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0: + mic_sel_reg = WCD9335_CDC_TX6_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0: + mic_sel_reg = WCD9335_CDC_TX7_TX_PATH_CFG0; + break; + case WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0: + mic_sel_reg = WCD9335_CDC_TX8_TX_PATH_CFG0; + break; + default: + dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + + /* ADC: 0, DMIC: 1 */ + mic_sel = val ? 0x0 : 0x1; + snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, mic_sel << 7); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tasha_int_dem_inp_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 snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + unsigned short look_ahead_dly_reg = WCD9335_CDC_RX0_RX_PATH_CFG0; + + val = ucontrol->value.enumerated.item[0]; + if (val >= e->items) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + if (e->reg == WCD9335_CDC_RX0_RX_PATH_SEC0) + look_ahead_dly_reg = WCD9335_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD9335_CDC_RX1_RX_PATH_SEC0) + look_ahead_dly_reg = WCD9335_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == WCD9335_CDC_RX2_RX_PATH_SEC0) + look_ahead_dly_reg = WCD9335_CDC_RX2_RX_PATH_CFG0; + + /* Set Look Ahead Delay */ + snd_soc_update_bits(codec, look_ahead_dly_reg, + 0x08, (val ? 0x08 : 0x00)); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tasha_ear_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ear_pa_gain = snd_soc_read(codec, WCD9335_ANA_EAR); + + ear_pa_gain = (ear_pa_gain & 0x70) >> 4; + + ucontrol->value.integer.value[0] = ear_pa_gain; + + dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__, + ear_pa_gain); + + return 0; +} + +static int tasha_ear_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + ear_pa_gain = ucontrol->value.integer.value[0] << 4; + + snd_soc_update_bits(codec, WCD9335_ANA_EAR, 0x70, ear_pa_gain); + return 0; +} + +static int tasha_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tasha->ear_spkr_gain; + + dev_dbg(codec->dev, "%s: ear_spkr_gain = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int tasha_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + tasha->ear_spkr_gain = ucontrol->value.integer.value[0]; + + return 0; +} + +static int tasha_config_compander(struct snd_soc_codec *codec, int interp_n, + int event) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int comp; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + comp = interp_n - 1; + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, tasha->comp_enabled[comp]); + + if (!tasha->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = WCD9335_CDC_COMPANDER1_CTL0 + (comp * 8); + rx_path_cfg0_reg = WCD9335_CDC_RX1_RX_PATH_CFG0 + (comp * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static int tasha_codec_config_mad(struct snd_soc_codec *codec) +{ + int ret = 0; + int idx; + const struct firmware *fw; + struct firmware_cal *hwdep_cal = NULL; + struct wcd_mad_audio_cal *mad_cal = NULL; + const void *data; + const char *filename = TASHA_MAD_AUDIO_FIRMWARE_PATH; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + size_t cal_size; + + hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_MAD_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + ret = request_firmware(&fw, filename, codec->dev); + if (ret || !fw) { + dev_err(codec->dev, + "%s: MAD firmware acquire failed, err = %d\n", + __func__, ret); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + + if (cal_size < sizeof(*mad_cal)) { + dev_err(codec->dev, + "%s: Incorrect size %zd for MAD Cal, expected %zd\n", + __func__, cal_size, sizeof(*mad_cal)); + ret = -ENOMEM; + goto done; + } + + mad_cal = (struct wcd_mad_audio_cal *) (data); + if (!mad_cal) { + dev_err(codec->dev, + "%s: Invalid calibration data\n", + __func__); + ret = -EINVAL; + goto done; + } + + snd_soc_write(codec, WCD9335_SOC_MAD_MAIN_CTL_2, + mad_cal->microphone_info.cycle_time); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_MAIN_CTL_1, 0xFF << 3, + ((uint16_t)mad_cal->microphone_info.settle_time) + << 3); + + /* Audio */ + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_8, + mad_cal->audio_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_CTL_1, + 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03 << 2, + mad_cal->audio_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_7, + mad_cal->audio_info.rms_diff_threshold & 0x3F); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_5, + mad_cal->audio_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_6, + mad_cal->audio_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL, + mad_cal->audio_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->audio_info.iir_coefficients[idx]); + } + + /* Beacon */ + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_8, + mad_cal->beacon_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_CTL_1, + 0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_CTL_2, 0x03 << 2, + mad_cal->beacon_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_7, + mad_cal->beacon_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_5, + mad_cal->beacon_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_6, + mad_cal->beacon_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL, + mad_cal->beacon_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->beacon_info.iir_coefficients[idx]); + } + + /* Ultrasound */ + snd_soc_update_bits(codec, WCD9335_SOC_MAD_ULTR_CTL_1, + 0x07 << 4, + mad_cal->ultrasound_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD9335_SOC_MAD_ULTR_CTL_2, 0x03 << 2, + mad_cal->ultrasound_info.detection_mechanism << 2); + snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_7, + mad_cal->ultrasound_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_5, + mad_cal->ultrasound_info.rms_threshold_lsb); + snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_6, + mad_cal->ultrasound_info.rms_threshold_msb); + +done: + if (!hwdep_cal) + release_firmware(fw); + + return ret; +} + +static int tasha_codec_enable_mad(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + + dev_dbg(codec->dev, + "%s: event = %d\n", __func__, event); + + /* Return if CPE INPUT is DEC1 */ + if (snd_soc_read(codec, WCD9335_CPE_SS_SVA_CFG) & 0x01) + return ret; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + + /* Turn on MAD clk */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x01, 0x01); + + /* Undo reset for MAD */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x02, 0x00); + ret = tasha_codec_config_mad(codec); + if (ret) + dev_err(codec->dev, + "%s: Failed to config MAD, err = %d\n", + __func__, ret); + break; + case SND_SOC_DAPM_POST_PMD: + /* Reset the MAD block */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x02, 0x02); + /* Turn off MAD clk */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL, + 0x01, 0x00); + break; + } + + return ret; +} + +static int tasha_codec_configure_cpe_input(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, + "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Configure CPE input as DEC1 */ + snd_soc_update_bits(codec, WCD9335_CPE_SS_SVA_CFG, + 0x01, 0x01); + + /* Configure DEC1 Tx out with sample rate as 16K */ + snd_soc_update_bits(codec, WCD9335_CDC_TX1_TX_PATH_CTL, + 0x0F, 0x01); + + break; + case SND_SOC_DAPM_POST_PMD: + /* Reset DEC1 Tx out sample rate */ + snd_soc_update_bits(codec, WCD9335_CDC_TX1_TX_PATH_CTL, + 0x0F, 0x04); + snd_soc_update_bits(codec, WCD9335_CPE_SS_SVA_CFG, + 0x01, 0x00); + + break; + } + + return 0; +} + + +static int tasha_codec_aif4_mixer_switch_get(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_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + if (test_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask)) + ucontrol->value.integer.value[0] = 1; + else + ucontrol->value.integer.value[0] = 0; + + dev_dbg(codec->dev, "%s: AIF4 switch value = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int tasha_codec_aif4_mixer_switch_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 snd_soc_dapm_update *update = NULL; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: AIF4 switch value = %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); + set_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask); + } else { + snd_soc_dapm_mixer_update_power(widget->dapm, + kcontrol, 0, update); + clear_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask); + } + + return 1; +} + +static const char * const tasha_ear_pa_gain_text[] = { + "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", + "G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB" +}; + +static const char * const tasha_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", "G_4_DB", + "G_5_DB", "G_6_DB" +}; + +static const struct soc_enum tasha_ear_pa_gain_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_pa_gain_text), + tasha_ear_pa_gain_text); + +static const struct soc_enum tasha_ear_spkr_pa_gain_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_spkr_pa_gain_text), + tasha_ear_spkr_pa_gain_text); + +static const struct snd_kcontrol_new tasha_analog_gain_controls[] = { + SOC_ENUM_EXT("EAR PA Gain", tasha_ear_pa_gain_enum, + tasha_ear_pa_gain_get, tasha_ear_pa_gain_put), + + SOC_ENUM_EXT("EAR SPKR PA Gain", tasha_ear_spkr_pa_gain_enum, + tasha_ear_spkr_pa_gain_get, tasha_ear_spkr_pa_gain_put), + + SOC_SINGLE_TLV("HPHL Volume", WCD9335_HPH_L_EN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD9335_HPH_R_EN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("LINEOUT1 Volume", WCD9335_DIFF_LO_LO1_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT2 Volume", WCD9335_DIFF_LO_LO2_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT3 Volume", WCD9335_SE_LO_LO3_GAIN, 0, 20, 1, + line_gain), + SOC_SINGLE_TLV("LINEOUT4 Volume", WCD9335_SE_LO_LO4_GAIN, 0, 20, 1, + line_gain), + + SOC_SINGLE_TLV("ADC1 Volume", WCD9335_ANA_AMIC1, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD9335_ANA_AMIC2, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD9335_ANA_AMIC3, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD9335_ANA_AMIC4, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC5 Volume", WCD9335_ANA_AMIC5, 0, 20, 0, + analog_gain), + SOC_SINGLE_TLV("ADC6 Volume", WCD9335_ANA_AMIC6, 0, 20, 0, + analog_gain), +}; + +static const char * const spl_src0_mux_text[] = { + "ZERO", "SRC_IN_HPHL", "SRC_IN_LO1", +}; + +static const char * const spl_src1_mux_text[] = { + "ZERO", "SRC_IN_HPHR", "SRC_IN_LO2", +}; + +static const char * const spl_src2_mux_text[] = { + "ZERO", "SRC_IN_LO3", "SRC_IN_SPKRL", +}; + +static const char * const spl_src3_mux_text[] = { + "ZERO", "SRC_IN_LO4", "SRC_IN_SPKRR", +}; + +static const char * const rx_int0_7_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7", "PROXIMITY" +}; + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_sidetone_mix_text[] = { + "ZERO", "SRC0", "SRC1", "SRC_SUM" +}; + +static const char * const sb_tx0_mux_text[] = { + "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192" +}; + +static const char * const sb_tx1_mux_text[] = { + "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192" +}; + +static const char * const sb_tx2_mux_text[] = { + "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192" +}; + +static const char * const sb_tx3_mux_text[] = { + "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192" +}; + +static const char * const sb_tx4_mux_text[] = { + "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192" +}; + +static const char * const sb_tx5_mux_text[] = { + "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192" +}; + +static const char * const sb_tx6_mux_text[] = { + "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192" +}; + +static const char * const sb_tx7_mux_text[] = { + "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192" +}; + +static const char * const sb_tx8_mux_text[] = { + "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192" +}; + +static const char * const sb_tx9_mux_text[] = { + "ZERO", "DEC7", "DEC7_192" +}; + +static const char * const sb_tx10_mux_text[] = { + "ZERO", "DEC6", "DEC6_192" +}; + +static const char * const sb_tx11_mux_text[] = { + "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST" +}; + +static const char * const sb_tx11_inp1_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", + "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12" +}; + +static const char * const sb_tx13_mux_text[] = { + "ZERO", "DEC5", "DEC5_192" +}; + +static const char * const tx13_inp_mux_text[] = { + "CDC_DEC_5", "MAD_BRDCST", "CPE_TX_PP" +}; + +static const char * const iir_inp_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", + "DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static const char * const rx_int0_interp_mux_text[] = { + "ZERO", "RX INT0 MIX2", +}; + +static const char * const rx_int1_interp_mux_text[] = { + "ZERO", "RX INT1 MIX2", +}; + +static const char * const rx_int2_interp_mux_text[] = { + "ZERO", "RX INT2 MIX2", +}; + +static const char * const rx_int3_interp_mux_text[] = { + "ZERO", "RX INT3 MIX2", +}; + +static const char * const rx_int4_interp_mux_text[] = { + "ZERO", "RX INT4 MIX2", +}; + +static const char * const rx_int5_interp_mux_text[] = { + "ZERO", "RX INT5 MIX2", +}; + +static const char * const rx_int6_interp_mux_text[] = { + "ZERO", "RX INT6 MIX2", +}; + +static const char * const rx_int7_interp_mux_text[] = { + "ZERO", "RX INT7 MIX2", +}; + +static const char * const rx_int8_interp_mux_text[] = { + "ZERO", "RX INT8 SEC MIX" +}; + +static const char * const mad_sel_text[] = { + "SPE", "MSM" +}; + +static const char * const adc_mux_text[] = { + "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2" +}; + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", + "SMIC0", "SMIC1", "SMIC2", "SMIC3" +}; + +static const char * const dmic_mux_alt_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", +}; + +static const char * const amic_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6" +}; + +static const char * const rx_echo_mux_text[] = { + "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4", + "RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8", "RX_MIX_VBAT5", + "RX_MIX_VBAT6", "RX_MIX_VBAT7", "RX_MIX_VBAT8" +}; + +static const char * const anc0_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR", + "ANC_IN_LO1" +}; + +static const char * const anc1_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHR", "ANC_IN_LO2" +}; + +static const char * const native_mux_text[] = { + "OFF", "ON", +}; + +static const struct soc_enum spl_src0_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 0, 3, + spl_src0_mux_text); + +static const struct soc_enum spl_src1_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 2, 3, + spl_src1_mux_text); + +static const struct soc_enum spl_src2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 4, 3, + spl_src2_mux_text); + +static const struct soc_enum spl_src3_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 6, 3, + spl_src3_mux_text); + +static const struct soc_enum rx_int0_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int1_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int2_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int3_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int4_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int5_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int6_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum rx_int7_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, 10, + rx_int0_7_mix_mux_text); + +static const struct soc_enum rx_int8_2_mux_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9, + rx_int_mix_mux_text); + +static const struct soc_enum int1_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int2_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int3_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum int4_1_native_enum = + SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text), + native_mux_text); + +static const struct soc_enum rx_int0_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int1_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int2_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int3_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int4_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int5_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int6_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int7_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int8_1_mix_inp2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, 13, + rx_prim_mix_text); + +static const struct soc_enum rx_int0_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int1_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int2_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int3_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int4_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, 4, + rx_sidetone_mix_text); + +static const struct soc_enum rx_int7_sidetone_mix_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, 4, + rx_sidetone_mix_text); + +static const struct soc_enum tx_adc_mux0_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux1_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux2_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux3_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux4_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux5_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux6_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux7_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux8_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux10_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux11_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux12_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_adc_mux13_chain_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 6, 4, + adc_mux_text); + +static const struct soc_enum tx_dmic_mux0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, 11, + dmic_mux_text); + +static const struct soc_enum tx_dmic_mux4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux10_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux11_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux12_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_dmic_mux13_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 3, 7, + dmic_mux_alt_text); + +static const struct soc_enum tx_amic_mux0_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux1_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux2_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux3_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux4_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux5_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux6_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux7_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux8_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux10_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux11_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux12_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum tx_amic_mux13_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0, 7, + amic_mux_text); + +static const struct soc_enum sb_tx0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 0, 4, + sb_tx0_mux_text); + +static const struct soc_enum sb_tx1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 2, 4, + sb_tx1_mux_text); + +static const struct soc_enum sb_tx2_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 4, 4, + sb_tx2_mux_text); + +static const struct soc_enum sb_tx3_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 6, 4, + sb_tx3_mux_text); + +static const struct soc_enum sb_tx4_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 0, 4, + sb_tx4_mux_text); + +static const struct soc_enum sb_tx5_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 2, 4, + sb_tx5_mux_text); + +static const struct soc_enum sb_tx6_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 4, 4, + sb_tx6_mux_text); + +static const struct soc_enum sb_tx7_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 6, 4, + sb_tx7_mux_text); + +static const struct soc_enum sb_tx8_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 0, 4, + sb_tx8_mux_text); + +static const struct soc_enum sb_tx9_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 2, 3, + sb_tx9_mux_text); + +static const struct soc_enum sb_tx10_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 4, 3, + sb_tx10_mux_text); + +static const struct soc_enum sb_tx11_mux_enum = + SOC_ENUM_SINGLE(WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG, 0, 4, + sb_tx11_mux_text); + +static const struct soc_enum sb_tx11_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 0, 10, + sb_tx11_inp1_mux_text); + +static const struct soc_enum sb_tx13_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 4, 3, + sb_tx13_mux_text); + +static const struct soc_enum tx13_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, 0, 3, + tx13_inp_mux_text); + +static const struct soc_enum rx_mix_tx0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx2_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx3_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx4_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx5_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx6_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx7_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 4, 14, + rx_echo_mux_text); + +static const struct soc_enum rx_mix_tx8_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, 14, + rx_echo_mux_text); + +static const struct soc_enum iir0_inp0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir0_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir0_inp2_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir0_inp3_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir1_inp0_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir1_inp1_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir1_inp2_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum iir1_inp3_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0, 18, + iir_inp_mux_text); + +static const struct soc_enum rx_int0_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int1_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int2_dem_inp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_SEC0, 0, + ARRAY_SIZE(rx_int_dem_inp_mux_text), + rx_int_dem_inp_mux_text); + +static const struct soc_enum rx_int0_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CTL, 5, 2, + rx_int0_interp_mux_text); + +static const struct soc_enum rx_int1_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CTL, 5, 2, + rx_int1_interp_mux_text); + +static const struct soc_enum rx_int2_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CTL, 5, 2, + rx_int2_interp_mux_text); + +static const struct soc_enum rx_int3_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CTL, 5, 2, + rx_int3_interp_mux_text); + +static const struct soc_enum rx_int4_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CTL, 5, 2, + rx_int4_interp_mux_text); + +static const struct soc_enum rx_int5_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CTL, 5, 2, + rx_int5_interp_mux_text); + +static const struct soc_enum rx_int6_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CTL, 5, 2, + rx_int6_interp_mux_text); + +static const struct soc_enum rx_int7_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CTL, 5, 2, + rx_int7_interp_mux_text); + +static const struct soc_enum rx_int8_interp_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CTL, 5, 2, + rx_int8_interp_mux_text); + +static const struct soc_enum mad_sel_enum = + SOC_ENUM_SINGLE(WCD9335_CPE_SS_CFG, 0, 2, mad_sel_text); + +static const struct soc_enum anc0_fb_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 0, 5, + anc0_fb_mux_text); + +static const struct soc_enum anc1_fb_mux_enum = + SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 3, 3, + anc1_fb_mux_text); + +static const struct snd_kcontrol_new rx_int0_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT0 DEM MUX Mux", rx_int0_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + tasha_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int1_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT1 DEM MUX Mux", rx_int1_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + tasha_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new rx_int2_dem_inp_mux = + SOC_DAPM_ENUM_EXT("RX INT2 DEM MUX Mux", rx_int2_dem_inp_mux_enum, + snd_soc_dapm_get_enum_double, + tasha_int_dem_inp_mux_put); + +static const struct snd_kcontrol_new spl_src0_mux = + SOC_DAPM_ENUM("SPL SRC0 MUX Mux", spl_src0_mux_chain_enum); + +static const struct snd_kcontrol_new spl_src1_mux = + SOC_DAPM_ENUM("SPL SRC1 MUX Mux", spl_src1_mux_chain_enum); + +static const struct snd_kcontrol_new spl_src2_mux = + SOC_DAPM_ENUM("SPL SRC2 MUX Mux", spl_src2_mux_chain_enum); + +static const struct snd_kcontrol_new spl_src3_mux = + SOC_DAPM_ENUM("SPL SRC3 MUX Mux", spl_src3_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int0_2_mux = + SOC_DAPM_ENUM("RX INT0_2 MUX Mux", rx_int0_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int1_2_mux = + SOC_DAPM_ENUM("RX INT1_2 MUX Mux", rx_int1_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int2_2_mux = + SOC_DAPM_ENUM("RX INT2_2 MUX Mux", rx_int2_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int3_2_mux = + SOC_DAPM_ENUM("RX INT3_2 MUX Mux", rx_int3_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int4_2_mux = + SOC_DAPM_ENUM("RX INT4_2 MUX Mux", rx_int4_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int5_2_mux = + SOC_DAPM_ENUM("RX INT5_2 MUX Mux", rx_int5_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int6_2_mux = + SOC_DAPM_ENUM("RX INT6_2 MUX Mux", rx_int6_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int7_2_mux = + SOC_DAPM_ENUM("RX INT7_2 MUX Mux", rx_int7_2_mux_chain_enum); + +static const struct snd_kcontrol_new rx_int8_2_mux = + SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum); + +static const struct snd_kcontrol_new int1_1_native_mux = + SOC_DAPM_ENUM("RX INT1_1 NATIVE MUX Mux", int1_1_native_enum); + +static const struct snd_kcontrol_new int2_1_native_mux = + SOC_DAPM_ENUM("RX INT2_1 NATIVE MUX Mux", int2_1_native_enum); + +static const struct snd_kcontrol_new int3_1_native_mux = + SOC_DAPM_ENUM("RX INT3_1 NATIVE MUX Mux", int3_1_native_enum); + +static const struct snd_kcontrol_new int4_1_native_mux = + SOC_DAPM_ENUM("RX INT4_1 NATIVE MUX Mux", int4_1_native_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP1 Mux", rx_int0_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT0_1 MIX1 INP2 Mux", rx_int0_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP0 Mux", rx_int1_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP1 Mux", rx_int1_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT1_1 MIX1 INP2 Mux", rx_int1_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP0 Mux", rx_int2_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP1 Mux", rx_int2_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT2_1 MIX1 INP2 Mux", rx_int2_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP0 Mux", rx_int3_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP1 Mux", rx_int3_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int3_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT3_1 MIX1 INP2 Mux", rx_int3_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP0 Mux", rx_int4_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP1 Mux", rx_int4_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int4_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT4_1 MIX1 INP2 Mux", rx_int4_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int5_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP0 Mux", rx_int5_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int5_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP1 Mux", rx_int5_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int5_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT5_1 MIX1 INP2 Mux", rx_int5_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP0 Mux", rx_int6_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP1 Mux", rx_int6_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int6_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT6_1 MIX1 INP2 Mux", rx_int6_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP0 Mux", rx_int7_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP1 Mux", rx_int7_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int7_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT7_1 MIX1 INP2 Mux", rx_int7_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp0_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP0 Mux", rx_int8_1_mix_inp0_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp1_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP1 Mux", rx_int8_1_mix_inp1_chain_enum); + +static const struct snd_kcontrol_new rx_int8_1_mix_inp2_mux = + SOC_DAPM_ENUM("RX INT8_1 MIX1 INP2 Mux", rx_int8_1_mix_inp2_chain_enum); + +static const struct snd_kcontrol_new rx_int0_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT0 MIX2 INP Mux", rx_int0_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int1_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT1 MIX2 INP Mux", rx_int1_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int2_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT2 MIX2 INP Mux", rx_int2_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int3_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT3 MIX2 INP Mux", rx_int3_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int4_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT4 MIX2 INP Mux", rx_int4_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new rx_int7_mix2_inp_mux = + SOC_DAPM_ENUM("RX INT7 MIX2 INP Mux", rx_int7_sidetone_mix_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux0 = + SOC_DAPM_ENUM_EXT("ADC MUX0 Mux", tx_adc_mux0_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux1 = + SOC_DAPM_ENUM_EXT("ADC MUX1 Mux", tx_adc_mux1_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux2 = + SOC_DAPM_ENUM_EXT("ADC MUX2 Mux", tx_adc_mux2_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux3 = + SOC_DAPM_ENUM_EXT("ADC MUX3 Mux", tx_adc_mux3_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux4 = + SOC_DAPM_ENUM_EXT("ADC MUX4 Mux", tx_adc_mux4_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux5 = + SOC_DAPM_ENUM_EXT("ADC MUX5 Mux", tx_adc_mux5_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux6 = + SOC_DAPM_ENUM_EXT("ADC MUX6 Mux", tx_adc_mux6_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux7 = + SOC_DAPM_ENUM_EXT("ADC MUX7 Mux", tx_adc_mux7_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux8 = + SOC_DAPM_ENUM_EXT("ADC MUX8 Mux", tx_adc_mux8_chain_enum, + snd_soc_dapm_get_enum_double, + tasha_put_dec_enum); + +static const struct snd_kcontrol_new tx_adc_mux10 = + SOC_DAPM_ENUM("ADC MUX10 Mux", tx_adc_mux10_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux11 = + SOC_DAPM_ENUM("ADC MUX11 Mux", tx_adc_mux11_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux12 = + SOC_DAPM_ENUM("ADC MUX12 Mux", tx_adc_mux12_chain_enum); + +static const struct snd_kcontrol_new tx_adc_mux13 = + SOC_DAPM_ENUM("ADC MUX13 Mux", tx_adc_mux13_chain_enum); + +static const struct snd_kcontrol_new tx_dmic_mux0 = + SOC_DAPM_ENUM("DMIC MUX0 Mux", tx_dmic_mux0_enum); + +static const struct snd_kcontrol_new tx_dmic_mux1 = + SOC_DAPM_ENUM("DMIC MUX1 Mux", tx_dmic_mux1_enum); + +static const struct snd_kcontrol_new tx_dmic_mux2 = + SOC_DAPM_ENUM("DMIC MUX2 Mux", tx_dmic_mux2_enum); + +static const struct snd_kcontrol_new tx_dmic_mux3 = + SOC_DAPM_ENUM("DMIC MUX3 Mux", tx_dmic_mux3_enum); + +static const struct snd_kcontrol_new tx_dmic_mux4 = + SOC_DAPM_ENUM("DMIC MUX4 Mux", tx_dmic_mux4_enum); + +static const struct snd_kcontrol_new tx_dmic_mux5 = + SOC_DAPM_ENUM("DMIC MUX5 Mux", tx_dmic_mux5_enum); + +static const struct snd_kcontrol_new tx_dmic_mux6 = + SOC_DAPM_ENUM("DMIC MUX6 Mux", tx_dmic_mux6_enum); + +static const struct snd_kcontrol_new tx_dmic_mux7 = + SOC_DAPM_ENUM("DMIC MUX7 Mux", tx_dmic_mux7_enum); + +static const struct snd_kcontrol_new tx_dmic_mux8 = + SOC_DAPM_ENUM("DMIC MUX8 Mux", tx_dmic_mux8_enum); + +static const struct snd_kcontrol_new tx_dmic_mux10 = + SOC_DAPM_ENUM("DMIC MUX10 Mux", tx_dmic_mux10_enum); + +static const struct snd_kcontrol_new tx_dmic_mux11 = + SOC_DAPM_ENUM("DMIC MUX11 Mux", tx_dmic_mux11_enum); + +static const struct snd_kcontrol_new tx_dmic_mux12 = + SOC_DAPM_ENUM("DMIC MUX12 Mux", tx_dmic_mux12_enum); + +static const struct snd_kcontrol_new tx_dmic_mux13 = + SOC_DAPM_ENUM("DMIC MUX13 Mux", tx_dmic_mux13_enum); + +static const struct snd_kcontrol_new tx_amic_mux0 = + SOC_DAPM_ENUM("AMIC MUX0 Mux", tx_amic_mux0_enum); + +static const struct snd_kcontrol_new tx_amic_mux1 = + SOC_DAPM_ENUM("AMIC MUX1 Mux", tx_amic_mux1_enum); + +static const struct snd_kcontrol_new tx_amic_mux2 = + SOC_DAPM_ENUM("AMIC MUX2 Mux", tx_amic_mux2_enum); + +static const struct snd_kcontrol_new tx_amic_mux3 = + SOC_DAPM_ENUM("AMIC MUX3 Mux", tx_amic_mux3_enum); + +static const struct snd_kcontrol_new tx_amic_mux4 = + SOC_DAPM_ENUM("AMIC MUX4 Mux", tx_amic_mux4_enum); + +static const struct snd_kcontrol_new tx_amic_mux5 = + SOC_DAPM_ENUM("AMIC MUX5 Mux", tx_amic_mux5_enum); + +static const struct snd_kcontrol_new tx_amic_mux6 = + SOC_DAPM_ENUM("AMIC MUX6 Mux", tx_amic_mux6_enum); + +static const struct snd_kcontrol_new tx_amic_mux7 = + SOC_DAPM_ENUM("AMIC MUX7 Mux", tx_amic_mux7_enum); + +static const struct snd_kcontrol_new tx_amic_mux8 = + SOC_DAPM_ENUM("AMIC MUX8 Mux", tx_amic_mux8_enum); + +static const struct snd_kcontrol_new tx_amic_mux10 = + SOC_DAPM_ENUM("AMIC MUX10 Mux", tx_amic_mux10_enum); + +static const struct snd_kcontrol_new tx_amic_mux11 = + SOC_DAPM_ENUM("AMIC MUX11 Mux", tx_amic_mux11_enum); + +static const struct snd_kcontrol_new tx_amic_mux12 = + SOC_DAPM_ENUM("AMIC MUX12 Mux", tx_amic_mux12_enum); + +static const struct snd_kcontrol_new tx_amic_mux13 = + SOC_DAPM_ENUM("AMIC MUX13 Mux", tx_amic_mux13_enum); + +static const struct snd_kcontrol_new sb_tx0_mux = + SOC_DAPM_ENUM("SLIM TX0 MUX Mux", sb_tx0_mux_enum); + +static const struct snd_kcontrol_new sb_tx1_mux = + SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum); + +static const struct snd_kcontrol_new sb_tx2_mux = + SOC_DAPM_ENUM("SLIM TX2 MUX Mux", sb_tx2_mux_enum); + +static const struct snd_kcontrol_new sb_tx3_mux = + SOC_DAPM_ENUM("SLIM TX3 MUX Mux", sb_tx3_mux_enum); + +static const struct snd_kcontrol_new sb_tx4_mux = + SOC_DAPM_ENUM("SLIM TX4 MUX Mux", sb_tx4_mux_enum); + +static const struct snd_kcontrol_new sb_tx5_mux = + SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum); + +static const struct snd_kcontrol_new sb_tx6_mux = + SOC_DAPM_ENUM("SLIM TX6 MUX Mux", sb_tx6_mux_enum); + +static const struct snd_kcontrol_new sb_tx7_mux = + SOC_DAPM_ENUM("SLIM TX7 MUX Mux", sb_tx7_mux_enum); + +static const struct snd_kcontrol_new sb_tx8_mux = + SOC_DAPM_ENUM("SLIM TX8 MUX Mux", sb_tx8_mux_enum); + +static const struct snd_kcontrol_new sb_tx9_mux = + SOC_DAPM_ENUM("SLIM TX9 MUX Mux", sb_tx9_mux_enum); + +static const struct snd_kcontrol_new sb_tx10_mux = + SOC_DAPM_ENUM("SLIM TX10 MUX Mux", sb_tx10_mux_enum); + +static const struct snd_kcontrol_new sb_tx11_mux = + SOC_DAPM_ENUM("SLIM TX11 MUX Mux", sb_tx11_mux_enum); + +static const struct snd_kcontrol_new sb_tx11_inp1_mux = + SOC_DAPM_ENUM("SLIM TX11 INP1 MUX Mux", sb_tx11_inp1_mux_enum); + +static const struct snd_kcontrol_new sb_tx13_mux = + SOC_DAPM_ENUM("SLIM TX13 MUX Mux", sb_tx13_mux_enum); + +static const struct snd_kcontrol_new tx13_inp_mux = + SOC_DAPM_ENUM("TX13 INP MUX Mux", tx13_inp_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx0_mux = + SOC_DAPM_ENUM("RX MIX TX0 MUX Mux", rx_mix_tx0_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx1_mux = + SOC_DAPM_ENUM("RX MIX TX1 MUX Mux", rx_mix_tx1_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx2_mux = + SOC_DAPM_ENUM("RX MIX TX2 MUX Mux", rx_mix_tx2_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx3_mux = + SOC_DAPM_ENUM("RX MIX TX3 MUX Mux", rx_mix_tx3_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx4_mux = + SOC_DAPM_ENUM("RX MIX TX4 MUX Mux", rx_mix_tx4_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx5_mux = + SOC_DAPM_ENUM("RX MIX TX5 MUX Mux", rx_mix_tx5_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx6_mux = + SOC_DAPM_ENUM("RX MIX TX6 MUX Mux", rx_mix_tx6_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx7_mux = + SOC_DAPM_ENUM("RX MIX TX7 MUX Mux", rx_mix_tx7_mux_enum); + +static const struct snd_kcontrol_new rx_mix_tx8_mux = + SOC_DAPM_ENUM("RX MIX TX8 MUX Mux", rx_mix_tx8_mux_enum); + +static const struct snd_kcontrol_new iir0_inp0_mux = + SOC_DAPM_ENUM("IIR0 INP0 Mux", iir0_inp0_mux_enum); + +static const struct snd_kcontrol_new iir0_inp1_mux = + SOC_DAPM_ENUM("IIR0 INP1 Mux", iir0_inp1_mux_enum); + +static const struct snd_kcontrol_new iir0_inp2_mux = + SOC_DAPM_ENUM("IIR0 INP2 Mux", iir0_inp2_mux_enum); + +static const struct snd_kcontrol_new iir0_inp3_mux = + SOC_DAPM_ENUM("IIR0 INP3 Mux", iir0_inp3_mux_enum); + +static const struct snd_kcontrol_new iir1_inp0_mux = + SOC_DAPM_ENUM("IIR1 INP0 Mux", iir1_inp0_mux_enum); + +static const struct snd_kcontrol_new iir1_inp1_mux = + SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum); + +static const struct snd_kcontrol_new iir1_inp2_mux = + SOC_DAPM_ENUM("IIR1 INP2 Mux", iir1_inp2_mux_enum); + +static const struct snd_kcontrol_new iir1_inp3_mux = + SOC_DAPM_ENUM("IIR1 INP3 Mux", iir1_inp3_mux_enum); + +static const struct snd_kcontrol_new rx_int0_interp_mux = + SOC_DAPM_ENUM("RX INT0 INTERP Mux", rx_int0_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int1_interp_mux = + SOC_DAPM_ENUM("RX INT1 INTERP Mux", rx_int1_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int2_interp_mux = + SOC_DAPM_ENUM("RX INT2 INTERP Mux", rx_int2_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int3_interp_mux = + SOC_DAPM_ENUM("RX INT3 INTERP Mux", rx_int3_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int4_interp_mux = + SOC_DAPM_ENUM("RX INT4 INTERP Mux", rx_int4_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int5_interp_mux = + SOC_DAPM_ENUM("RX INT5 INTERP Mux", rx_int5_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int6_interp_mux = + SOC_DAPM_ENUM("RX INT6 INTERP Mux", rx_int6_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int7_interp_mux = + SOC_DAPM_ENUM("RX INT7 INTERP Mux", rx_int7_interp_mux_enum); + +static const struct snd_kcontrol_new rx_int8_interp_mux = + SOC_DAPM_ENUM("RX INT8 INTERP Mux", rx_int8_interp_mux_enum); + +static const struct snd_kcontrol_new mad_sel_mux = + SOC_DAPM_ENUM("MAD_SEL MUX Mux", mad_sel_enum); + +static const struct snd_kcontrol_new aif4_mad_switch = + SOC_DAPM_SINGLE("Switch", WCD9335_CPE_SS_CFG, 5, 1, 0); + +static const struct snd_kcontrol_new mad_brdcst_switch = + SOC_DAPM_SINGLE("Switch", WCD9335_CPE_SS_CFG, 6, 1, 0); + +static const struct snd_kcontrol_new aif4_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, tasha_codec_aif4_mixer_switch_get, + tasha_codec_aif4_mixer_switch_put); + +static const struct snd_kcontrol_new anc_hphl_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_hphr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_ear_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_ear_spkr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_lineout1_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_lineout2_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_spkr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux0_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux1_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux2_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux3_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux4_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux5_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux6_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux7_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux8_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc0_fb_mux = + SOC_DAPM_ENUM("ANC0 FB MUX Mux", anc0_fb_mux_enum); + +static const struct snd_kcontrol_new anc1_fb_mux = + SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum); + +static int tasha_codec_ec_buf_mux_enable(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d name = %s\n", + __func__, event, w->name); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3B); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x08); + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x08); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, + 0x08, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x00); + snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x00); + break; + } + + return 0; +}; + +static const char * const ec_buf_mux_text[] = { + "ZERO", "RXMIXEC", "SB_RX0", "SB_RX1", "SB_RX2", "SB_RX3", + "I2S_RX_SD0_L", "I2S_RX_SD0_R", "I2S_RX_SD1_L", "I2S_RX_SD1_R", + "DEC1" +}; + +static SOC_ENUM_SINGLE_DECL(ec_buf_mux_enum, WCD9335_CPE_SS_US_EC_MUX_CFG, + 0, ec_buf_mux_text); + +static const struct snd_kcontrol_new ec_buf_mux = + SOC_DAPM_ENUM("EC BUF Mux", ec_buf_mux_enum); + +static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("ANC EAR"), + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, + AIF4_PB, 0, tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF MIX1 PB", "AIF Mix Playback", 0, + SND_SOC_NOPM, AIF_MIX1_PB, 0, + tasha_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, TASHA_RX0, 0, + &slim_rx_mux[TASHA_RX0]), + SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TASHA_RX1, 0, + &slim_rx_mux[TASHA_RX1]), + SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TASHA_RX2, 0, + &slim_rx_mux[TASHA_RX2]), + SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TASHA_RX3, 0, + &slim_rx_mux[TASHA_RX3]), + SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TASHA_RX4, 0, + &slim_rx_mux[TASHA_RX4]), + SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TASHA_RX5, 0, + &slim_rx_mux[TASHA_RX5]), + SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TASHA_RX6, 0, + &slim_rx_mux[TASHA_RX6]), + SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TASHA_RX7, 0, + &slim_rx_mux[TASHA_RX7]), + + SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX_E("SPL SRC0 MUX", SND_SOC_NOPM, SPLINE_SRC0, 0, + &spl_src0_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("SPL SRC1 MUX", SND_SOC_NOPM, SPLINE_SRC1, 0, + &spl_src1_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("SPL SRC2 MUX", SND_SOC_NOPM, SPLINE_SRC2, 0, + &spl_src2_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("SPL SRC3 MUX", SND_SOC_NOPM, SPLINE_SRC3, 0, + &spl_src3_mux, tasha_codec_enable_spline_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", WCD9335_CDC_RX0_RX_PATH_MIX_CTL, + 5, 0, &rx_int0_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", WCD9335_CDC_RX1_RX_PATH_MIX_CTL, + 5, 0, &rx_int1_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", WCD9335_CDC_RX2_RX_PATH_MIX_CTL, + 5, 0, &rx_int2_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", WCD9335_CDC_RX3_RX_PATH_MIX_CTL, + 5, 0, &rx_int3_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", WCD9335_CDC_RX4_RX_PATH_MIX_CTL, + 5, 0, &rx_int4_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT5_2 MUX", WCD9335_CDC_RX5_RX_PATH_MIX_CTL, + 5, 0, &rx_int5_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT6_2 MUX", WCD9335_CDC_RX6_RX_PATH_MIX_CTL, + 5, 0, &rx_int6_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", WCD9335_CDC_RX7_RX_PATH_MIX_CTL, + 5, 0, &rx_int7_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", WCD9335_CDC_RX8_RX_PATH_MIX_CTL, + 5, 0, &rx_int8_2_mux, tasha_codec_enable_mix_path, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int0_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int1_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int2_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int3_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int4_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int5_1_mix_inp2_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp0_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp1_mux), + SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int6_1_mix_inp2_mux), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux, tasha_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int1_spline_mix_switch, + ARRAY_SIZE(rx_int1_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int2_spline_mix_switch, + ARRAY_SIZE(rx_int2_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int3_spline_mix_switch, + ARRAY_SIZE(rx_int3_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int4_spline_mix_switch, + ARRAY_SIZE(rx_int4_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int5_spline_mix_switch, + ARRAY_SIZE(rx_int5_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT5 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT6_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT6 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int6_spline_mix_switch, + ARRAY_SIZE(rx_int6_spline_mix_switch)), + SND_SOC_DAPM_MIXER("RX INT6 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int7_spline_mix_switch, + ARRAY_SIZE(rx_int7_spline_mix_switch)), + + SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 SPLINE MIX", SND_SOC_NOPM, 0, 0, + rx_int8_spline_mix_switch, + ARRAY_SIZE(rx_int8_spline_mix_switch)), + + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT5 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT6 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, tasha_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, tasha_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("RX INT5 VBAT", SND_SOC_NOPM, 0, 0, + rx_int5_vbat_mix_switch, + ARRAY_SIZE(rx_int5_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT6 VBAT", SND_SOC_NOPM, 0, 0, + rx_int6_vbat_mix_switch, + ARRAY_SIZE(rx_int6_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT7 VBAT", SND_SOC_NOPM, 0, 0, + rx_int7_vbat_mix_switch, + ARRAY_SIZE(rx_int7_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT8 VBAT", SND_SOC_NOPM, 0, 0, + rx_int8_vbat_mix_switch, + ARRAY_SIZE(rx_int8_vbat_mix_switch), + tasha_codec_vbat_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT0 MIX2 INP", WCD9335_CDC_RX0_RX_PATH_CFG1, 4, + 0, &rx_int0_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT1 MIX2 INP", WCD9335_CDC_RX1_RX_PATH_CFG1, 4, + 0, &rx_int1_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT2 MIX2 INP", WCD9335_CDC_RX2_RX_PATH_CFG1, 4, + 0, &rx_int2_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT3 MIX2 INP", WCD9335_CDC_RX3_RX_PATH_CFG1, 4, + 0, &rx_int3_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT4 MIX2 INP", WCD9335_CDC_RX4_RX_PATH_CFG1, 4, + 0, &rx_int4_mix2_inp_mux), + SND_SOC_DAPM_MUX("RX INT7 MIX2 INP", WCD9335_CDC_RX7_RX_PATH_CFG1, 4, + 0, &rx_int7_mix2_inp_mux), + + SND_SOC_DAPM_MUX("SLIM TX0 MUX", SND_SOC_NOPM, TASHA_TX0, 0, + &sb_tx0_mux), + SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TASHA_TX1, 0, + &sb_tx1_mux), + SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TASHA_TX2, 0, + &sb_tx2_mux), + SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TASHA_TX3, 0, + &sb_tx3_mux), + SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TASHA_TX4, 0, + &sb_tx4_mux), + SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TASHA_TX5, 0, + &sb_tx5_mux), + SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TASHA_TX6, 0, + &sb_tx6_mux), + SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TASHA_TX7, 0, + &sb_tx7_mux), + SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TASHA_TX8, 0, + &sb_tx8_mux), + SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TASHA_TX9, 0, + &sb_tx9_mux), + SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TASHA_TX10, 0, + &sb_tx10_mux), + SND_SOC_DAPM_MUX("SLIM TX11 MUX", SND_SOC_NOPM, TASHA_TX11, 0, + &sb_tx11_mux), + SND_SOC_DAPM_MUX("SLIM TX11 INP1 MUX", SND_SOC_NOPM, TASHA_TX11, 0, + &sb_tx11_inp1_mux), + SND_SOC_DAPM_MUX("SLIM TX13 MUX", SND_SOC_NOPM, TASHA_TX13, 0, + &sb_tx13_mux), + SND_SOC_DAPM_MUX("TX13 INP MUX", SND_SOC_NOPM, 0, 0, + &tx13_inp_mux), + + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD9335_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX1", WCD9335_CDC_TX1_TX_PATH_CTL, 5, 0, + &tx_adc_mux1, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX2", WCD9335_CDC_TX2_TX_PATH_CTL, 5, 0, + &tx_adc_mux2, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX3", WCD9335_CDC_TX3_TX_PATH_CTL, 5, 0, + &tx_adc_mux3, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX4", WCD9335_CDC_TX4_TX_PATH_CTL, 5, 0, + &tx_adc_mux4, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX5", WCD9335_CDC_TX5_TX_PATH_CTL, 5, 0, + &tx_adc_mux5, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX6", WCD9335_CDC_TX6_TX_PATH_CTL, 5, 0, + &tx_adc_mux6, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX7", WCD9335_CDC_TX7_TX_PATH_CTL, 5, 0, + &tx_adc_mux7, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX8", WCD9335_CDC_TX8_TX_PATH_CTL, 5, 0, + &tx_adc_mux8, tasha_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX10", SND_SOC_NOPM, 10, 0, + &tx_adc_mux10, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX11", SND_SOC_NOPM, 11, 0, + &tx_adc_mux11, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX12", SND_SOC_NOPM, 12, 0, + &tx_adc_mux12, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX13", SND_SOC_NOPM, 13, 0, + &tx_adc_mux13, tasha_codec_tx_adc_cfg, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX("DMIC MUX0", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux0), + SND_SOC_DAPM_MUX("DMIC MUX1", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux1), + SND_SOC_DAPM_MUX("DMIC MUX2", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux2), + SND_SOC_DAPM_MUX("DMIC MUX3", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux3), + SND_SOC_DAPM_MUX("DMIC MUX4", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux4), + SND_SOC_DAPM_MUX("DMIC MUX5", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux5), + SND_SOC_DAPM_MUX("DMIC MUX6", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux6), + SND_SOC_DAPM_MUX("DMIC MUX7", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux7), + SND_SOC_DAPM_MUX("DMIC MUX8", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux8), + SND_SOC_DAPM_MUX("DMIC MUX10", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux10), + SND_SOC_DAPM_MUX("DMIC MUX11", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux11), + SND_SOC_DAPM_MUX("DMIC MUX12", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux12), + SND_SOC_DAPM_MUX("DMIC MUX13", SND_SOC_NOPM, 0, 0, + &tx_dmic_mux13), + + SND_SOC_DAPM_MUX("AMIC MUX0", SND_SOC_NOPM, 0, 0, + &tx_amic_mux0), + SND_SOC_DAPM_MUX("AMIC MUX1", SND_SOC_NOPM, 0, 0, + &tx_amic_mux1), + SND_SOC_DAPM_MUX("AMIC MUX2", SND_SOC_NOPM, 0, 0, + &tx_amic_mux2), + SND_SOC_DAPM_MUX("AMIC MUX3", SND_SOC_NOPM, 0, 0, + &tx_amic_mux3), + SND_SOC_DAPM_MUX("AMIC MUX4", SND_SOC_NOPM, 0, 0, + &tx_amic_mux4), + SND_SOC_DAPM_MUX("AMIC MUX5", SND_SOC_NOPM, 0, 0, + &tx_amic_mux5), + SND_SOC_DAPM_MUX("AMIC MUX6", SND_SOC_NOPM, 0, 0, + &tx_amic_mux6), + SND_SOC_DAPM_MUX("AMIC MUX7", SND_SOC_NOPM, 0, 0, + &tx_amic_mux7), + SND_SOC_DAPM_MUX("AMIC MUX8", SND_SOC_NOPM, 0, 0, + &tx_amic_mux8), + SND_SOC_DAPM_MUX("AMIC MUX10", SND_SOC_NOPM, 0, 0, + &tx_amic_mux10), + SND_SOC_DAPM_MUX("AMIC MUX11", SND_SOC_NOPM, 0, 0, + &tx_amic_mux11), + SND_SOC_DAPM_MUX("AMIC MUX12", SND_SOC_NOPM, 0, 0, + &tx_amic_mux12), + SND_SOC_DAPM_MUX("AMIC MUX13", SND_SOC_NOPM, 0, 0, + &tx_amic_mux13), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD9335_ANA_AMIC1, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD9335_ANA_AMIC2, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD9335_ANA_AMIC3, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD9335_ANA_AMIC4, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC5", NULL, WCD9335_ANA_AMIC5, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC6", NULL, WCD9335_ANA_AMIC6, 7, 0, + tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("RX INT1 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHL, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT2 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHR, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT3 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO1, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT4 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO2, 0, tasha_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS3", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS4", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS4_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY(DAPM_LDO_H_STANDALONE, SND_SOC_NOPM, 0, 0, + tasha_codec_force_enable_ldo_h, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("ANC0 FB MUX", SND_SOC_NOPM, 0, 0, &anc0_fb_mux), + SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux), + + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + SND_SOC_DAPM_INPUT("AMIC6"), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, tasha_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, + AIF2_CAP, 0, tasha_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, + AIF3_CAP, 0, tasha_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, + AIF4_VIFEED, 0, tasha_codec_enable_slimvi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0, + aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)), + + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), + + SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, + aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), + + SND_SOC_DAPM_INPUT("VIINPUT"), + + SND_SOC_DAPM_AIF_OUT("AIF5 CPE", "AIF5 CPE TX", 0, SND_SOC_NOPM, + AIF5_CPE_TX, 0), + + SND_SOC_DAPM_MUX_E("EC BUF MUX INP", SND_SOC_NOPM, 0, 0, &ec_buf_mux, + tasha_codec_ec_buf_mux_enable, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0, + tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("IIR0 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp0_mux), + SND_SOC_DAPM_MUX("IIR0 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp1_mux), + SND_SOC_DAPM_MUX("IIR0 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp2_mux), + SND_SOC_DAPM_MUX("IIR0 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp3_mux), + SND_SOC_DAPM_MUX("IIR1 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp0_mux), + SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux), + SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux), + SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux), + + SND_SOC_DAPM_MIXER_E("IIR0", WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL, + 4, 0, NULL, 0, tasha_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER_E("IIR1", WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL, + 4, 0, NULL, 0, tasha_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("SRC0", WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SRC1", WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("CPE IN Mixer", SND_SOC_NOPM, 0, 0, + cpe_in_mix_switch, + ARRAY_SIZE(cpe_in_mix_switch), + tasha_codec_configure_cpe_input, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("RX INT1_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int1_1_native_mux), + SND_SOC_DAPM_MUX("RX INT2_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int2_1_native_mux), + SND_SOC_DAPM_MUX("RX INT3_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int3_1_native_mux), + SND_SOC_DAPM_MUX("RX INT4_1 NATIVE MUX", SND_SOC_NOPM, 0, 0, + &int4_1_native_mux), + SND_SOC_DAPM_MUX("RX MIX TX0 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx0_mux), + SND_SOC_DAPM_MUX("RX MIX TX1 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx1_mux), + SND_SOC_DAPM_MUX("RX MIX TX2 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx2_mux), + SND_SOC_DAPM_MUX("RX MIX TX3 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx3_mux), + SND_SOC_DAPM_MUX("RX MIX TX4 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx4_mux), + SND_SOC_DAPM_MUX("RX MIX TX5 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx5_mux), + SND_SOC_DAPM_MUX("RX MIX TX6 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx6_mux), + SND_SOC_DAPM_MUX("RX MIX TX7 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx7_mux), + SND_SOC_DAPM_MUX("RX MIX TX8 MUX", SND_SOC_NOPM, 0, 0, + &rx_mix_tx8_mux), + + SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int0_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int1_dem_inp_mux), + SND_SOC_DAPM_MUX("RX INT2 DEM MUX", SND_SOC_NOPM, 0, 0, + &rx_int2_dem_inp_mux), + + SND_SOC_DAPM_MUX_E("RX INT0 INTERP", SND_SOC_NOPM, + INTERP_EAR, 0, &rx_int0_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 INTERP", SND_SOC_NOPM, + INTERP_HPHL, 0, &rx_int1_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 INTERP", SND_SOC_NOPM, + INTERP_HPHR, 0, &rx_int2_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3 INTERP", SND_SOC_NOPM, + INTERP_LO1, 0, &rx_int3_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4 INTERP", SND_SOC_NOPM, + INTERP_LO2, 0, &rx_int4_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT5 INTERP", SND_SOC_NOPM, + INTERP_LO3, 0, &rx_int5_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT6 INTERP", SND_SOC_NOPM, + INTERP_LO4, 0, &rx_int6_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7 INTERP", SND_SOC_NOPM, + INTERP_SPKR1, 0, &rx_int7_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8 INTERP", SND_SOC_NOPM, + INTERP_SPKR2, 0, &rx_int8_interp_mux, + tasha_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, WCD9335_ANA_HPH, + 5, 0, tasha_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, WCD9335_ANA_HPH, + 4, 0, tasha_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT5 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT6 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PA", WCD9335_ANA_HPH, 7, 0, NULL, 0, + tasha_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PA", WCD9335_ANA_HPH, 6, 0, NULL, 0, + tasha_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0, + tasha_codec_enable_ear_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD9335_ANA_LO_1_2, 7, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD9335_ANA_LO_1_2, 6, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT3 PA", WCD9335_ANA_LO_3_4, 7, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT4 PA", WCD9335_ANA_LO_3_4, 6, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0, + tasha_codec_enable_ear_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC LINEOUT1 PA", WCD9335_ANA_LO_1_2, + 7, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC LINEOUT2 PA", WCD9335_ANA_LO_1_2, + 6, 0, NULL, 0, + tasha_codec_enable_lineout_pa, + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tasha_codec_enable_spk_anc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + SND_SOC_DAPM_OUTPUT("ANC HPHL"), + SND_SOC_DAPM_OUTPUT("ANC HPHR"), + SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, + tasha_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUTPUT("SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + SND_SOC_DAPM_OUTPUT("LINEOUT3"), + SND_SOC_DAPM_OUTPUT("LINEOUT4"), + SND_SOC_DAPM_OUTPUT("ANC LINEOUT1"), + SND_SOC_DAPM_OUTPUT("ANC LINEOUT2"), + SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM, + ON_DEMAND_MICBIAS, 0, + tasha_codec_enable_on_demand_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD9335_CDC_TX0_TX_PATH_192_CTL, 0, + 0, &adc_us_mux0_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD9335_CDC_TX1_TX_PATH_192_CTL, 0, + 0, &adc_us_mux1_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX2", WCD9335_CDC_TX2_TX_PATH_192_CTL, 0, + 0, &adc_us_mux2_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX3", WCD9335_CDC_TX3_TX_PATH_192_CTL, 0, + 0, &adc_us_mux3_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX4", WCD9335_CDC_TX4_TX_PATH_192_CTL, 0, + 0, &adc_us_mux4_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX5", WCD9335_CDC_TX5_TX_PATH_192_CTL, 0, + 0, &adc_us_mux5_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX6", WCD9335_CDC_TX6_TX_PATH_192_CTL, 0, + 0, &adc_us_mux6_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX7", WCD9335_CDC_TX7_TX_PATH_192_CTL, 0, + 0, &adc_us_mux7_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD9335_CDC_TX8_TX_PATH_192_CTL, 0, + 0, &adc_us_mux8_switch), + /* MAD related widgets */ + SND_SOC_DAPM_AIF_OUT_E("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0, + tasha_codec_enable_mad, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("MAD_SEL MUX", SND_SOC_NOPM, 0, 0, + &mad_sel_mux), + SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"), + SND_SOC_DAPM_INPUT("MADINPUT"), + SND_SOC_DAPM_SWITCH("MADONOFF", SND_SOC_NOPM, 0, 0, + &aif4_mad_switch), + SND_SOC_DAPM_SWITCH("MAD_BROADCAST", SND_SOC_NOPM, 0, 0, + &mad_brdcst_switch), + SND_SOC_DAPM_SWITCH("AIF4", SND_SOC_NOPM, 0, 0, + &aif4_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("ANC HPHL Enable", SND_SOC_NOPM, 0, 0, + &anc_hphl_switch), + SND_SOC_DAPM_SWITCH("ANC HPHR Enable", SND_SOC_NOPM, 0, 0, + &anc_hphr_switch), + SND_SOC_DAPM_SWITCH("ANC EAR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_switch), + SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_spkr_switch), + SND_SOC_DAPM_SWITCH("ANC LINEOUT1 Enable", SND_SOC_NOPM, 0, 0, + &anc_lineout1_switch), + SND_SOC_DAPM_SWITCH("ANC LINEOUT2 Enable", SND_SOC_NOPM, 0, 0, + &anc_lineout2_switch), + SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0, + &anc_spkr_pa_switch), +}; + +static int tasha_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(dai->codec); + u32 i = 0; + struct wcd9xxx_ch *ch; + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + case AIF_MIX1_PB: + if (!rx_slot || !rx_num) { + pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n", + __func__, rx_slot, rx_num); + return -EINVAL; + } + list_for_each_entry(ch, &tasha_p->dai[dai->id].wcd9xxx_ch_list, + list) { + pr_debug("%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + rx_slot[i++] = ch->ch_num; + } + pr_debug("%s: rx_num %d\n", __func__, i); + *rx_num = i; + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + case AIF4_MAD_TX: + case AIF4_VIFEED: + if (!tx_slot || !tx_num) { + pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n", + __func__, tx_slot, tx_num); + return -EINVAL; + } + list_for_each_entry(ch, &tasha_p->dai[dai->id].wcd9xxx_ch_list, + list) { + pr_debug("%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + tx_slot[i++] = ch->ch_num; + } + pr_debug("%s: tx_num %d\n", __func__, i); + *tx_num = i; + break; + + default: + pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id); + break; + } + + return 0; +} + +static int tasha_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 tasha_priv *tasha; + struct wcd9xxx *core; + struct wcd9xxx_codec_dai_data *dai_data = NULL; + + if (!dai) { + pr_err("%s: dai is empty\n", __func__); + return -EINVAL; + } + tasha = snd_soc_codec_get_drvdata(dai->codec); + core = dev_get_drvdata(dai->codec->dev->parent); + + if (!tx_slot || !rx_slot) { + pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n", + __func__, tx_slot, rx_slot); + return -EINVAL; + } + pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n" + "tasha->intf_type %d\n", + __func__, dai->name, dai->id, tx_num, rx_num, + tasha->intf_type); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + wcd9xxx_init_slimslave(core, core->slim->laddr, + tx_num, tx_slot, rx_num, rx_slot); + /* Reserve TX12/TX13 for MAD data channel */ + dai_data = &tasha->dai[AIF4_MAD_TX]; + if (dai_data) { + if (TASHA_IS_2_0(tasha->wcd9xxx)) + list_add_tail(&core->tx_chs[TASHA_TX13].list, + &dai_data->wcd9xxx_ch_list); + else + list_add_tail(&core->tx_chs[TASHA_TX12].list, + &dai_data->wcd9xxx_ch_list); + } + } + return 0; +} + +static int tasha_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + return 0; +} + +static void tasha_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + return; + + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && + test_bit(SB_CLK_GEAR, &tasha->status_mask)) { + tasha_codec_vote_max_bw(dai->codec, false); + clear_bit(SB_CLK_GEAR, &tasha->status_mask); + } +} + +static int tasha_set_decimator_rate(struct snd_soc_dai *dai, + u8 tx_fs_rate_reg_val, u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u32 tx_port = 0; + u8 shift = 0, shift_val = 0, tx_mux_sel = 0; + int decimator = -1; + u16 tx_port_reg = 0, tx_fs_reg = 0; + + list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) { + tx_port = ch->port; + dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d", + __func__, dai->id, tx_port); + + if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) { + dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n", + __func__, tx_port, dai->id); + return -EINVAL; + } + /* Find the SB TX MUX input - which decimator is connected */ + if (tx_port < 4) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0; + shift = (tx_port << 1); + shift_val = 0x03; + } else if ((tx_port >= 4) && (tx_port < 8)) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1; + shift = ((tx_port - 4) << 1); + shift_val = 0x03; + } else if ((tx_port >= 8) && (tx_port < 11)) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2; + shift = ((tx_port - 8) << 1); + shift_val = 0x03; + } else if (tx_port == 11) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 0; + shift_val = 0x0F; + } else if (tx_port == 13) { + tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 4; + shift_val = 0x03; + } + tx_mux_sel = snd_soc_read(codec, tx_port_reg) & + (shift_val << shift); + tx_mux_sel = tx_mux_sel >> shift; + + if (tx_port <= 8) { + if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3)) + decimator = tx_port; + } else if (tx_port <= 10) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = ((tx_port == 9) ? 7 : 6); + } else if (tx_port == 11) { + if ((tx_mux_sel >= 1) && (tx_mux_sel < 7)) + decimator = tx_mux_sel - 1; + } else if (tx_port == 13) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = 5; + } + + if (decimator >= 0) { + tx_fs_reg = WCD9335_CDC_TX0_TX_PATH_CTL + + 16 * decimator; + dev_dbg(codec->dev, "%s: set DEC%u (-> SLIM_TX%u) rate to %u\n", + __func__, decimator, tx_port, sample_rate); + snd_soc_update_bits(codec, tx_fs_reg, 0x0F, + tx_fs_rate_reg_val); + } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) { + /* Check if the TX Mux input is RX MIX TXn */ + dev_dbg(codec->dev, "%s: RX_MIX_TX%u going to SLIM TX%u\n", + __func__, tx_port, tx_port); + } else { + dev_err(codec->dev, "%s: ERROR: Invalid decimator: %d\n", + __func__, decimator); + return -EINVAL; + } + } + return 0; +} + +static int tasha_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 int_mix_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp; + u32 j; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) { + int_2_inp = ch->port + INTn_2_INP_SEL_RX0 - + TASHA_RX_PORT_START_NUMBER; + if ((int_2_inp < INTn_2_INP_SEL_RX0) || + (int_2_inp > INTn_2_INP_SEL_RX7)) { + pr_err("%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - TASHA_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg1 = WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < TASHA_NUM_INTERPOLATORS; j++) { + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) & + 0x0F; + if (int_mux_cfg1_val == int_2_inp) { + int_fs_reg = WCD9335_CDC_RX0_RX_PATH_MIX_CTL + + 20 * j; + pr_debug("%s: AIF_MIX_PB DAI(%d) connected to INT%u_2\n", + __func__, dai->id, j); + pr_debug("%s: set INT%u_2 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, + 0x0F, int_mix_fs_rate_reg_val); + } + int_mux_cfg1 += 2; + } + } + return 0; +} + +static int tasha_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 int_prim_fs_rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp; + u32 j; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) { + int_1_mix1_inp = ch->port + INTn_1_MIX_INP_SEL_RX0 - + TASHA_RX_PORT_START_NUMBER; + if ((int_1_mix1_inp < INTn_1_MIX_INP_SEL_RX0) || + (int_1_mix1_inp > INTn_1_MIX_INP_SEL_RX7)) { + pr_err("%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - TASHA_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg0 = WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0; + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the slim rx port + * is connected + */ + for (j = 0; j < TASHA_NUM_INTERPOLATORS; j++) { + int_mux_cfg1 = int_mux_cfg0 + 1; + + int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & 0x0F; + inp1_sel = (int_mux_cfg0_val >> 4) & 0x0F; + inp2_sel = (int_mux_cfg1_val >> 4) & 0x0F; + if ((inp0_sel == int_1_mix1_inp) || + (inp1_sel == int_1_mix1_inp) || + (inp2_sel == int_1_mix1_inp)) { + int_fs_reg = WCD9335_CDC_RX0_RX_PATH_CTL + + 20 * j; + pr_debug("%s: AIF_PB DAI(%d) connected to INT%u_1\n", + __func__, dai->id, j); + pr_debug("%s: set INT%u_1 sample rate to %u\n", + __func__, j, sample_rate); + /* sample_rate is in Hz */ + if ((j == 0) && (sample_rate == 44100)) { + pr_info("%s: Cannot set 44.1KHz on INT0\n", + __func__); + } else + snd_soc_update_bits(codec, int_fs_reg, + 0x0F, int_prim_fs_rate_reg_val); + } + int_mux_cfg0 += 2; + } + } + + return 0; +} + + +static int tasha_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + int rate_val = 0; + int i, ret; + + /* set mixing path rate */ + for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) { + if (sample_rate == + int_mix_sample_rate_val[i].sample_rate) { + rate_val = + int_mix_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) || + (rate_val < 0)) + goto prim_rate; + ret = tasha_set_mix_interpolator_rate(dai, + (u8) rate_val, sample_rate); +prim_rate: + /* set primary path sample rate */ + for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) { + if (sample_rate == + int_prim_sample_rate_val[i].sample_rate) { + rate_val = + int_prim_sample_rate_val[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) || + (rate_val < 0)) + return -EINVAL; + ret = tasha_set_prim_interpolator_rate(dai, + (u8) rate_val, sample_rate); + return ret; +} + +static int tasha_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); + + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && + test_bit(SB_CLK_GEAR, &tasha->status_mask)) { + tasha_codec_vote_max_bw(dai->codec, false); + clear_bit(SB_CLK_GEAR, &tasha->status_mask); + } + return 0; +} + +static int tasha_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); + int ret; + int tx_fs_rate = -EINVAL; + int rx_fs_rate = -EINVAL; + int i2s_bit_mode; + struct snd_soc_codec *codec = dai->codec; + + pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__, + dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = tasha_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + pr_err("%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16: + tasha->dai[dai->id].bit_width = 16; + i2s_bit_mode = 0x01; + break; + case 24: + tasha->dai[dai->id].bit_width = 24; + i2s_bit_mode = 0x00; + break; + default: + return -EINVAL; + } + tasha->dai[dai->id].rate = params_rate(params); + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + switch (params_rate(params)) { + case 8000: + rx_fs_rate = 0; + break; + case 16000: + rx_fs_rate = 1; + break; + case 32000: + rx_fs_rate = 2; + break; + case 48000: + rx_fs_rate = 3; + break; + case 96000: + rx_fs_rate = 4; + break; + case 192000: + rx_fs_rate = 5; + break; + default: + dev_err(tasha->dev, + "%s: Invalid RX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + }; + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x20, i2s_bit_mode << 5); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x1c, (rx_fs_rate << 2)); + } + break; + case SNDRV_PCM_STREAM_CAPTURE: + switch (params_rate(params)) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + case 384000: + tx_fs_rate = 7; + break; + default: + dev_err(tasha->dev, "%s: Invalid TX sample rate: %d\n", + __func__, params_rate(params)); + return -EINVAL; + + }; + if (dai->id != AIF4_VIFEED && + dai->id != AIF4_MAD_TX) { + ret = tasha_set_decimator_rate(dai, tx_fs_rate, + params_rate(params)); + if (ret < 0) { + dev_err(tasha->dev, "%s: cannot set TX Decimator rate: %d\n", + __func__, tx_fs_rate); + return ret; + } + } + tasha->dai[dai->id].rate = params_rate(params); + switch (params_width(params)) { + case 16: + tasha->dai[dai->id].bit_width = 16; + i2s_bit_mode = 0x01; + break; + case 24: + tasha->dai[dai->id].bit_width = 24; + i2s_bit_mode = 0x00; + break; + case 32: + tasha->dai[dai->id].bit_width = 32; + i2s_bit_mode = 0x00; + break; + default: + dev_err(tasha->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + }; + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x20, i2s_bit_mode << 5); + if (tx_fs_rate > 1) + tx_fs_rate--; + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x1c, tx_fs_rate << 2); + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG, + 0x05, 0x05); + + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG, + 0x05, 0x05); + + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG, + 0x05, 0x05); + + snd_soc_update_bits(codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG, + 0x05, 0x05); + } + break; + default: + pr_err("%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + }; + if (dai->id == AIF4_VIFEED) + tasha->dai[dai->id].bit_width = 32; + + return 0; +} + +static int tasha_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* CPU is master */ + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + if (dai->id == AIF1_CAP) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x2, 0); + else if (dai->id == AIF1_PB) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x2, 0); + } + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* CPU is slave */ + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + if (dai->id == AIF1_CAP) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL, + 0x2, 0x2); + else if (dai->id == AIF1_PB) + snd_soc_update_bits(dai->codec, + WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL, + 0x2, 0x2); + } + break; + default: + return -EINVAL; + } + return 0; +} + +static int tasha_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + pr_debug("%s\n", __func__); + return 0; +} + +static struct snd_soc_dai_ops tasha_dai_ops = { + .startup = tasha_startup, + .shutdown = tasha_shutdown, + .hw_params = tasha_hw_params, + .prepare = tasha_prepare, + .set_sysclk = tasha_set_dai_sysclk, + .set_fmt = tasha_set_dai_fmt, + .set_channel_map = tasha_set_channel_map, + .get_channel_map = tasha_get_channel_map, +}; + +static struct snd_soc_dai_driver tasha_dai[] = { + { + .name = "tasha_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_mix_rx1", + .id = AIF_MIX1_PB, + .playback = { + .stream_name = "AIF Mix Playback", + .rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_mad1", + .id = AIF4_MAD_TX, + .capture = { + .stream_name = "AIF4 MAD TX", + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_min = 16000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_vifeedback", + .id = AIF4_VIFEED, + .capture = { + .stream_name = "VIfeed", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_cpe", + .id = AIF5_CPE_TX, + .capture = { + .stream_name = "AIF5 CPE TX", + .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, + .formats = TASHA_FORMATS_S16_S24_S32_LE, + .rate_min = 16000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 1, + }, + }, +}; + +static struct snd_soc_dai_driver tasha_i2s_dai[] = { + { + .name = "tasha_i2s_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_i2s_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_i2s_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tasha_dai_ops, + }, + { + .name = "tasha_i2s_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD9335_RATES_MASK, + .formats = TASHA_FORMATS_S16_S24_LE, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tasha_dai_ops, + }, +}; + +static void tasha_codec_power_gate_digital_core(struct tasha_priv *tasha) +{ + struct snd_soc_codec *codec = tasha->codec; + + if (!codec) + return; + + mutex_lock(&tasha->power_lock); + dev_dbg(codec->dev, "%s: Entering power gating function, %d\n", + __func__, tasha->power_active_ref); + + if (tasha->power_active_ref > 0) + goto exit; + + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_BEGIN, + WCD9XXX_DIG_CORE_REGION_1); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, + 0x02, 0x00); + clear_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, sido_buck_svs_voltage); + wcd9xxx_set_power_state(tasha->wcd9xxx, WCD_REGION_POWER_DOWN, + WCD9XXX_DIG_CORE_REGION_1); +exit: + dev_dbg(codec->dev, "%s: Exiting power gating function, %d\n", + __func__, tasha->power_active_ref); + mutex_unlock(&tasha->power_lock); +} + +static void tasha_codec_power_gate_work(struct work_struct *work) +{ + struct tasha_priv *tasha; + struct delayed_work *dwork; + struct snd_soc_codec *codec; + + dwork = to_delayed_work(work); + tasha = container_of(dwork, struct tasha_priv, power_gate_work); + codec = tasha->codec; + + if (!codec) + return; + + tasha_codec_power_gate_digital_core(tasha); +} + +/* called under power_lock acquisition */ +static int tasha_dig_core_remove_power_collapse(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + tasha_codec_vote_max_bw(codec, true); + snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5); + snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7); + snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_RST_CTL, 0x02, 0x00); + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_RST_CTL, 0x02, 0x02); + + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + regcache_mark_dirty(codec->component.regmap); + regcache_sync_region(codec->component.regmap, + TASHA_DIG_CORE_REG_MIN, TASHA_DIG_CORE_REG_MAX); + tasha_codec_vote_max_bw(codec, false); + + return 0; +} + +static int tasha_dig_core_power_collapse(struct tasha_priv *tasha, + int req_state) +{ + struct snd_soc_codec *codec; + int cur_state; + + /* Exit if feature is disabled */ + if (!dig_core_collapse_enable) + return 0; + + mutex_lock(&tasha->power_lock); + if (req_state == POWER_COLLAPSE) + tasha->power_active_ref--; + else if (req_state == POWER_RESUME) + tasha->power_active_ref++; + else + goto unlock_mutex; + + if (tasha->power_active_ref < 0) { + dev_dbg(tasha->dev, "%s: power_active_ref is negative\n", + __func__); + goto unlock_mutex; + } + + codec = tasha->codec; + if (!codec) + goto unlock_mutex; + + if (req_state == POWER_COLLAPSE) { + if (tasha->power_active_ref == 0) { + schedule_delayed_work(&tasha->power_gate_work, + msecs_to_jiffies(dig_core_collapse_timer * 1000)); + } + } else if (req_state == POWER_RESUME) { + if (tasha->power_active_ref == 1) { + /* + * At this point, there can be two cases: + * 1. Core already in power collapse state + * 2. Timer kicked in and still did not expire or + * waiting for the power_lock + */ + cur_state = wcd9xxx_get_current_power_state( + tasha->wcd9xxx, + WCD9XXX_DIG_CORE_REGION_1); + if (cur_state == WCD_REGION_POWER_DOWN) + tasha_dig_core_remove_power_collapse(codec); + else { + mutex_unlock(&tasha->power_lock); + cancel_delayed_work_sync( + &tasha->power_gate_work); + mutex_lock(&tasha->power_lock); + } + } + } + +unlock_mutex: + mutex_unlock(&tasha->power_lock); + + return 0; +} + +static int __tasha_cdc_mclk_enable_locked(struct tasha_priv *tasha, + bool enable) +{ + int ret = 0; + + if (!tasha->wcd_ext_clk) { + dev_err(tasha->dev, "%s: wcd ext clock is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(tasha->dev, "%s: mclk_enable = %u\n", __func__, enable); + + if (enable) { + tasha_dig_core_power_collapse(tasha, POWER_RESUME); + ret = tasha_cdc_req_mclk_enable(tasha, true); + if (ret) + goto err; + + set_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha_codec_apply_sido_voltage(tasha, + SIDO_VOLTAGE_NOMINAL_MV); + } else { + if (!dig_core_collapse_enable) { + clear_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, + sido_buck_svs_voltage); + } + tasha_cdc_req_mclk_enable(tasha, false); + tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE); + } + +err: + return ret; +} + +static int __tasha_cdc_mclk_enable(struct tasha_priv *tasha, + bool enable) +{ + int ret; + + WCD9XXX_V2_BG_CLK_LOCK(tasha->resmgr); + ret = __tasha_cdc_mclk_enable_locked(tasha, enable); + WCD9XXX_V2_BG_CLK_UNLOCK(tasha->resmgr); + + return ret; +} + +int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, bool dapm) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + return __tasha_cdc_mclk_enable(tasha, enable); +} +EXPORT_SYMBOL(tasha_cdc_mclk_enable); + +int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, bool dapm) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(tasha->dev, "%s: clk_mode: %d, enable: %d, clk_internal: %d\n", + __func__, tasha->clk_mode, enable, tasha->clk_internal); + if (tasha->clk_mode || tasha->clk_internal) { + if (enable) { + tasha_cdc_sido_ccl_enable(tasha, true); + wcd_resmgr_enable_master_bias(tasha->resmgr); + tasha_dig_core_power_collapse(tasha, POWER_RESUME); + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + set_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, + SIDO_VOLTAGE_NOMINAL_MV); + tasha->clk_internal = true; + } else { + tasha->clk_internal = false; + clear_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, + sido_buck_svs_voltage); + tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE); + wcd_resmgr_disable_master_bias(tasha->resmgr); + tasha_cdc_sido_ccl_enable(tasha, false); + } + } else { + ret = __tasha_cdc_mclk_enable(tasha, enable); + } + return ret; +} +EXPORT_SYMBOL(tasha_cdc_mclk_tx_enable); + +static ssize_t tasha_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + char buffer[TASHA_VERSION_ENTRY_SIZE]; + int len = 0; + + tasha = (struct tasha_priv *) entry->private_data; + if (!tasha) { + pr_err("%s: tasha priv is null\n", __func__); + return -EINVAL; + } + + wcd9xxx = tasha->wcd9xxx; + + if (wcd9xxx->codec_type->id_major == TASHA_MAJOR) { + if (TASHA_IS_1_0(wcd9xxx)) + len = snprintf(buffer, sizeof(buffer), "WCD9335_1_0\n"); + else if (TASHA_IS_1_1(wcd9xxx)) + len = snprintf(buffer, sizeof(buffer), "WCD9335_1_1\n"); + else + snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } else if (wcd9xxx->codec_type->id_major == TASHA2P0_MAJOR) { + len = snprintf(buffer, sizeof(buffer), "WCD9335_2_0\n"); + } else + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops tasha_codec_info_ops = { + .read = tasha_codec_version_read, +}; + +/* + * tasha_codec_info_create_codec_entry - creates wcd9335 module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wcd9335 module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int tasha_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct tasha_priv *tasha; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + tasha = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + tasha->entry = snd_info_create_subdir(codec_root->module, + "tasha", codec_root); + if (!tasha->entry) { + dev_dbg(codec->dev, "%s: failed to create wcd9335 entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + tasha->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wcd9335 version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = tasha; + version_entry->size = TASHA_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &tasha_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + tasha->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(tasha_codec_info_create_codec_entry); + +static int __tasha_codec_internal_rco_ctrl( + struct snd_soc_codec *codec, bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (enable) { + tasha_cdc_sido_ccl_enable(tasha, true); + if (wcd_resmgr_get_clk_type(tasha->resmgr) == + WCD_CLK_RCO) { + ret = wcd_resmgr_enable_clk_block(tasha->resmgr, + WCD_CLK_RCO); + } else { + ret = tasha_cdc_req_mclk_enable(tasha, true); + ret |= wcd_resmgr_enable_clk_block(tasha->resmgr, + WCD_CLK_RCO); + ret |= tasha_cdc_req_mclk_enable(tasha, false); + } + + } else { + ret = wcd_resmgr_disable_clk_block(tasha->resmgr, + WCD_CLK_RCO); + tasha_cdc_sido_ccl_enable(tasha, false); + } + + if (ret) { + dev_err(codec->dev, "%s: Error in %s RCO\n", + __func__, (enable ? "enabling" : "disabling")); + ret = -EINVAL; + } + + return ret; +} + +/* + * tasha_codec_internal_rco_ctrl() + * Make sure that the caller does not acquire + * BG_CLK_LOCK. + */ +static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + WCD9XXX_V2_BG_CLK_LOCK(tasha->resmgr); + ret = __tasha_codec_internal_rco_ctrl(codec, enable); + WCD9XXX_V2_BG_CLK_UNLOCK(tasha->resmgr); + return ret; +} + +/* + * tasha_mbhc_hs_detect: starts mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + * @mbhc_cfg: handle to mbhc configuration structure + * return 0 if mbhc_start is success or error code in case of failure + */ +int tasha_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + return wcd_mbhc_start(&tasha->mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(tasha_mbhc_hs_detect); + +/* + * tasha_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + */ +void tasha_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + wcd_mbhc_stop(&tasha->mbhc); +} +EXPORT_SYMBOL(tasha_mbhc_hs_detect_exit); + +static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} + +static const struct tasha_reg_mask_val tasha_reg_update_reset_val_1_1[] = { + {WCD9335_RCO_CTRL_2, 0xFF, 0x47}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0xFF, 0x60}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_val_1_1[] = { + {WCD9335_FLYBACK_VNEG_DAC_CTRL_1, 0xFF, 0x65}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_2, 0xFF, 0x52}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_3, 0xFF, 0xAF}, + {WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0xFF, 0x60}, + {WCD9335_FLYBACK_VNEG_CTRL_3, 0xFF, 0xF4}, + {WCD9335_FLYBACK_VNEG_CTRL_9, 0xFF, 0x40}, + {WCD9335_FLYBACK_VNEG_CTRL_2, 0xFF, 0x4F}, + {WCD9335_FLYBACK_EN, 0xFF, 0x6E}, + {WCD9335_CDC_RX2_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_CDC_RX1_RX_PATH_SEC0, 0xF8, 0xF8}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_val_1_0[] = { + {WCD9335_FLYBACK_VNEG_CTRL_3, 0xFF, 0x54}, + {WCD9335_CDC_RX2_RX_PATH_SEC0, 0xFC, 0xFC}, + {WCD9335_CDC_RX1_RX_PATH_SEC0, 0xFC, 0xFC}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_val_2_0[] = { + {WCD9335_RCO_CTRL_2, 0x0F, 0x08}, + {WCD9335_RX_BIAS_FLYB_MID_RST, 0xF0, 0x10}, + {WCD9335_FLYBACK_CTRL_1, 0x20, 0x20}, + {WCD9335_HPH_OCP_CTL, 0xFF, 0x7A}, + {WCD9335_HPH_L_TEST, 0x01, 0x01}, + {WCD9335_HPH_R_TEST, 0x01, 0x01}, + {WCD9335_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {WCD9335_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x1E, 0x18}, + {WCD9335_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {WCD9335_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x1E, 0x18}, + {WCD9335_CDC_TX0_TX_PATH_SEC7, 0xFF, 0x45}, + {WCD9335_CDC_RX0_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_HPH_REFBUFF_LP_CTL, 0x08, 0x08}, + {WCD9335_HPH_REFBUFF_LP_CTL, 0x06, 0x02}, + {WCD9335_DIFF_LO_CORE_OUT_PROG, 0xFC, 0xA0}, + {WCD9335_SE_LO_COM1, 0xFF, 0xC0}, + {WCD9335_CDC_RX3_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_CDC_RX4_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD9335_CDC_RX5_RX_PATH_SEC0, 0xFC, 0xF8}, + {WCD9335_CDC_RX6_RX_PATH_SEC0, 0xFC, 0xF8}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_defaults[] = { + {WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x00}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x01}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x04, 0x04}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_i2c_defaults[] = { + {WCD9335_ANA_CLK_TOP, 0x20, 0x20}, + {WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x01}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x00}, + {WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG, 0x01, 0x01}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG, 0x05, 0x05}, + {WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG, 0x05, 0x05}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_common_val[] = { + /* Rbuckfly/R_EAR(32) */ + {WCD9335_CDC_CLSH_K2_MSB, 0x0F, 0x00}, + {WCD9335_CDC_CLSH_K2_LSB, 0xFF, 0x60}, + {WCD9335_CPE_SS_DMIC_CFG, 0x80, 0x00}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x70, 0x50}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x70, 0x50}, + {WCD9335_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08}, + {WCD9335_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08}, + {WCD9335_ANA_LO_1_2, 0x3C, 0X3C}, + {WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x70, 0x00}, + {WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03, 0x03}, + {WCD9335_CDC_TOP_TOP_CFG1, 0x02, 0x02}, + {WCD9335_CDC_TOP_TOP_CFG1, 0x01, 0x01}, + {WCD9335_EAR_CMBUFF, 0x08, 0x00}, + {WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD9335_CDC_RX0_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX1_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX2_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX3_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX4_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX5_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX6_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX7_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX8_RX_PATH_CFG0, 0x01, 0x01}, + {WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x01, 0x01}, + {WCD9335_VBADC_IBIAS_FE, 0x0C, 0x08}, +}; + +static const struct tasha_reg_mask_val tasha_codec_reg_init_1_x_val[] = { + /* Enable TX HPF Filter & Linear Phase */ + {WCD9335_CDC_TX0_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX1_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX2_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX3_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX4_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX5_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX6_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX7_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_TX8_TX_PATH_CFG0, 0x11, 0x11}, + {WCD9335_CDC_RX0_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_CDC_RX0_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX1_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX2_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX3_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX4_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX5_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX6_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX7_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX8_RX_PATH_SEC1, 0x08, 0x08}, + {WCD9335_CDC_RX0_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX1_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX2_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX3_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX4_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX5_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX6_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x08, 0x08}, + {WCD9335_CDC_TX0_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX1_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX2_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX3_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX4_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX5_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX6_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX7_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_TX8_TX_PATH_SEC2, 0x01, 0x01}, + {WCD9335_CDC_RX3_RX_PATH_SEC0, 0xF8, 0xF0}, + {WCD9335_CDC_RX4_RX_PATH_SEC0, 0xF8, 0xF0}, + {WCD9335_CDC_RX5_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_CDC_RX6_RX_PATH_SEC0, 0xF8, 0xF8}, + {WCD9335_RX_OCP_COUNT, 0xFF, 0xFF}, + {WCD9335_HPH_OCP_CTL, 0xF0, 0x70}, + {WCD9335_CPE_SS_CPAR_CFG, 0xFF, 0x00}, + {WCD9335_FLYBACK_VNEG_CTRL_1, 0xFF, 0x63}, + {WCD9335_FLYBACK_VNEG_CTRL_4, 0xFF, 0x7F}, + {WCD9335_CLASSH_CTRL_VCL_1, 0xFF, 0x60}, + {WCD9335_CLASSH_CTRL_CCL_5, 0xFF, 0x40}, + {WCD9335_RX_TIMER_DIV, 0xFF, 0x32}, + {WCD9335_SE_LO_COM2, 0xFF, 0x01}, + {WCD9335_MBHC_ZDET_ANA_CTL, 0x0F, 0x07}, + {WCD9335_RX_BIAS_HPH_PA, 0xF0, 0x60}, + {WCD9335_HPH_RDAC_LDO_CTL, 0x88, 0x88}, + {WCD9335_HPH_L_EN, 0x20, 0x20}, + {WCD9335_HPH_R_EN, 0x20, 0x20}, + {WCD9335_DIFF_LO_CORE_OUT_PROG, 0xFC, 0xD8}, + {WCD9335_CDC_RX5_RX_PATH_SEC3, 0xBD, 0xBD}, + {WCD9335_CDC_RX6_RX_PATH_SEC3, 0xBD, 0xBD}, + {WCD9335_DIFF_LO_COM_PA_FREQ, 0x70, 0x40}, +}; + +static void tasha_update_reg_reset_values(struct snd_soc_codec *codec) +{ + u32 i; + struct wcd9xxx *tasha_core = dev_get_drvdata(codec->dev->parent); + + if (TASHA_IS_1_1(tasha_core)) { + for (i = 0; i < ARRAY_SIZE(tasha_reg_update_reset_val_1_1); + i++) + snd_soc_write(codec, + tasha_reg_update_reset_val_1_1[i].reg, + tasha_reg_update_reset_val_1_1[i].val); + } +} + +static void tasha_codec_init_reg(struct snd_soc_codec *codec) +{ + u32 i; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_common_val); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_common_val[i].reg, + tasha_codec_reg_init_common_val[i].mask, + tasha_codec_reg_init_common_val[i].val); + + if (TASHA_IS_1_1(wcd9xxx) || + TASHA_IS_1_0(wcd9xxx)) + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_1_x_val); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_1_x_val[i].reg, + tasha_codec_reg_init_1_x_val[i].mask, + tasha_codec_reg_init_1_x_val[i].val); + + if (TASHA_IS_1_1(wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_1); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_val_1_1[i].reg, + tasha_codec_reg_init_val_1_1[i].mask, + tasha_codec_reg_init_val_1_1[i].val); + } else if (TASHA_IS_1_0(wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_0); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_val_1_0[i].reg, + tasha_codec_reg_init_val_1_0[i].mask, + tasha_codec_reg_init_val_1_0[i].val); + } else if (TASHA_IS_2_0(wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_2_0); i++) + snd_soc_update_bits(codec, + tasha_codec_reg_init_val_2_0[i].reg, + tasha_codec_reg_init_val_2_0[i].mask, + tasha_codec_reg_init_val_2_0[i].val); + } +} + +static void tasha_update_reg_defaults(struct tasha_priv *tasha) +{ + u32 i; + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tasha->wcd9xxx; + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_defaults); i++) + regmap_update_bits(wcd9xxx->regmap, + tasha_codec_reg_defaults[i].reg, + tasha_codec_reg_defaults[i].mask, + tasha_codec_reg_defaults[i].val); + + tasha->intf_type = wcd9xxx_get_intf_type(); + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_i2c_defaults); i++) + regmap_update_bits(wcd9xxx->regmap, + tasha_codec_reg_i2c_defaults[i].reg, + tasha_codec_reg_i2c_defaults[i].mask, + tasha_codec_reg_i2c_defaults[i].val); + +} + +static void tasha_slim_interface_init_reg(struct snd_soc_codec *codec) +{ + int i; + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) + wcd9xxx_interface_reg_write(priv->wcd9xxx, + TASHA_SLIM_PGD_PORT_INT_EN0 + i, + 0xFF); +} + +static irqreturn_t tasha_slimbus_irq(int irq, void *data) +{ + struct tasha_priv *priv = data; + unsigned long status = 0; + int i, j, port_id, k; + u32 bit; + u8 val, int_val = 0; + bool tx, cleared; + unsigned short reg = 0; + + for (i = TASHA_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= TASHA_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + val = wcd9xxx_interface_reg_read(priv->wcd9xxx, i); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = (j >= 16 ? true : false); + port_id = (tx ? j - 16 : j); + val = wcd9xxx_interface_reg_read(priv->wcd9xxx, + TASHA_SLIM_PGD_PORT_INT_RX_SOURCE0 + j); + if (val) { + if (!tx) + reg = TASHA_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + priv->wcd9xxx, reg); + /* + * Ignore interrupts for ports for which the + * interrupts are not specifically enabled. + */ + if (!(int_val & (1 << (port_id % 8)))) + continue; + } + if (val & TASHA_SLIM_IRQ_OVERFLOW) + pr_err_ratelimited( + "%s: overflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if (val & TASHA_SLIM_IRQ_UNDERFLOW) + pr_err_ratelimited( + "%s: underflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if ((val & TASHA_SLIM_IRQ_OVERFLOW) || + (val & TASHA_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = TASHA_SLIM_PGD_PORT_INT_EN0 + + (port_id / 8); + else + reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + priv->wcd9xxx, reg); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + wcd9xxx_interface_reg_write(priv->wcd9xxx, + reg, int_val); + } + } + if (val & TASHA_SLIM_IRQ_PORT_CLOSED) { + /* + * INT SOURCE register starts from RX to TX + * but port number in the ch_mask is in opposite way + */ + bit = (tx ? j - 16 : j + 16); + pr_debug("%s: %s port %d closed value %x, bit %u\n", + __func__, (tx ? "TX" : "RX"), port_id, val, + bit); + for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) { + pr_debug("%s: priv->dai[%d].ch_mask = 0x%lx\n", + __func__, k, priv->dai[k].ch_mask); + if (test_and_clear_bit(bit, + &priv->dai[k].ch_mask)) { + cleared = true; + if (!priv->dai[k].ch_mask) + wake_up(&priv->dai[k].dai_wait); + /* + * There are cases when multiple DAIs + * might be using the same slimbus + * channel. Hence don't break here. + */ + } + } + WARN(!cleared, + "Couldn't find slimbus %s port %d for closing\n", + (tx ? "TX" : "RX"), port_id); + } + wcd9xxx_interface_reg_write(priv->wcd9xxx, + TASHA_SLIM_PGD_PORT_INT_CLR_RX_0 + + (j / 8), + 1 << (j % 8)); + } + + return IRQ_HANDLED; +} + +static int tasha_setup_irqs(struct tasha_priv *tasha) +{ + int ret = 0; + struct snd_soc_codec *codec = tasha->codec; + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS, + tasha_slimbus_irq, "SLIMBUS Slave", tasha); + if (ret) + pr_err("%s: Failed to request irq %d\n", __func__, + WCD9XXX_IRQ_SLIMBUS); + else + tasha_slim_interface_init_reg(codec); + + return ret; +} + +static void tasha_init_slim_slave_cfg(struct snd_soc_codec *codec) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + struct afe_param_cdc_slimbus_slave_cfg *cfg; + struct wcd9xxx *wcd9xxx = priv->wcd9xxx; + uint64_t eaddr = 0; + + cfg = &priv->slimbus_slave_cfg; + cfg->minor_version = 1; + cfg->tx_slave_port_offset = 0; + cfg->rx_slave_port_offset = 16; + + memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr)); + WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6); + cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF; + cfg->device_enum_addr_msw = eaddr >> 32; + + dev_dbg(codec->dev, "%s: slimbus logical address 0x%llx\n", + __func__, eaddr); +} + +static void tasha_cleanup_irqs(struct tasha_priv *tasha) +{ + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tasha); +} + +static int tasha_handle_pdata(struct tasha_priv *tasha, + struct wcd9xxx_pdata *pdata) +{ + struct snd_soc_codec *codec = tasha->codec; + u8 dmic_ctl_val, mad_dmic_ctl_val; + u8 anc_ctl_value; + u32 def_dmic_rate, dmic_clk_drv; + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + int rc = 0; + + if (!pdata) { + dev_err(codec->dev, "%s: NULL pdata\n", __func__); + return -ENODEV; + } + + /* set micbias voltage */ + vout_ctl_1 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb1_mv); + vout_ctl_2 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb2_mv); + vout_ctl_3 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb3_mv); + vout_ctl_4 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb4_mv); + if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || + vout_ctl_3 < 0 || vout_ctl_4 < 0) { + rc = -EINVAL; + goto done; + } + snd_soc_update_bits(codec, WCD9335_ANA_MICB1, 0x3F, vout_ctl_1); + snd_soc_update_bits(codec, WCD9335_ANA_MICB2, 0x3F, vout_ctl_2); + snd_soc_update_bits(codec, WCD9335_ANA_MICB3, 0x3F, vout_ctl_3); + snd_soc_update_bits(codec, WCD9335_ANA_MICB4, 0x3F, vout_ctl_4); + + /* Set the DMIC sample rate */ + switch (pdata->mclk_rate) { + case TASHA_MCLK_CLK_9P6MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + break; + case TASHA_MCLK_CLK_12P288MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ; + break; + default: + /* should never happen */ + dev_err(codec->dev, "%s: Invalid mclk_rate %d\n", + __func__, pdata->mclk_rate); + rc = -EINVAL; + goto done; + }; + + if (pdata->dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + pdata->dmic_sample_rate = def_dmic_rate; + } + if (pdata->mad_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for MAD + * if mad dmic sample rate is undefined + */ + pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate; + } + if (pdata->ecpp_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, + "%s: ecpp_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for ECPP DMIC + * if ecpp dmic sample rate is undefined + */ + pdata->ecpp_dmic_sample_rate = pdata->dmic_sample_rate; + } + + if (pdata->dmic_clk_drv == + WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) { + pdata->dmic_clk_drv = WCD9335_DMIC_CLK_DRIVE_DEFAULT; + dev_info(codec->dev, + "%s: dmic_clk_strength invalid, default = %d\n", + __func__, pdata->dmic_clk_drv); + } + + switch (pdata->dmic_clk_drv) { + case 2: + dmic_clk_drv = 0; + break; + case 4: + dmic_clk_drv = 1; + break; + case 8: + dmic_clk_drv = 2; + break; + case 16: + dmic_clk_drv = 3; + break; + default: + dev_err(codec->dev, + "%s: invalid dmic_clk_drv %d, using default\n", + __func__, pdata->dmic_clk_drv); + dmic_clk_drv = 0; + break; + } + + snd_soc_update_bits(codec, WCD9335_TEST_DEBUG_PAD_DRVCTL, + 0x0C, dmic_clk_drv << 2); + + /* + * Default the DMIC clk rates to mad_dmic_sample_rate, + * whereas, the anc/txfe dmic rates to dmic_sample_rate + * since the anc/txfe are independent of mad block. + */ + mad_dmic_ctl_val = tasha_get_dmic_clk_val(tasha->codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC0_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC1_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC2_CTL, + 0x0E, mad_dmic_ctl_val << 1); + + dmic_ctl_val = tasha_get_dmic_clk_val(tasha->codec, + pdata->mclk_rate, + pdata->dmic_sample_rate); + + if (dmic_ctl_val == WCD9335_DMIC_CLK_DIV_2) + anc_ctl_value = WCD9335_ANC_DMIC_X2_FULL_RATE; + else + anc_ctl_value = WCD9335_ANC_DMIC_X2_HALF_RATE; + + snd_soc_update_bits(codec, WCD9335_CDC_ANC0_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD9335_CDC_ANC0_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + snd_soc_update_bits(codec, WCD9335_CDC_ANC1_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD9335_CDC_ANC1_MODE_2_CTL, + 0x20, anc_ctl_value << 5); +done: + return rc; +} + +static struct wcd_cpe_core *tasha_codec_get_cpe_core( + struct snd_soc_codec *codec) +{ + struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec); + + return priv->cpe_core; +} + +static int tasha_codec_cpe_fll_update_divider( + struct snd_soc_codec *codec, u32 cpe_fll_rate) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + u32 div_val = 0, l_val = 0; + u32 computed_cpe_fll; + + if (cpe_fll_rate != CPE_FLL_CLK_75MHZ && + cpe_fll_rate != CPE_FLL_CLK_150MHZ) { + dev_err(codec->dev, + "%s: Invalid CPE fll rate request %u\n", + __func__, cpe_fll_rate); + return -EINVAL; + } + + if (wcd9xxx->mclk_rate == TASHA_MCLK_CLK_12P288MHZ) { + /* update divider to 10 and enable 5x divider */ + snd_soc_write(codec, WCD9335_CPE_FLL_USER_CTL_1, + 0x55); + div_val = 10; + } else if (wcd9xxx->mclk_rate == TASHA_MCLK_CLK_9P6MHZ) { + /* update divider to 8 and enable 2x divider */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0, + 0x7C, 0x70); + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_1, + 0xE0, 0x20); + div_val = 8; + } else { + dev_err(codec->dev, + "%s: Invalid MCLK rate %u\n", + __func__, wcd9xxx->mclk_rate); + return -EINVAL; + } + + l_val = ((cpe_fll_rate / 1000) * div_val) / + (wcd9xxx->mclk_rate / 1000); + + /* If l_val was integer truncated, increment l_val once */ + computed_cpe_fll = (wcd9xxx->mclk_rate / div_val) * l_val; + if (computed_cpe_fll < cpe_fll_rate) + l_val++; + + + /* update L value LSB and MSB */ + snd_soc_write(codec, WCD9335_CPE_FLL_L_VAL_CTL_0, + (l_val & 0xFF)); + snd_soc_write(codec, WCD9335_CPE_FLL_L_VAL_CTL_1, + ((l_val >> 8) & 0xFF)); + + tasha->current_cpe_clk_freq = cpe_fll_rate; + dev_dbg(codec->dev, + "%s: updated l_val to %u for cpe_clk %u and mclk %u\n", + __func__, l_val, cpe_fll_rate, wcd9xxx->mclk_rate); + + return 0; +} + +static int __tasha_cdc_change_cpe_clk(struct snd_soc_codec *codec, + u32 clk_freq) +{ + int ret = 0; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (!tasha_cdc_is_svs_enabled(tasha)) { + dev_dbg(codec->dev, + "%s: SVS not enabled or tasha is not 2p0, return\n", + __func__); + return 0; + } + dev_dbg(codec->dev, "%s: clk_freq = %u\n", __func__, clk_freq); + + if (clk_freq == CPE_FLL_CLK_75MHZ) { + /* Change to SVS */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x08, 0x08); + if (tasha_codec_cpe_fll_update_divider(codec, clk_freq)) { + ret = -EINVAL; + goto done; + } + + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x10, 0x10); + + clear_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, sido_buck_svs_voltage); + + } else if (clk_freq == CPE_FLL_CLK_150MHZ) { + /* change to nominal */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x08, 0x08); + + set_bit(CPE_NOMINAL, &tasha->status_mask); + tasha_codec_update_sido_voltage(tasha, SIDO_VOLTAGE_NOMINAL_MV); + + if (tasha_codec_cpe_fll_update_divider(codec, clk_freq)) { + ret = -EINVAL; + goto done; + } + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x10, 0x10); + } else { + dev_err(codec->dev, + "%s: Invalid clk_freq request %d for CPE FLL\n", + __func__, clk_freq); + ret = -EINVAL; + } + +done: + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x10, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x08, 0x00); + return ret; +} + + +static int tasha_codec_cpe_fll_enable(struct snd_soc_codec *codec, + bool enable) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u8 clk_sel_reg_val = 0x00; + + dev_dbg(codec->dev, "%s: enable = %s\n", + __func__, enable ? "true" : "false"); + + if (enable) { + if (tasha_cdc_is_svs_enabled(tasha)) { + /* FLL enable is always at SVS */ + if (__tasha_cdc_change_cpe_clk(codec, + CPE_FLL_CLK_75MHZ)) { + dev_err(codec->dev, + "%s: clk change to %d failed\n", + __func__, CPE_FLL_CLK_75MHZ); + return -EINVAL; + } + } else { + if (tasha_codec_cpe_fll_update_divider(codec, + CPE_FLL_CLK_75MHZ)) { + dev_err(codec->dev, + "%s: clk change to %d failed\n", + __func__, CPE_FLL_CLK_75MHZ); + return -EINVAL; + } + } + + if (TASHA_IS_1_0(wcd9xxx)) { + tasha_cdc_mclk_enable(codec, true, false); + clk_sel_reg_val = 0x02; + } + + /* Setup CPE reference clk */ + snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP, + 0x02, clk_sel_reg_val); + + /* enable CPE FLL reference clk */ + snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP, + 0x01, 0x01); + + /* program the PLL */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0, + 0x01, 0x01); + + /* TEST clk setting */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_TEST_CTL_0, + 0x80, 0x80); + /* set FLL mode to HW controlled */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x60, 0x00); + snd_soc_write(codec, WCD9335_CPE_FLL_FLL_MODE, 0x80); + } else { + /* disable CPE FLL reference clk */ + snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP, + 0x01, 0x00); + /* undo TEST clk setting */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_TEST_CTL_0, + 0x80, 0x00); + /* undo FLL mode to HW control */ + snd_soc_write(codec, WCD9335_CPE_FLL_FLL_MODE, 0x00); + snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE, + 0x60, 0x20); + /* undo the PLL */ + snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0, + 0x01, 0x00); + + if (TASHA_IS_1_0(wcd9xxx)) + tasha_cdc_mclk_enable(codec, false, false); + + /* + * FLL could get disabled while at nominal, + * scale it back to SVS + */ + if (tasha_cdc_is_svs_enabled(tasha)) + __tasha_cdc_change_cpe_clk(codec, + CPE_FLL_CLK_75MHZ); + } + + return 0; + +} + +static void tasha_cdc_query_cpe_clk_plan(void *data, + struct cpe_svc_cfg_clk_plan *clk_freq) +{ + struct snd_soc_codec *codec = data; + struct tasha_priv *tasha; + u32 cpe_clk_khz; + + if (!codec) { + pr_err("%s: Invalid codec handle\n", + __func__); + return; + } + + tasha = snd_soc_codec_get_drvdata(codec); + cpe_clk_khz = tasha->current_cpe_clk_freq / 1000; + + dev_dbg(codec->dev, + "%s: current_clk_freq = %u\n", + __func__, tasha->current_cpe_clk_freq); + + clk_freq->current_clk_feq = cpe_clk_khz; + clk_freq->num_clk_freqs = 2; + + if (tasha_cdc_is_svs_enabled(tasha)) { + clk_freq->clk_freqs[0] = CPE_FLL_CLK_75MHZ / 1000; + clk_freq->clk_freqs[1] = CPE_FLL_CLK_150MHZ / 1000; + } else { + clk_freq->clk_freqs[0] = CPE_FLL_CLK_75MHZ; + clk_freq->clk_freqs[1] = CPE_FLL_CLK_150MHZ; + } +} + +static void tasha_cdc_change_cpe_clk(void *data, + u32 clk_freq) +{ + struct snd_soc_codec *codec = data; + struct tasha_priv *tasha; + u32 cpe_clk_khz, req_freq = 0; + + if (!codec) { + pr_err("%s: Invalid codec handle\n", + __func__); + return; + } + + tasha = snd_soc_codec_get_drvdata(codec); + cpe_clk_khz = tasha->current_cpe_clk_freq / 1000; + + if (tasha_cdc_is_svs_enabled(tasha)) { + if ((clk_freq * 1000) <= CPE_FLL_CLK_75MHZ) + req_freq = CPE_FLL_CLK_75MHZ; + else + req_freq = CPE_FLL_CLK_150MHZ; + } + + dev_dbg(codec->dev, + "%s: requested clk_freq = %u, current clk_freq = %u\n", + __func__, clk_freq * 1000, + tasha->current_cpe_clk_freq); + + if (tasha_cdc_is_svs_enabled(tasha)) { + if (__tasha_cdc_change_cpe_clk(codec, req_freq)) + dev_err(codec->dev, + "%s: clock/voltage scaling failed\n", + __func__); + } +} + +static int tasha_codec_slim_reserve_bw(struct snd_soc_codec *codec, + u32 bw_ops, bool commit) +{ + struct wcd9xxx *wcd9xxx; + + if (!codec) { + pr_err("%s: Invalid handle to codec\n", + __func__); + return -EINVAL; + } + + wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!wcd9xxx) { + dev_err(codec->dev, "%s: Invalid parent drv_data\n", + __func__); + return -EINVAL; + } + + return wcd9xxx_slim_reserve_bw(wcd9xxx, bw_ops, commit); +} + +static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec, + bool vote) +{ + u32 bw_ops; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + return 0; + + if (vote) + bw_ops = SLIM_BW_CLK_GEAR_9; + else + bw_ops = SLIM_BW_UNVOTE; + + return tasha_codec_slim_reserve_bw(codec, + bw_ops, true); +} + +static int tasha_cpe_err_irq_control(struct snd_soc_codec *codec, + enum cpe_err_irq_cntl_type cntl_type, u8 *status) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + u8 irq_bits; + + if (TASHA_IS_2_0(tasha->wcd9xxx)) + irq_bits = 0xFF; + else + irq_bits = 0x3F; + + if (status) + irq_bits = (*status) & irq_bits; + + switch (cntl_type) { + case CPE_ERR_IRQ_MASK: + snd_soc_update_bits(codec, + WCD9335_CPE_SS_SS_ERROR_INT_MASK, + irq_bits, irq_bits); + break; + case CPE_ERR_IRQ_UNMASK: + snd_soc_update_bits(codec, + WCD9335_CPE_SS_SS_ERROR_INT_MASK, + irq_bits, 0x00); + break; + case CPE_ERR_IRQ_CLEAR: + snd_soc_write(codec, WCD9335_CPE_SS_SS_ERROR_INT_CLEAR, + irq_bits); + break; + case CPE_ERR_IRQ_STATUS: + if (!status) + return -EINVAL; + *status = snd_soc_read(codec, + WCD9335_CPE_SS_SS_ERROR_INT_STATUS); + break; + } + + return 0; +} + +static const struct wcd_cpe_cdc_cb cpe_cb = { + .cdc_clk_en = tasha_codec_internal_rco_ctrl, + .cpe_clk_en = tasha_codec_cpe_fll_enable, + .get_afe_out_port_id = tasha_codec_get_mad_port_id, + .lab_cdc_ch_ctl = tasha_codec_enable_slimtx_mad, + .cdc_ext_clk = tasha_cdc_mclk_enable, + .bus_vote_bw = tasha_codec_vote_max_bw, + .cpe_err_irq_control = tasha_cpe_err_irq_control, +}; + +static struct cpe_svc_init_param cpe_svc_params = { + .version = CPE_SVC_INIT_PARAM_V1, + .query_freq_plans_cb = tasha_cdc_query_cpe_clk_plan, + .change_freq_plan_cb = tasha_cdc_change_cpe_clk, +}; + +static int tasha_cpe_initialize(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd_cpe_params cpe_params; + + memset(&cpe_params, 0, + sizeof(struct wcd_cpe_params)); + cpe_params.codec = codec; + cpe_params.get_cpe_core = tasha_codec_get_cpe_core; + cpe_params.cdc_cb = &cpe_cb; + cpe_params.dbg_mode = cpe_debug_mode; + cpe_params.cdc_major_ver = CPE_SVC_CODEC_WCD9335; + cpe_params.cdc_minor_ver = CPE_SVC_CODEC_V1P0; + cpe_params.cdc_id = CPE_SVC_CODEC_WCD9335; + + cpe_params.cdc_irq_info.cpe_engine_irq = + WCD9335_IRQ_SVA_OUTBOX1; + cpe_params.cdc_irq_info.cpe_err_irq = + WCD9335_IRQ_SVA_ERROR; + cpe_params.cdc_irq_info.cpe_fatal_irqs = + TASHA_CPE_FATAL_IRQS; + + cpe_svc_params.context = codec; + cpe_params.cpe_svc_params = &cpe_svc_params; + + tasha->cpe_core = wcd_cpe_init("cpe_9335", codec, + &cpe_params); + if (IS_ERR_OR_NULL(tasha->cpe_core)) { + dev_err(codec->dev, + "%s: Failed to enable CPE\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static const struct wcd_resmgr_cb tasha_resmgr_cb = { + .cdc_rco_ctrl = __tasha_codec_internal_rco_ctrl, +}; + +static int tasha_device_down(struct wcd9xxx *wcd9xxx) +{ + struct snd_soc_codec *codec; + struct tasha_priv *priv; + int count; + int i = 0; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + priv = snd_soc_codec_get_drvdata(codec); + wcd_cpe_ssr_event(priv->cpe_core, WCD_CPE_BUS_DOWN_EVENT); + for (i = 0; i < priv->nr; i++) + swrm_wcd_notify(priv->swr_ctrl_data[i].swr_pdev, + SWR_DEVICE_DOWN, NULL); + snd_soc_card_change_online_state(codec->component.card, 0); + for (count = 0; count < NUM_CODEC_DAIS; count++) + priv->dai[count].bus_down_in_recovery = true; + + priv->resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + + return 0; +} + +static int tasha_post_reset_cb(struct wcd9xxx *wcd9xxx) +{ + int i, ret = 0; + struct wcd9xxx *control; + struct snd_soc_codec *codec; + struct tasha_priv *tasha; + struct wcd9xxx_pdata *pdata; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + tasha = snd_soc_codec_get_drvdata(codec); + control = dev_get_drvdata(codec->dev->parent); + + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + + mutex_lock(&tasha->codec_mutex); + + tasha_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tasha_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tasha_init_slim_slave_cfg(codec); + if (tasha->machine_codec_event_cb) + tasha->machine_codec_event_cb(codec, + WCD9335_CODEC_EVENT_CODEC_UP); + snd_soc_card_change_online_state(codec->component.card, 1); + + /* Class-H Init*/ + wcd_clsh_init(&tasha->clsh_d); + + for (i = 0; i < TASHA_MAX_MICBIAS; i++) + tasha->micb_ref[i] = 0; + + tasha_update_reg_defaults(tasha); + + tasha->codec = codec; + + dev_dbg(codec->dev, "%s: MCLK Rate = %x\n", + __func__, control->mclk_rate); + + if (control->mclk_rate == TASHA_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (control->mclk_rate == TASHA_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + tasha_codec_init_reg(codec); + + wcd_resmgr_post_ssr_v2(tasha->resmgr); + + tasha_enable_efuse_sensing(codec); + + regcache_mark_dirty(codec->component.regmap); + regcache_sync(codec->component.regmap); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tasha_handle_pdata(tasha, pdata); + if (ret < 0) + dev_err(codec->dev, "%s: invalid pdata\n", __func__); + + /* MBHC Init */ + wcd_mbhc_deinit(&tasha->mbhc); + tasha->mbhc_started = false; + + /* Initialize MBHC module */ + ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, TASHA_ZDET_SUPPORTED); + if (ret) + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + else + tasha_mbhc_hs_detect(codec, tasha->mbhc.mbhc_cfg); + + tasha_cleanup_irqs(tasha); + ret = tasha_setup_irqs(tasha); + if (ret) { + dev_err(codec->dev, "%s: tasha irq setup failed %d\n", + __func__, ret); + goto err; + } + + tasha_set_spkr_mode(codec, tasha->spkr_mode); + wcd_cpe_ssr_event(tasha->cpe_core, WCD_CPE_BUS_UP_EVENT); + +err: + mutex_unlock(&tasha->codec_mutex); + return ret; +} + +static struct regulator *tasha_codec_find_ondemand_regulator( + struct snd_soc_codec *codec, const char *name) +{ + int i; + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = tasha->wcd9xxx; + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + + for (i = 0; i < wcd9xxx->num_of_supplies; ++i) { + if (pdata->regulator[i].ondemand && + wcd9xxx->supplies[i].supply && + !strcmp(wcd9xxx->supplies[i].supply, name)) + return wcd9xxx->supplies[i].consumer; + } + + dev_dbg(tasha->dev, "Warning: regulator not found:%s\n", + name); + return NULL; +} + +static int tasha_codec_probe(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tasha_priv *tasha; + struct wcd9xxx_pdata *pdata; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int i, ret; + void *ptr = NULL; + struct regulator *supply; + + control = dev_get_drvdata(codec->dev->parent); + + dev_info(codec->dev, "%s()\n", __func__); + tasha = snd_soc_codec_get_drvdata(codec); + tasha->intf_type = wcd9xxx_get_intf_type(); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + control->dev_down = tasha_device_down; + control->post_reset = tasha_post_reset_cb; + control->ssr_priv = (void *)codec; + } + + /* Resource Manager post Init */ + ret = wcd_resmgr_post_init(tasha->resmgr, &tasha_resmgr_cb, codec); + if (ret) { + dev_err(codec->dev, "%s: wcd resmgr post init failed\n", + __func__); + goto err; + } + /* Class-H Init*/ + wcd_clsh_init(&tasha->clsh_d); + /* Default HPH Mode to Class-H HiFi */ + tasha->hph_mode = CLS_H_HIFI; + + tasha->codec = codec; + for (i = 0; i < COMPANDER_MAX; i++) + tasha->comp_enabled[i] = 0; + + tasha->spkr_gain_offset = RX_GAIN_OFFSET_0_DB; + tasha->intf_type = wcd9xxx_get_intf_type(); + tasha_update_reg_reset_values(codec); + pr_debug("%s: MCLK Rate = %x\n", __func__, control->mclk_rate); + if (control->mclk_rate == TASHA_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (control->mclk_rate == TASHA_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + tasha_codec_init_reg(codec); + + tasha_enable_efuse_sensing(codec); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tasha_handle_pdata(tasha, pdata); + if (ret < 0) { + pr_err("%s: bad pdata\n", __func__); + goto err; + } + + supply = tasha_codec_find_ondemand_regulator(codec, + on_demand_supply_name[ON_DEMAND_MICBIAS]); + if (supply) { + tasha->on_demand_list[ON_DEMAND_MICBIAS].supply = supply; + tasha->on_demand_list[ON_DEMAND_MICBIAS].ondemand_supply_count = + 0; + } + + tasha->fw_data = devm_kzalloc(codec->dev, + sizeof(*(tasha->fw_data)), GFP_KERNEL); + if (!tasha->fw_data) + goto err; + set_bit(WCD9XXX_ANC_CAL, tasha->fw_data->cal_bit); + set_bit(WCD9XXX_MBHC_CAL, tasha->fw_data->cal_bit); + set_bit(WCD9XXX_MAD_CAL, tasha->fw_data->cal_bit); + set_bit(WCD9XXX_VBAT_CAL, tasha->fw_data->cal_bit); + + ret = wcd_cal_create_hwdep(tasha->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + goto err_hwdep; + } + + /* Initialize MBHC module */ + if (TASHA_IS_2_0(tasha->wcd9xxx)) { + wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].reg = + WCD9335_MBHC_FSM_STATUS; + wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].mask = 0x01; + } + ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, TASHA_ZDET_SUPPORTED); + if (ret) { + pr_err("%s: mbhc initialization failed\n", __func__); + goto err_hwdep; + } + + ptr = devm_kzalloc(codec->dev, (sizeof(tasha_rx_chs) + + sizeof(tasha_tx_chs)), GFP_KERNEL); + if (!ptr) { + ret = -ENOMEM; + goto err_hwdep; + } + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + snd_soc_dapm_new_controls(dapm, tasha_dapm_i2s_widgets, + ARRAY_SIZE(tasha_dapm_i2s_widgets)); + snd_soc_dapm_add_routes(dapm, audio_i2s_map, + ARRAY_SIZE(audio_i2s_map)); + for (i = 0; i < ARRAY_SIZE(tasha_i2s_dai); i++) { + INIT_LIST_HEAD(&tasha->dai[i].wcd9xxx_ch_list); + init_waitqueue_head(&tasha->dai[i].dai_wait); + } + } else if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + for (i = 0; i < NUM_CODEC_DAIS; i++) { + INIT_LIST_HEAD(&tasha->dai[i].wcd9xxx_ch_list); + init_waitqueue_head(&tasha->dai[i].dai_wait); + } + tasha_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tasha_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tasha_slimbus_slave_port_cfg.slave_port_mapping[0] = + TASHA_TX13; + tasha_init_slim_slave_cfg(codec); + } + + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + snd_soc_add_codec_controls(codec, + tasha_analog_gain_controls, + ARRAY_SIZE(tasha_analog_gain_controls)); + control->num_rx_port = TASHA_RX_MAX; + control->rx_chs = ptr; + memcpy(control->rx_chs, tasha_rx_chs, sizeof(tasha_rx_chs)); + control->num_tx_port = TASHA_TX_MAX; + control->tx_chs = ptr + sizeof(tasha_rx_chs); + memcpy(control->tx_chs, tasha_tx_chs, sizeof(tasha_tx_chs)); + + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); + + if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF Mix Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + snd_soc_dapm_ignore_suspend(dapm, "AIF5 CPE TX"); + } + + snd_soc_dapm_sync(dapm); + + ret = tasha_setup_irqs(tasha); + if (ret) { + pr_err("%s: tasha irq setup failed %d\n", __func__, ret); + goto err_pdata; + } + + ret = tasha_cpe_initialize(codec); + if (ret) { + dev_err(codec->dev, + "%s: cpe initialization failed, err = %d\n", + __func__, ret); + /* Do not fail probe if CPE failed */ + ret = 0; + } + + for (i = 0; i < TASHA_NUM_DECIMATORS; i++) { + tasha->tx_hpf_work[i].tasha = tasha; + tasha->tx_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&tasha->tx_hpf_work[i].dwork, + tasha_tx_hpf_corner_freq_callback); + } + + for (i = 0; i < TASHA_NUM_DECIMATORS; i++) { + tasha->tx_mute_dwork[i].tasha = tasha; + tasha->tx_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&tasha->tx_mute_dwork[i].dwork, + tasha_tx_mute_update_callback); + } + + tasha->spk_anc_dwork.tasha = tasha; + INIT_DELAYED_WORK(&tasha->spk_anc_dwork.dwork, + tasha_spk_anc_update_callback); + + mutex_lock(&tasha->codec_mutex); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); + mutex_unlock(&tasha->codec_mutex); + snd_soc_dapm_sync(dapm); + + return ret; + +err_pdata: + devm_kfree(codec->dev, ptr); + control->rx_chs = NULL; + control->tx_chs = NULL; +err_hwdep: + devm_kfree(codec->dev, tasha->fw_data); + tasha->fw_data = NULL; +err: + return ret; +} + +static int tasha_codec_remove(struct snd_soc_codec *codec) +{ + struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *control; + + control = dev_get_drvdata(codec->dev->parent); + control->rx_chs = NULL; + control->tx_chs = NULL; + + tasha_cleanup_irqs(tasha); + /* Cleanup MBHC */ + /* Cleanup resmgr */ + + return 0; +} + +static struct regmap *tasha_get_regmap(struct device *dev) +{ + struct wcd9xxx *control = dev_get_drvdata(dev->parent); + + return control->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_tasha = { + .probe = tasha_codec_probe, + .remove = tasha_codec_remove, + .get_regmap = tasha_get_regmap, + .component_driver = { + .controls = tasha_snd_controls, + .num_controls = ARRAY_SIZE(tasha_snd_controls), + .dapm_widgets = tasha_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tasha_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + }, +}; + +#ifdef CONFIG_PM +static int tasha_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tasha_priv *tasha = platform_get_drvdata(pdev); + + dev_dbg(dev, "%s: system suspend\n", __func__); + if (cancel_delayed_work_sync(&tasha->power_gate_work)) + tasha_codec_power_gate_digital_core(tasha); + + return 0; +} + +static int tasha_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tasha_priv *tasha = platform_get_drvdata(pdev); + + if (!tasha) { + dev_err(dev, "%s: tasha private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system resume\n", __func__); + return 0; +} + +static const struct dev_pm_ops tasha_pm_ops = { + .suspend = tasha_suspend, + .resume = tasha_resume, +}; +#endif + +static int tasha_swrm_read(void *handle, int reg) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + unsigned short swr_rd_addr_base; + unsigned short swr_rd_data_base; + int val, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tasha = (struct tasha_priv *)handle; + wcd9xxx = tasha->wcd9xxx; + + dev_dbg(tasha->dev, "%s: Reading soundwire register, 0x%x\n", + __func__, reg); + swr_rd_addr_base = WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0; + swr_rd_data_base = WCD9335_SWR_AHB_BRIDGE_RD_DATA_0; + /* read_lock */ + mutex_lock(&tasha->swr_read_lock); + ret = regmap_bulk_write(wcd9xxx->regmap, swr_rd_addr_base, + (u8 *)®, 4); + if (ret < 0) { + pr_err("%s: RD Addr Failure\n", __func__); + goto err; + } + /* Check for RD status */ + ret = regmap_bulk_read(wcd9xxx->regmap, swr_rd_data_base, + (u8 *)&val, 4); + if (ret < 0) { + pr_err("%s: RD Data Failure\n", __func__); + goto err; + } + ret = val; +err: + /* read_unlock */ + mutex_unlock(&tasha->swr_read_lock); + return ret; +} + +static int tasha_swrm_i2s_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + size_t len) +{ + int i, ret = 0; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + + swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0; + + for (i = 0; i < (len * 2); i += 2) { + /* First Write the Data to register */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_data_base, bulk_reg[i].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Data Failure\n", + __func__); + break; + } + /* Next Write Address */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_addr_base, bulk_reg[i+1].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Addr Failure\n", + __func__); + break; + } + } + return ret; +} + +static int tasha_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_reg_val *bulk_reg; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + int i, j, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + if (len <= 0) { + pr_err("%s: Invalid size: %zu\n", __func__, len); + return -EINVAL; + } + tasha = (struct tasha_priv *)handle; + wcd9xxx = tasha->wcd9xxx; + + swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0; + + bulk_reg = kzalloc((2 * len * sizeof(struct wcd9xxx_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + for (i = 0, j = 0; i < (len * 2); i += 2, j++) { + bulk_reg[i].reg = swr_wr_data_base; + bulk_reg[i].buf = (u8 *)(&val[j]); + bulk_reg[i].bytes = 4; + bulk_reg[i+1].reg = swr_wr_addr_base; + bulk_reg[i+1].buf = (u8 *)(®[j]); + bulk_reg[i+1].bytes = 4; + } + mutex_lock(&tasha->swr_write_lock); + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tasha_swrm_i2s_bulk_write(wcd9xxx, bulk_reg, len); + if (ret) { + dev_err(tasha->dev, "%s: i2s bulk write failed, ret: %d\n", + __func__, ret); + } + } else { + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + (len * 2), false); + if (ret) { + dev_err(tasha->dev, "%s: swrm bulk write failed, ret: %d\n", + __func__, ret); + } + } + + mutex_unlock(&tasha->swr_write_lock); + kfree(bulk_reg); + + return ret; +} + +static int tasha_swrm_write(void *handle, int reg, int val) +{ + struct tasha_priv *tasha; + struct wcd9xxx *wcd9xxx; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + struct wcd9xxx_reg_val bulk_reg[2]; + int ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tasha = (struct tasha_priv *)handle; + wcd9xxx = tasha->wcd9xxx; + + swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0; + + /* First Write the Data to register */ + bulk_reg[0].reg = swr_wr_data_base; + bulk_reg[0].buf = (u8 *)(&val); + bulk_reg[0].bytes = 4; + bulk_reg[1].reg = swr_wr_addr_base; + bulk_reg[1].buf = (u8 *)(®); + bulk_reg[1].bytes = 4; + + mutex_lock(&tasha->swr_write_lock); + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tasha_swrm_i2s_bulk_write(wcd9xxx, bulk_reg, 1); + if (ret) { + dev_err(tasha->dev, "%s: i2s swrm write failed, ret: %d\n", + __func__, ret); + } + } else { + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false); + if (ret < 0) + pr_err("%s: WR Data Failure\n", __func__); + } + + mutex_unlock(&tasha->swr_write_lock); + return ret; +} + +static int tasha_swrm_clock(void *handle, bool enable) +{ + struct tasha_priv *tasha = (struct tasha_priv *) handle; + + mutex_lock(&tasha->swr_clk_lock); + + dev_dbg(tasha->dev, "%s: swrm clock %s\n", + __func__, (enable?"enable" : "disable")); + if (enable) { + tasha->swr_clk_users++; + if (tasha->swr_clk_users == 1) { + if (TASHA_IS_2_0(tasha->wcd9xxx)) + regmap_update_bits( + tasha->wcd9xxx->regmap, + WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x00); + __tasha_cdc_mclk_enable(tasha, true); + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + } + } else { + tasha->swr_clk_users--; + if (tasha->swr_clk_users == 0) { + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + __tasha_cdc_mclk_enable(tasha, false); + if (TASHA_IS_2_0(tasha->wcd9xxx)) + regmap_update_bits( + tasha->wcd9xxx->regmap, + WCD9335_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x10); + } + } + dev_dbg(tasha->dev, "%s: swrm clock users %d\n", + __func__, tasha->swr_clk_users); + mutex_unlock(&tasha->swr_clk_lock); + return 0; +} + +static int tasha_swrm_handle_irq(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action) +{ + struct tasha_priv *tasha; + int ret = 0; + struct wcd9xxx *wcd9xxx; + + if (!handle) { + pr_err("%s: null handle received\n", __func__); + return -EINVAL; + } + tasha = (struct tasha_priv *) handle; + wcd9xxx = tasha->wcd9xxx; + + if (action) { + ret = wcd9xxx_request_irq(&wcd9xxx->core_res, + WCD9335_IRQ_SOUNDWIRE, + swrm_irq_handler, + "Tasha SWR Master", swrm_handle); + if (ret) + dev_err(tasha->dev, "%s: Failed to request irq %d\n", + __func__, WCD9335_IRQ_SOUNDWIRE); + } else + wcd9xxx_free_irq(&wcd9xxx->core_res, WCD9335_IRQ_SOUNDWIRE, + swrm_handle); + + return ret; +} + +static void tasha_add_child_devices(struct work_struct *work) +{ + struct tasha_priv *tasha; + struct platform_device *pdev; + struct device_node *node; + struct wcd9xxx *wcd9xxx; + struct tasha_swr_ctrl_data *swr_ctrl_data = NULL, *temp; + int ret, ctrl_num = 0; + struct wcd_swr_ctrl_platform_data *platdata; + char plat_dev_name[WCD9335_STRING_LEN]; + + tasha = container_of(work, struct tasha_priv, + tasha_add_child_devices_work); + if (!tasha) { + pr_err("%s: Memory for WCD9335 does not exist\n", + __func__); + return; + } + wcd9xxx = tasha->wcd9xxx; + if (!wcd9xxx) { + pr_err("%s: Memory for WCD9XXX does not exist\n", + __func__); + return; + } + if (!wcd9xxx->dev->of_node) { + pr_err("%s: DT node for wcd9xxx does not exist\n", + __func__); + return; + } + + platdata = &tasha->swr_plat_data; + + for_each_child_of_node(wcd9xxx->dev->of_node, node) { + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "tasha_swr_ctrl", + (WCD9335_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WCD9335_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = tasha->dev; + pdev->dev.of_node = node; + + if (!strcmp(node->name, "swr_master")) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (!strcmp(node->name, "swr_master")) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct tasha_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(wcd9xxx->dev, "out of memory\n"); + ret = -ENOMEM; + goto err; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + tasha->nr = ctrl_num; + tasha->swr_ctrl_data = swr_ctrl_data; + } + } + + return; +fail_pdev_add: + platform_device_put(pdev); +err: + return; +} + +/* + * tasha_codec_ver: to get tasha codec version + * @codec: handle to snd_soc_codec * + * return enum codec_variant - version + */ +enum codec_variant tasha_codec_ver(void) +{ + return codec_ver; +} +EXPORT_SYMBOL(tasha_codec_ver); + +static int __tasha_enable_efuse_sensing(struct tasha_priv *tasha) +{ + int val, rc; + + __tasha_cdc_mclk_enable(tasha, true); + + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x20); + regmap_update_bits(tasha->wcd9xxx->regmap, + WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x01, 0x01); + + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + rc = regmap_read(tasha->wcd9xxx->regmap, + WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS, &val); + + if (rc || (!(val & 0x01))) + WARN(1, "%s: Efuse sense is not complete\n", __func__); + + __tasha_cdc_mclk_enable(tasha, false); + + return rc; +} + +void tasha_get_codec_ver(struct tasha_priv *tasha) +{ + int i; + int val; + struct tasha_reg_mask_val codec_reg[] = { + {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10, 0xFF, 0xFF}, + {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11, 0xFF, 0x83}, + {WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12, 0xFF, 0x0A}, + }; + + __tasha_enable_efuse_sensing(tasha); + for (i = 0; i < ARRAY_SIZE(codec_reg); i++) { + regmap_read(tasha->wcd9xxx->regmap, codec_reg[i].reg, &val); + if (!(val && codec_reg[i].val)) { + codec_ver = WCD9335; + goto ret; + } + } + codec_ver = WCD9326; +ret: + pr_debug("%s: codec is %d\n", __func__, codec_ver); +} +EXPORT_SYMBOL(tasha_get_codec_ver); + +static int tasha_probe(struct platform_device *pdev) +{ + int ret = 0; + struct tasha_priv *tasha; + struct clk *wcd_ext_clk, *wcd_native_clk; + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9xxx_power_region *cdc_pwr; + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) { + if (apr_get_subsys_state() == APR_SUBSYS_DOWN) { + dev_err(&pdev->dev, "%s: dsp down\n", __func__); + return -EPROBE_DEFER; + } + } + + tasha = devm_kzalloc(&pdev->dev, sizeof(struct tasha_priv), + GFP_KERNEL); + if (!tasha) + return -ENOMEM; + platform_set_drvdata(pdev, tasha); + + tasha->wcd9xxx = dev_get_drvdata(pdev->dev.parent); + tasha->dev = &pdev->dev; + INIT_DELAYED_WORK(&tasha->power_gate_work, tasha_codec_power_gate_work); + mutex_init(&tasha->power_lock); + mutex_init(&tasha->sido_lock); + INIT_WORK(&tasha->tasha_add_child_devices_work, + tasha_add_child_devices); + BLOCKING_INIT_NOTIFIER_HEAD(&tasha->notifier); + mutex_init(&tasha->micb_lock); + mutex_init(&tasha->swr_read_lock); + mutex_init(&tasha->swr_write_lock); + mutex_init(&tasha->swr_clk_lock); + mutex_init(&tasha->mclk_lock); + + cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region), + GFP_KERNEL); + if (!cdc_pwr) { + ret = -ENOMEM; + goto err_cdc_pwr; + } + tasha->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr; + cdc_pwr->pwr_collapse_reg_min = TASHA_DIG_CORE_REG_MIN; + cdc_pwr->pwr_collapse_reg_max = TASHA_DIG_CORE_REG_MAX; + wcd9xxx_set_power_state(tasha->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + + mutex_init(&tasha->codec_mutex); + /* + * Init resource manager so that if child nodes such as SoundWire + * requests for clock, resource manager can honor the request + */ + resmgr = wcd_resmgr_init(&tasha->wcd9xxx->core_res, NULL); + if (IS_ERR(resmgr)) { + ret = PTR_ERR(resmgr); + dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n", + __func__); + goto err_resmgr; + } + tasha->resmgr = resmgr; + tasha->swr_plat_data.handle = (void *) tasha; + tasha->swr_plat_data.read = tasha_swrm_read; + tasha->swr_plat_data.write = tasha_swrm_write; + tasha->swr_plat_data.bulk_write = tasha_swrm_bulk_write; + tasha->swr_plat_data.clk = tasha_swrm_clock; + tasha->swr_plat_data.handle_irq = tasha_swrm_handle_irq; + + /* Register for Clock */ + wcd_ext_clk = clk_get(tasha->wcd9xxx->dev, "wcd_clk"); + if (IS_ERR(wcd_ext_clk)) { + dev_err(tasha->wcd9xxx->dev, "%s: clk get %s failed\n", + __func__, "wcd_ext_clk"); + goto err_clk; + } + tasha->wcd_ext_clk = wcd_ext_clk; + tasha->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV; + set_bit(AUDIO_NOMINAL, &tasha->status_mask); + tasha->sido_ccl_cnt = 0; + + /* Register native clk for 44.1 playback */ + wcd_native_clk = clk_get(tasha->wcd9xxx->dev, "wcd_native_clk"); + if (IS_ERR(wcd_native_clk)) + dev_dbg(tasha->wcd9xxx->dev, "%s: clk get %s failed\n", + __func__, "wcd_native_clk"); + else + tasha->wcd_native_clk = wcd_native_clk; + + if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha, + tasha_dai, ARRAY_SIZE(tasha_dai)); + else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha, + tasha_i2s_dai, + ARRAY_SIZE(tasha_i2s_dai)); + else + ret = -EINVAL; + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed, ret = %d\n", + __func__, ret); + goto err_cdc_reg; + } + /* Update codec register default values */ + tasha_update_reg_defaults(tasha); + schedule_work(&tasha->tasha_add_child_devices_work); + tasha_get_codec_ver(tasha); + + dev_info(&pdev->dev, "%s: Tasha driver probe done\n", __func__); + return ret; + +err_cdc_reg: + clk_put(tasha->wcd_ext_clk); + if (tasha->wcd_native_clk) + clk_put(tasha->wcd_native_clk); +err_clk: + wcd_resmgr_remove(tasha->resmgr); +err_resmgr: + devm_kfree(&pdev->dev, cdc_pwr); +err_cdc_pwr: + mutex_destroy(&tasha->mclk_lock); + devm_kfree(&pdev->dev, tasha); + return ret; +} + +static int tasha_remove(struct platform_device *pdev) +{ + struct tasha_priv *tasha; + + tasha = platform_get_drvdata(pdev); + + mutex_destroy(&tasha->codec_mutex); + clk_put(tasha->wcd_ext_clk); + if (tasha->wcd_native_clk) + clk_put(tasha->wcd_native_clk); + mutex_destroy(&tasha->mclk_lock); + devm_kfree(&pdev->dev, tasha); + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static struct platform_driver tasha_codec_driver = { + .probe = tasha_probe, + .remove = tasha_remove, + .driver = { + .name = "tasha_codec", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &tasha_pm_ops, +#endif + }, +}; + +module_platform_driver(tasha_codec_driver); + +MODULE_DESCRIPTION("Tasha Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h new file mode 100644 index 000000000000..c76461edecf1 --- /dev/null +++ b/sound/soc/codecs/wcd9335.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 WCD9335_H +#define WCD9335_H + +#include +#include +#include +#include +#include "wcd-mbhc-v2.h" + +#define TASHA_REG_VAL(reg, val) {reg, 0, val} + +#define TASHA_REGISTER_START_OFFSET 0x800 +#define TASHA_SB_PGD_PORT_RX_BASE 0x40 +#define TASHA_SB_PGD_PORT_TX_BASE 0x50 + +#define TASHA_ZDET_SUPPORTED true +/* z value defined in milliohm */ +#define TASHA_ZDET_VAL_32 32000 +#define TASHA_ZDET_VAL_400 400000 +#define TASHA_ZDET_VAL_1200 1200000 +#define TASHA_ZDET_VAL_100K 100000000 +/* z floating defined in ohms */ +#define TASHA_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE + +#define WCD9335_DMIC_CLK_DIV_2 0x0 +#define WCD9335_DMIC_CLK_DIV_3 0x1 +#define WCD9335_DMIC_CLK_DIV_4 0x2 +#define WCD9335_DMIC_CLK_DIV_6 0x3 +#define WCD9335_DMIC_CLK_DIV_8 0x4 +#define WCD9335_DMIC_CLK_DIV_16 0x5 +#define WCD9335_DMIC_CLK_DRIVE_DEFAULT 0x02 + +#define WCD9335_ANC_DMIC_X2_FULL_RATE 1 +#define WCD9335_ANC_DMIC_X2_HALF_RATE 0 + +/* Number of input and output Slimbus port */ +enum { + TASHA_RX0 = 0, + TASHA_RX1, + TASHA_RX2, + TASHA_RX3, + TASHA_RX4, + TASHA_RX5, + TASHA_RX6, + TASHA_RX7, + TASHA_RX8, + TASHA_RX9, + TASHA_RX10, + TASHA_RX11, + TASHA_RX12, + TASHA_RX_MAX, +}; + +enum { + TASHA_TX0 = 0, + TASHA_TX1, + TASHA_TX2, + TASHA_TX3, + TASHA_TX4, + TASHA_TX5, + TASHA_TX6, + TASHA_TX7, + TASHA_TX8, + TASHA_TX9, + TASHA_TX10, + TASHA_TX11, + TASHA_TX12, + TASHA_TX13, + TASHA_TX14, + TASHA_TX15, + TASHA_TX_MAX, +}; + +enum wcd9335_codec_event { + WCD9335_CODEC_EVENT_CODEC_UP = 0, +}; + +enum tasha_on_demand_supply { + ON_DEMAND_MICBIAS = 0, + ON_DEMAND_SUPPLIES_MAX, +}; + +/* structure used to put the defined + * ondemand supply for codec + * and count being used. + */ +struct on_demand_supply { + struct regulator *supply; + int ondemand_supply_count; +}; + +/* Dai data structure holds the + * dai specific info like rate, + * channel number etc. + */ +struct tasha_codec_dai_data { + u32 rate; + u32 *ch_num; + u32 ch_act; + u32 ch_tot; +}; + +/* Structure used to update codec + * register defaults after reset + */ +struct tasha_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +/* Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + SPKR_MODE_DEFAULT, + SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +/* + * Rx path gain offsets + */ +enum { + RX_GAIN_OFFSET_M1P5_DB, + RX_GAIN_OFFSET_0_DB, +}; + +extern void *tasha_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type); +extern int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, + bool dapm); +extern int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, + bool dapm); +extern int tasha_enable_efuse_sensing(struct snd_soc_codec *codec); +extern int tasha_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg); +extern void tasha_mbhc_hs_detect_exit(struct snd_soc_codec *codec); +extern void tasha_mbhc_zdet_gpio_ctrl( + int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high), + struct snd_soc_codec *codec); +extern int tasha_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +extern void tasha_event_register( + int (*machine_event_cb)(struct snd_soc_codec *codec, + enum wcd9335_codec_event), + struct snd_soc_codec *codec); +extern int tasha_codec_enable_standalone_micbias(struct snd_soc_codec *codec, + int micb_num, + bool enable); +extern int tasha_set_spkr_mode(struct snd_soc_codec *codec, int mode); +extern int tasha_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset); +extern enum codec_variant tasha_codec_ver(void); +#endif diff --git a/sound/soc/codecs/wcd934x/Makefile b/sound/soc/codecs/wcd934x/Makefile new file mode 100644 index 000000000000..12781f6d4556 --- /dev/null +++ b/sound/soc/codecs/wcd934x/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for wcd934x codec driver. +# +snd-soc-wcd934x-objs := wcd934x.o wcd934x-dsp-cntl.o \ + wcd934x-mbhc.o wcd934x-dsd.o +obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/sound/soc/codecs/wcd934x/wcd934x-dsd.c new file mode 100644 index 000000000000..3e23e3749bda --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.c @@ -0,0 +1,772 @@ +/* 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 "wcd934x-dsd.h" + +#define DSD_VOLUME_MAX_0dB 0 +#define DSD_VOLUME_MIN_M110dB -110 + +#define DSD_VOLUME_RANGE_CHECK(x) ((x >= DSD_VOLUME_MIN_M110dB) &&\ + (x <= DSD_VOLUME_MAX_0dB)) +#define DSD_VOLUME_STEPS 3 +#define DSD_VOLUME_UPDATE_DELAY_MS 30 +#define DSD_VOLUME_USLEEP_MARGIN_US 100 +#define DSD_VOLUME_STEP_DELAY_US ((1000 * DSD_VOLUME_UPDATE_DELAY_MS) / \ + (2 * DSD_VOLUME_STEPS)) + +#define TAVIL_VERSION_1_0 0 +#define TAVIL_VERSION_1_1 1 + +static const DECLARE_TLV_DB_MINMAX(tavil_dsd_db_scale, DSD_VOLUME_MIN_M110dB, + DSD_VOLUME_MAX_0dB); + +static const char *const dsd_if_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7", + "DSD_DATA_PAD" +}; + +static const char * const dsd_filt0_mux_text[] = { + "ZERO", "DSD_L IF MUX", +}; + +static const char * const dsd_filt1_mux_text[] = { + "ZERO", "DSD_R IF MUX", +}; + +static const struct soc_enum dsd_filt0_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_DSD0_PATH_CTL, 0, + ARRAY_SIZE(dsd_filt0_mux_text), dsd_filt0_mux_text); + +static const struct soc_enum dsd_filt1_mux_enum = + SOC_ENUM_SINGLE(WCD934X_CDC_DSD1_PATH_CTL, 0, + ARRAY_SIZE(dsd_filt1_mux_text), dsd_filt1_mux_text); + +static SOC_ENUM_SINGLE_DECL(dsd_l_if_enum, WCD934X_CDC_DSD0_CFG0, + 2, dsd_if_text); +static SOC_ENUM_SINGLE_DECL(dsd_r_if_enum, WCD934X_CDC_DSD1_CFG0, + 2, dsd_if_text); + +static const struct snd_kcontrol_new dsd_filt0_mux = + SOC_DAPM_ENUM("DSD Filt0 Mux", dsd_filt0_mux_enum); + +static const struct snd_kcontrol_new dsd_filt1_mux = + SOC_DAPM_ENUM("DSD Filt1 Mux", dsd_filt1_mux_enum); + +static const struct snd_kcontrol_new dsd_l_if_mux = + SOC_DAPM_ENUM("DSD Left If Mux", dsd_l_if_enum); +static const struct snd_kcontrol_new dsd_r_if_mux = + SOC_DAPM_ENUM("DSD Right If Mux", dsd_r_if_enum); + +static const struct snd_soc_dapm_route tavil_dsd_audio_map[] = { + {"DSD_L IF MUX", "RX0", "CDC_IF RX0 MUX"}, + {"DSD_L IF MUX", "RX1", "CDC_IF RX1 MUX"}, + {"DSD_L IF MUX", "RX2", "CDC_IF RX2 MUX"}, + {"DSD_L IF MUX", "RX3", "CDC_IF RX3 MUX"}, + {"DSD_L IF MUX", "RX4", "CDC_IF RX4 MUX"}, + {"DSD_L IF MUX", "RX5", "CDC_IF RX5 MUX"}, + {"DSD_L IF MUX", "RX6", "CDC_IF RX6 MUX"}, + {"DSD_L IF MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"DSD_FILTER_0", NULL, "DSD_L IF MUX"}, + {"DSD_FILTER_0", NULL, "RX INT1 NATIVE SUPPLY"}, + {"RX INT1 MIX3", "DSD HPHL Switch", "DSD_FILTER_0"}, + + {"DSD_R IF MUX", "RX0", "CDC_IF RX0 MUX"}, + {"DSD_R IF MUX", "RX1", "CDC_IF RX1 MUX"}, + {"DSD_R IF MUX", "RX2", "CDC_IF RX2 MUX"}, + {"DSD_R IF MUX", "RX3", "CDC_IF RX3 MUX"}, + {"DSD_R IF MUX", "RX4", "CDC_IF RX4 MUX"}, + {"DSD_R IF MUX", "RX5", "CDC_IF RX5 MUX"}, + {"DSD_R IF MUX", "RX6", "CDC_IF RX6 MUX"}, + {"DSD_R IF MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"DSD_FILTER_1", NULL, "DSD_R IF MUX"}, + {"DSD_FILTER_1", NULL, "RX INT2 NATIVE SUPPLY"}, + {"RX INT2 MIX3", "DSD HPHR Switch", "DSD_FILTER_1"}, + + {"DSD_FILTER_0", NULL, "RX INT3 NATIVE SUPPLY"}, + {"RX INT3 MIX3", "DSD LO1 Switch", "DSD_FILTER_0"}, + {"DSD_FILTER_1", NULL, "RX INT4 NATIVE SUPPLY"}, + {"RX INT4 MIX3", "DSD LO2 Switch", "DSD_FILTER_1"}, +}; + +static bool is_valid_dsd_interpolator(int interp_num) +{ + if ((interp_num == INTERP_HPHL) || (interp_num == INTERP_HPHR) || + (interp_num == INTERP_LO1) || (interp_num == INTERP_LO2)) + return true; + + return false; +} + +/** + * tavil_dsd_set_mixer_value - Set DSD HPH/LO mixer value + * + * @dsd_conf: pointer to dsd config + * @interp_num: Interpolator number (HPHL/R, LO1/2) + * @sw_value: Mixer switch value + * + * Returns 0 on success or -EINVAL on failure + */ +int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num, int sw_value) +{ + if (!dsd_conf) + return -EINVAL; + + if (!is_valid_dsd_interpolator(interp_num)) + return -EINVAL; + + dsd_conf->dsd_interp_mixer[interp_num] = !!sw_value; + + return 0; +} +EXPORT_SYMBOL(tavil_dsd_set_mixer_value); + +/** + * tavil_dsd_get_current_mixer_value - Get DSD HPH/LO mixer value + * + * @dsd_conf: pointer to dsd config + * @interp_num: Interpolator number (HPHL/R, LO1/2) + * + * Returns current mixer val for success or -EINVAL for failure + */ +int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + if (!dsd_conf) + return -EINVAL; + + if (!is_valid_dsd_interpolator(interp_num)) + return -EINVAL; + + return dsd_conf->dsd_interp_mixer[interp_num]; +} +EXPORT_SYMBOL(tavil_dsd_get_current_mixer_value); + +/** + * tavil_dsd_set_out_select - DSD0/1 out select to HPH or LO + * + * @dsd_conf: pointer to dsd config + * @interp_num: Interpolator number (HPHL/R, LO1/2) + * + * Returns 0 for success or -EINVAL for failure + */ +int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + unsigned int reg, val; + struct snd_soc_codec *codec; + + if (!dsd_conf || !dsd_conf->codec) + return -EINVAL; + + codec = dsd_conf->codec; + + if (!is_valid_dsd_interpolator(interp_num)) { + dev_err(codec->dev, "%s: Invalid Interpolator: %d for DSD\n", + __func__, interp_num); + return -EINVAL; + } + + switch (interp_num) { + case INTERP_HPHL: + reg = WCD934X_CDC_DSD0_CFG0; + val = 0x00; + break; + case INTERP_HPHR: + reg = WCD934X_CDC_DSD1_CFG0; + val = 0x00; + break; + case INTERP_LO1: + reg = WCD934X_CDC_DSD0_CFG0; + val = 0x02; + break; + case INTERP_LO2: + reg = WCD934X_CDC_DSD1_CFG0; + val = 0x02; + break; + default: + return -EINVAL; + } + + snd_soc_update_bits(codec, reg, 0x02, val); + + return 0; +} +EXPORT_SYMBOL(tavil_dsd_set_out_select); + +/** + * tavil_dsd_reset - Reset DSD block + * + * @dsd_conf: pointer to dsd config + * + */ +void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf) +{ + if (!dsd_conf || !dsd_conf->codec) + return; + + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x01, 0x00); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x01, 0x00); +} +EXPORT_SYMBOL(tavil_dsd_reset); + +/** + * tavil_dsd_set_interp_rate - Set interpolator rate for DSD + * + * @dsd_conf: pointer to dsd config + * @rx_port: RX port number + * @sample_rate: Sample rate of the RX interpolator + * @sample_rate_val: Interpolator rate value + */ +void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port, + u32 sample_rate, u8 sample_rate_val) +{ + u8 dsd_inp_sel; + u8 dsd0_inp, dsd1_inp; + u8 val0, val1; + u8 dsd0_out_sel, dsd1_out_sel; + u16 int_fs_reg, interp_num = 0; + struct snd_soc_codec *codec; + + if (!dsd_conf || !dsd_conf->codec) + return; + + codec = dsd_conf->codec; + + dsd_inp_sel = DSD_INP_SEL_RX0 + rx_port - WCD934X_RX_PORT_START_NUMBER; + + val0 = snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0); + val1 = snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0); + dsd0_inp = (val0 & 0x3C) >> 2; + dsd1_inp = (val1 & 0x3C) >> 2; + dsd0_out_sel = (val0 & 0x02) >> 1; + dsd1_out_sel = (val1 & 0x02) >> 1; + + /* Set HPHL or LO1 interp rate based on out select */ + if (dsd_inp_sel == dsd0_inp) { + interp_num = dsd0_out_sel ? INTERP_LO1 : INTERP_HPHL; + dsd_conf->base_sample_rate[DSD0] = sample_rate; + } + + /* Set HPHR or LO2 interp rate based on out select */ + if (dsd_inp_sel == dsd1_inp) { + interp_num = dsd1_out_sel ? INTERP_LO2 : INTERP_HPHR; + dsd_conf->base_sample_rate[DSD1] = sample_rate; + } + + if (interp_num) { + int_fs_reg = WCD934X_CDC_RX0_RX_PATH_CTL + 20 * interp_num; + if ((snd_soc_read(codec, int_fs_reg) & 0x0f) < 0x09) { + dev_dbg(codec->dev, "%s: Set Interp %d to sample_rate val 0x%x\n", + __func__, interp_num, sample_rate_val); + snd_soc_update_bits(codec, int_fs_reg, 0x0F, + sample_rate_val); + } + } +} +EXPORT_SYMBOL(tavil_dsd_set_interp_rate); + +static int tavil_set_dsd_mode(struct snd_soc_codec *codec, int dsd_num, + u8 *pcm_rate_val) +{ + unsigned int dsd_out_sel_reg; + u8 dsd_mode; + u32 sample_rate; + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + + if (!dsd_conf) + return -EINVAL; + + if ((dsd_num < 0) || (dsd_num > 1)) + return -EINVAL; + + sample_rate = dsd_conf->base_sample_rate[dsd_num]; + dsd_out_sel_reg = WCD934X_CDC_DSD0_CFG0 + dsd_num * 16; + + switch (sample_rate) { + case 176400: + dsd_mode = 0; /* DSD_64 */ + *pcm_rate_val = 0xb; + break; + case 352800: + dsd_mode = 1; /* DSD_128 */ + *pcm_rate_val = 0xc; + break; + default: + dev_err(codec->dev, "%s: Invalid DSD rate: %d\n", + __func__, sample_rate); + return -EINVAL; + } + + snd_soc_update_bits(codec, dsd_out_sel_reg, 0x01, dsd_mode); + + return 0; +} + +static void tavil_dsd_data_pull(struct snd_soc_codec *codec, int dsd_num, + u8 pcm_rate_val, bool enable) +{ + u8 clk_en, mute_en; + u8 dsd_inp_sel; + + if (enable) { + clk_en = 0x20; + mute_en = 0x10; + } else { + clk_en = 0x00; + mute_en = 0x00; + } + + if (dsd_num & 0x01) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_MIX_CTL, + 0x20, clk_en); + dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & + 0x3C) >> 2; + dsd_inp_sel = (enable) ? dsd_inp_sel : 0; + if (dsd_inp_sel < 9) { + snd_soc_update_bits(codec, + WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, + 0x0F, dsd_inp_sel); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_CTL, + 0x0F, pcm_rate_val); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_CTL, + 0x10, mute_en); + } + } + if (dsd_num & 0x02) { + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_MIX_CTL, + 0x20, clk_en); + dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & + 0x3C) >> 2; + dsd_inp_sel = (enable) ? dsd_inp_sel : 0; + if (dsd_inp_sel < 9) { + snd_soc_update_bits(codec, + WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, + 0x0F, dsd_inp_sel); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_CTL, + 0x0F, pcm_rate_val); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_CTL, + 0x10, mute_en); + } + } +} + +static void tavil_dsd_update_volume(struct tavil_dsd_config *dsd_conf) +{ + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0, + 0x01, 0x01); + snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0, + 0x01, 0x00); +} + +static int tavil_enable_dsd(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + int rc, clk_users; + int interp_idx; + u8 pcm_rate_val; + + if (!dsd_conf) { + dev_err(codec->dev, "%s: null dsd_config pointer\n", __func__); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: DSD%d, event: %d\n", __func__, + w->shift, event); + + if (w->shift == DSD0) { + /* Read out select */ + if (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x02) + interp_idx = INTERP_LO1; + else + interp_idx = INTERP_HPHL; + } else if (w->shift == DSD1) { + /* Read out select */ + if (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x02) + interp_idx = INTERP_LO2; + else + interp_idx = INTERP_HPHR; + } else { + dev_err(codec->dev, "%s: Unsupported DSD:%d\n", + __func__, w->shift); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + clk_users = tavil_codec_enable_interp_clk(codec, event, + interp_idx); + + rc = tavil_set_dsd_mode(codec, w->shift, &pcm_rate_val); + if (rc) + return rc; + + tavil_dsd_data_pull(codec, (1 << w->shift), pcm_rate_val, + true); + + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x01, + 0x01); + if (w->shift == DSD0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x01, 0x01); + /* Apply Gain */ + snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1, + dsd_conf->volume[DSD0]); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + + } else if (w->shift == DSD1) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x01, 0x01); + /* Apply Gain */ + snd_soc_write(codec, WCD934X_CDC_DSD1_CFG1, + dsd_conf->volume[DSD1]); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + } + /* 10msec sleep required after DSD clock is set */ + usleep_range(10000, 10100); + + if (clk_users > 1) { + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x02, 0x02); + if (w->shift == DSD0) + snd_soc_update_bits(codec, + WCD934X_CDC_DSD0_CFG2, + 0x04, 0x00); + if (w->shift == DSD1) + snd_soc_update_bits(codec, + WCD934X_CDC_DSD1_CFG2, + 0x04, 0x00); + + } + break; + case SND_SOC_DAPM_POST_PMD: + if (w->shift == DSD0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL, + 0x01, 0x00); + } else if (w->shift == DSD1) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL, + 0x01, 0x00); + } + + tavil_codec_enable_interp_clk(codec, event, interp_idx); + + if (!(snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01) && + !(snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) { + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, + 0x01, 0x00); + tavil_dsd_data_pull(codec, 0x03, 0x04, false); + tavil_dsd_reset(dsd_conf); + } + break; + } + + return 0; +} + +static int tavil_dsd_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = DSD_VOLUME_MIN_M110dB; + uinfo->value.integer.max = DSD_VOLUME_MAX_0dB; + + return 0; +} + +static int tavil_dsd_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + int nv[DSD_MAX], cv[DSD_MAX]; + int step_size, nv1; + int i, dsd_idx; + + if (!dsd_conf) + return 0; + + mutex_lock(&dsd_conf->vol_mutex); + + for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) { + cv[dsd_idx] = dsd_conf->volume[dsd_idx]; + nv[dsd_idx] = ucontrol->value.integer.value[dsd_idx]; + } + + if ((!DSD_VOLUME_RANGE_CHECK(nv[DSD0])) || + (!DSD_VOLUME_RANGE_CHECK(nv[DSD1]))) + goto done; + + for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) { + if (cv[dsd_idx] == nv[dsd_idx]) + continue; + + dev_dbg(codec->dev, "%s: DSD%d cur.vol: %d, new vol: %d\n", + __func__, dsd_idx, cv[dsd_idx], nv[dsd_idx]); + + step_size = (nv[dsd_idx] - cv[dsd_idx]) / + DSD_VOLUME_STEPS; + + nv1 = cv[dsd_idx]; + + for (i = 0; i < DSD_VOLUME_STEPS; i++) { + nv1 += step_size; + snd_soc_write(codec, + WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, + nv1); + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + + /* sleep required after each volume step */ + usleep_range(DSD_VOLUME_STEP_DELAY_US, + (DSD_VOLUME_STEP_DELAY_US + + DSD_VOLUME_USLEEP_MARGIN_US)); + } + if (nv1 != nv[dsd_idx]) { + snd_soc_write(codec, + WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx, + nv[dsd_idx]); + + if (dsd_conf->version == TAVIL_VERSION_1_1) + tavil_dsd_update_volume(dsd_conf); + } + + dsd_conf->volume[dsd_idx] = nv[dsd_idx]; + } + +done: + mutex_unlock(&dsd_conf->vol_mutex); + + return 0; +} + +static int tavil_dsd_vol_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec); + + if (dsd_conf) { + ucontrol->value.integer.value[0] = dsd_conf->volume[DSD0]; + ucontrol->value.integer.value[1] = dsd_conf->volume[DSD1]; + } + + return 0; +} + +static const struct snd_kcontrol_new tavil_dsd_vol_controls[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ), + .name = "DSD Volume", + .info = tavil_dsd_vol_info, + .get = tavil_dsd_vol_get, + .put = tavil_dsd_vol_put, + .tlv = { .p = tavil_dsd_db_scale }, + }, +}; + +static const struct snd_soc_dapm_widget tavil_dsd_widgets[] = { + SND_SOC_DAPM_MUX("DSD_L IF MUX", SND_SOC_NOPM, 0, 0, &dsd_l_if_mux), + SND_SOC_DAPM_MUX_E("DSD_FILTER_0", SND_SOC_NOPM, 0, 0, &dsd_filt0_mux, + tavil_enable_dsd, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("DSD_R IF MUX", SND_SOC_NOPM, 0, 0, &dsd_r_if_mux), + SND_SOC_DAPM_MUX_E("DSD_FILTER_1", SND_SOC_NOPM, 1, 0, &dsd_filt1_mux, + tavil_enable_dsd, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +/** + * tavil_dsd_post_ssr_init - DSD intialization after subsystem restart + * + * @codec: pointer to snd_soc_codec + * + * Returns 0 on success or error on failure + */ +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_conf) +{ + struct snd_soc_codec *codec; + + if (!dsd_conf || !dsd_conf->codec) + return -EINVAL; + + codec = dsd_conf->codec; + /* Disable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08); + + /* DSD registers init */ + if (dsd_conf->version == TAVIL_VERSION_1_0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00); + } + /* DSD0: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04); + /* DSD1: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07, + 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07, + 0x04); + + /* Enable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00); + + return 0; +} +EXPORT_SYMBOL(tavil_dsd_post_ssr_init); + +/** + * tavil_dsd_init - DSD intialization + * + * @codec: pointer to snd_soc_codec + * + * Returns pointer to tavil_dsd_config for success or NULL for failure + */ +struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm; + struct tavil_dsd_config *dsd_conf; + u8 val; + + if (!codec) + return NULL; + + dapm = snd_soc_codec_get_dapm(codec); + + /* Read efuse register to check if DSD is supported */ + val = snd_soc_read(codec, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14); + if (val & 0x80) { + dev_info(codec->dev, "%s: DSD unsupported for this codec version\n", + __func__); + return NULL; + } + + dsd_conf = devm_kzalloc(codec->dev, sizeof(struct tavil_dsd_config), + GFP_KERNEL); + if (!dsd_conf) + return NULL; + + dsd_conf->codec = codec; + + /* Read version */ + dsd_conf->version = snd_soc_read(codec, + WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0); + /* DSD registers init */ + if (dsd_conf->version == TAVIL_VERSION_1_0) { + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00); + } + /* DSD0: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04); + /* DSD1: Mute EN */ + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10, + 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E, + 0x0A); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07, + 0x04); + snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07, + 0x04); + + snd_soc_dapm_new_controls(dapm, tavil_dsd_widgets, + ARRAY_SIZE(tavil_dsd_widgets)); + + snd_soc_dapm_add_routes(dapm, tavil_dsd_audio_map, + ARRAY_SIZE(tavil_dsd_audio_map)); + + mutex_init(&dsd_conf->vol_mutex); + dsd_conf->volume[DSD0] = DSD_VOLUME_MAX_0dB; + dsd_conf->volume[DSD1] = DSD_VOLUME_MAX_0dB; + + snd_soc_add_codec_controls(codec, tavil_dsd_vol_controls, + ARRAY_SIZE(tavil_dsd_vol_controls)); + + /* Enable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00); + + return dsd_conf; +} +EXPORT_SYMBOL(tavil_dsd_init); + +/** + * tavil_dsd_deinit - DSD de-intialization + * + * @dsd_conf: pointer to tavil_dsd_config + */ +void tavil_dsd_deinit(struct tavil_dsd_config *dsd_conf) +{ + struct snd_soc_codec *codec; + + if (!dsd_conf) + return; + + codec = dsd_conf->codec; + + mutex_destroy(&dsd_conf->vol_mutex); + + /* Disable DSD Interrupts */ + snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08); + + devm_kfree(codec->dev, dsd_conf); +} +EXPORT_SYMBOL(tavil_dsd_deinit); diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.h b/sound/soc/codecs/wcd934x/wcd934x-dsd.h new file mode 100644 index 000000000000..834b96cd1805 --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.h @@ -0,0 +1,97 @@ +/* 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 __WCD934X_DSD_H__ +#define __WCD934X_DSD_H__ + +#include +#include "wcd934x.h" + +enum { + DSD0, + DSD1, + DSD_MAX, +}; + +enum { + DSD_INP_SEL_ZERO = 0, + DSD_INP_SEL_RX0, + DSD_INP_SEL_RX1, + DSD_INP_SEL_RX2, + DSD_INP_SEL_RX3, + DSD_INP_SEL_RX4, + DSD_INP_SEL_RX5, + DSD_INP_SEL_RX6, + DSD_INP_SEL_RX7, +}; + +struct tavil_dsd_config { + struct snd_soc_codec *codec; + unsigned int dsd_interp_mixer[INTERP_MAX]; + u32 base_sample_rate[DSD_MAX]; + int volume[DSD_MAX]; + struct mutex vol_mutex; + int version; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD934X_DSD) +int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num, int sw_value); +int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num); +int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf, + int interp_num); +void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf); +void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port, + u32 sample_rate, u8 sample_rate_val); +struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec); +void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config); +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config); +#else +int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num, int sw_value) +{ + return 0; +} + +int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + return 0; +} + +int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf, + int interp_num) +{ + return 0; +} + +void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf) +{ } + +void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port, + u32 sample_rate, u8 sample_rate_val) +{ } + +struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec) +{ + return NULL; +} + +void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config) +{ } +int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config) +{ + return 0; +} +#endif +#endif diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c new file mode 100644 index 000000000000..8da042531fd8 --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -0,0 +1,1369 @@ +/* + * 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 "wcd934x.h" +#include "wcd934x-dsp-cntl.h" + +#define WCD_CNTL_DIR_NAME_LEN_MAX 32 +#define WCD_CPE_FLL_MAX_RETRIES 5 +#define WCD_MEM_ENABLE_MAX_RETRIES 20 +#define WCD_DSP_BOOT_TIMEOUT_MS 3000 +#define WCD_SYSFS_ENTRY_MAX_LEN 8 +#define WCD_PROCFS_ENTRY_MAX_LEN 16 +#define WCD_934X_RAMDUMP_START_ADDR 0x20100000 +#define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128) + +#define WCD_CNTL_MUTEX_LOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WCD_CNTL_MUTEX_UNLOCK(codec, lock) \ +{ \ + dev_dbg(codec->dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +enum wcd_mem_type { + WCD_MEM_TYPE_ALWAYS_ON, + WCD_MEM_TYPE_SWITCHABLE, +}; + +struct wcd_cntl_attribute { + struct attribute attr; + ssize_t (*show)(struct wcd_dsp_cntl *cntl, char *buf); + ssize_t (*store)(struct wcd_dsp_cntl *cntl, const char *buf, + ssize_t count); +}; + +#define WCD_CNTL_ATTR(_name, _mode, _show, _store) \ +static struct wcd_cntl_attribute cntl_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ + .show = _show, \ + .store = _store, \ +} + +#define to_wcd_cntl_attr(a) \ + container_of((a), struct wcd_cntl_attribute, attr) + +#define to_wcd_cntl(kobj) \ + container_of((kobj), struct wcd_dsp_cntl, wcd_kobj) + +static u8 mem_enable_values[] = { + 0xFE, 0xFC, 0xF8, 0xF0, + 0xE0, 0xC0, 0x80, 0x00, +}; + +static ssize_t wdsp_boot_show(struct wcd_dsp_cntl *cntl, char *buf) +{ + return snprintf(buf, WCD_SYSFS_ENTRY_MAX_LEN, + "%u", cntl->boot_reqs); +} + +static ssize_t wdsp_boot_store(struct wcd_dsp_cntl *cntl, + const char *buf, ssize_t count) +{ + u32 val; + bool vote; + int ret; + + ret = kstrtou32(buf, 10, &val); + if (ret) { + dev_err(cntl->codec->dev, + "%s: Invalid entry, ret = %d\n", __func__, ret); + return -EINVAL; + } + + if (val > 0) { + cntl->boot_reqs++; + vote = true; + } else { + cntl->boot_reqs--; + vote = false; + } + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->vote_for_dsp) + ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote); + else + ret = -EINVAL; + + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: failed to %s dsp\n", __func__, + vote ? "enable" : "disable"); + return count; +} + +WCD_CNTL_ATTR(boot, 0660, wdsp_boot_show, wdsp_boot_store); + +static ssize_t wcd_cntl_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr); + struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj); + ssize_t ret = -EINVAL; + + if (cntl && wcd_attr->show) + ret = wcd_attr->show(cntl, buf); + + return ret; +} + +static ssize_t wcd_cntl_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr); + struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj); + ssize_t ret = -EINVAL; + + if (cntl && wcd_attr->store) + ret = wcd_attr->store(cntl, buf, count); + + return ret; +} + +static const struct sysfs_ops wcd_cntl_sysfs_ops = { + .show = wcd_cntl_sysfs_show, + .store = wcd_cntl_sysfs_store, +}; + +static struct kobj_type wcd_cntl_ktype = { + .sysfs_ops = &wcd_cntl_sysfs_ops, +}; + +static void wcd_cntl_change_online_state(struct wcd_dsp_cntl *cntl, + u8 online) +{ + struct wdsp_ssr_entry *ssr_entry = &cntl->ssr_entry; + unsigned long ret; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + ssr_entry->offline = !online; + /* Make sure the write is complete */ + wmb(); + ret = xchg(&ssr_entry->offline_change, 1); + wake_up_interruptible(&ssr_entry->offline_poll_wait); + dev_dbg(cntl->codec->dev, + "%s: requested %u, offline %u offline_change %u, ret = %ldn", + __func__, online, ssr_entry->offline, + ssr_entry->offline_change, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); +} + +static ssize_t wdsp_ssr_entry_read(struct snd_info_entry *entry, + void *file_priv_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + int len = 0; + char buffer[WCD_PROCFS_ENTRY_MAX_LEN]; + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + ssize_t ret; + u8 offline; + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + if (!cntl) { + pr_err("%s: Invalid private data for SSR procfs entry\n", + __func__); + return -EINVAL; + } + + ssr_entry = &cntl->ssr_entry; + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + offline = ssr_entry->offline; + /* Make sure the read is complete */ + rmb(); + dev_dbg(cntl->codec->dev, "%s: offline = %s\n", __func__, + offline ? "true" : "false"); + len = snprintf(buffer, sizeof(buffer), "%s\n", + offline ? "OFFLINE" : "ONLINE"); + ret = simple_read_from_buffer(buf, count, &pos, buffer, len); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static unsigned int wdsp_ssr_entry_poll(struct snd_info_entry *entry, + void *private_data, struct file *file, + poll_table *wait) +{ + struct wcd_dsp_cntl *cntl; + struct wdsp_ssr_entry *ssr_entry; + unsigned int ret = 0; + + if (!entry || !entry->private_data) { + pr_err("%s: %s is NULL\n", __func__, + (!entry) ? "entry" : "private_data"); + return -EINVAL; + } + + cntl = (struct wcd_dsp_cntl *) entry->private_data; + ssr_entry = &cntl->ssr_entry; + + dev_dbg(cntl->codec->dev, "%s: Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + poll_wait(file, &ssr_entry->offline_poll_wait, wait); + dev_dbg(cntl->codec->dev, "%s: Woken up Poll wait, offline = %u\n", + __func__, ssr_entry->offline); + + WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex); + if (xchg(&ssr_entry->offline_change, 0)) + ret = POLLIN | POLLPRI | POLLRDNORM; + dev_dbg(cntl->codec->dev, "%s: ret (%d) from poll_wait\n", + __func__, ret); + WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex); + + return ret; +} + +static struct snd_info_entry_ops wdsp_ssr_entry_ops = { + .read = wdsp_ssr_entry_read, + .poll = wdsp_ssr_entry_poll, +}; + +static int wcd_cntl_cpe_fll_calibrate(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0, retry = 0; + u8 cal_lsb, cal_msb; + u8 lock_det; + + /* Make sure clocks are gated */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x05, 0x00); + + /* Enable CPE FLL reference clock */ + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x80, 0x80); + + snd_soc_update_bits(codec, WCD934X_CPE_FLL_USER_CTL_5, + 0xF3, 0x13); + snd_soc_write(codec, WCD934X_CPE_FLL_L_VAL_CTL_0, 0x50); + + /* Disable CPAR reset and Enable CPAR clk */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, + 0x02, 0x02); + + /* Write calibration l-value based on cdc clk rate */ + if (cntl->clk_rate == 9600000) { + cal_lsb = 0x6d; + cal_msb = 0x00; + } else { + cal_lsb = 0x56; + cal_msb = 0x00; + } + snd_soc_write(codec, WCD934X_CPE_FLL_USER_CTL_6, cal_lsb); + snd_soc_write(codec, WCD934X_CPE_FLL_USER_CTL_7, cal_msb); + + /* FLL mode to follow power up sequence */ + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x60, 0x00); + + /* HW controlled CPE FLL */ + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x80, 0x80); + + /* Force on CPE FLL */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x04); + + do { + /* Time for FLL calibration to complete */ + usleep_range(1000, 1100); + lock_det = snd_soc_read(codec, WCD934X_CPE_FLL_STATUS_3); + retry++; + } while (!(lock_det & 0x01) && + retry <= WCD_CPE_FLL_MAX_RETRIES); + + if (!(lock_det & 0x01)) { + dev_err(codec->dev, "%s: lock detect not set, 0x%02x\n", + __func__, lock_det); + ret = -EIO; + goto err_lock_det; + } + + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x60, 0x20); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x00); + return ret; + +err_lock_det: + /* Undo the register settings */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, + 0x02, 0x00); + return ret; +} + +static void wcd_cntl_config_cpar(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + u8 nom_lo, nom_hi, svs2_lo, svs2_hi; + + /* Configure CPAR */ + nom_hi = svs2_hi = 0; + if (cntl->clk_rate == 9600000) { + nom_lo = 0x90; + svs2_lo = 0x50; + } else { + nom_lo = 0x70; + svs2_lo = 0x3e; + } + + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_NOM_LOW, nom_lo); + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_NOM_HIGH, nom_hi); + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW, svs2_lo); + snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH, svs2_hi); + + snd_soc_update_bits(codec, WCD934X_CPE_SS_PWR_CPEFLL_CTL, + 0x03, 0x03); +} + +static int wcd_cntl_cpe_fll_ctrl(struct wcd_dsp_cntl *cntl, + bool enable) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + if (enable) { + ret = wcd_cntl_cpe_fll_calibrate(cntl); + if (ret < 0) { + dev_err(codec->dev, + "%s: cpe_fll_cal failed, err = %d\n", + __func__, ret); + goto done; + } + + wcd_cntl_config_cpar(cntl); + + /* Enable AHB CLK and CPE CLK*/ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x05, 0x05); + } else { + /* Disable AHB CLK and CPE CLK */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x05, 0x00); + /* Reset the CPAR mode for CPE FLL */ + snd_soc_write(codec, WCD934X_CPE_FLL_FLL_MODE, 0x20); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, + 0x02, 0x00); + } +done: + return ret; +} + +static int wcd_cntl_clocks_enable(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret; + + WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex); + /* Enable codec clock */ + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + ret = cntl->cdc_cb->cdc_clk_en(codec, true); + else + ret = -EINVAL; + + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to enable cdc clk, err = %d\n", + __func__, ret); + goto done; + } + /* Pull CPAR out of reset */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x04, 0x00); + + /* Configure and Enable CPE FLL clock */ + ret = wcd_cntl_cpe_fll_ctrl(cntl, true); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to enable cpe clk, err = %d\n", + __func__, ret); + goto err_cpe_clk; + } + cntl->is_clk_enabled = true; + + /* Ungate the CPR clock */ + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, 0x10, 0x00); +done: + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; + +err_cpe_clk: + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + cntl->cdc_cb->cdc_clk_en(codec, false); + + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x04, 0x04); + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; +} + +static int wcd_cntl_clocks_disable(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex); + if (!cntl->is_clk_enabled) { + dev_info(codec->dev, "%s: clocks already disabled\n", + __func__); + goto done; + } + + /* Gate the CPR clock */ + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, 0x10, 0x10); + + /* Disable CPE FLL clock */ + ret = wcd_cntl_cpe_fll_ctrl(cntl, false); + if (ret < 0) + dev_err(codec->dev, + "%s: Failed to disable cpe clk, err = %d\n", + __func__, ret); + + /* + * Even if CPE FLL disable failed, go ahead and disable + * the codec clock + */ + if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en) + ret = cntl->cdc_cb->cdc_clk_en(codec, false); + else + ret = -EINVAL; + + cntl->is_clk_enabled = false; + + /* Put CPAR in reset */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x04, 0x04); +done: + WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex); + return ret; +} + +static void wcd_cntl_cpar_ctrl(struct wcd_dsp_cntl *cntl, + bool enable) +{ + struct snd_soc_codec *codec = cntl->codec; + + if (enable) + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x03, 0x03); + else + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL, 0x03, 0x00); +} + +static int wcd_cntl_enable_memory(struct wcd_dsp_cntl *cntl, + enum wcd_mem_type mem_type) +{ + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int loop_cnt = 0; + u8 status; + int ret = 0; + + + switch (mem_type) { + + case WCD_MEM_TYPE_ALWAYS_ON: + + /* 512KB of always on region */ + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + break; + + case WCD_MEM_TYPE_SWITCHABLE: + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x04, 0x00); + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL, + 0x80, 0x80); + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x01, 0x01); + do { + loop_cnt++; + /* Time to enable the power domain for memory */ + usleep_range(100, 150); + status = snd_soc_read(codec, + WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL); + } while ((status & 0x02) != 0x02 && + loop_cnt != WCD_MEM_ENABLE_MAX_RETRIES); + + if ((status & 0x02) != 0x02) { + dev_err(cntl->codec->dev, + "%s: power domain not enabled, status = 0x%02x\n", + __func__, status); + ret = -EIO; + goto done; + } + + /* Rest of the memory */ + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + wcd9xxx_slim_write_repeat(wcd9xxx, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + ARRAY_SIZE(mem_enable_values), + mem_enable_values); + + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + 0x05); + break; + + default: + dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n", + __func__, mem_type); + ret = -EINVAL; + break; + } +done: + /* Make sure Deep sleep of memories is enabled for all banks */ + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); + + return ret; +} + +static void wcd_cntl_disable_memory(struct wcd_dsp_cntl *cntl, + enum wcd_mem_type mem_type) +{ + struct snd_soc_codec *codec = cntl->codec; + u8 val; + + switch (mem_type) { + case WCD_MEM_TYPE_ALWAYS_ON: + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + 0xFF); + break; + case WCD_MEM_TYPE_SWITCHABLE: + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + 0x07); + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + 0x01, 0x00); + val = snd_soc_read(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL); + if (val & 0x02) + dev_err(codec->dev, + "%s: Disable switchable failed, val = 0x%02x", + __func__, val); + + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL, + 0x80, 0x00); + break; + default: + dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n", + __func__, mem_type); + break; + } + + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F); +} + +static void wcd_cntl_do_shutdown(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + + /* Disable WDOG */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x3F, 0x01); + + /* Put WDSP in reset state */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x02, 0x00); + + /* If DSP transitions from boot to shutdown, then vote for SVS */ + if (cntl->is_wdsp_booted) + cntl->cdc_cb->cdc_vote_svs(codec, true); + cntl->is_wdsp_booted = false; +} + +static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + /* + * Debug mode is set from debugfs file node. If debug_mode + * is set, then do not configure the watchdog timer. This + * will be required for debugging the DSP firmware. + */ + if (cntl->debug_mode) { + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x3F, 0x01); + } else { + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x3F, 0x21); + } + + /* Make sure all the error interrupts are cleared */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B, 0xFF); + + reinit_completion(&cntl->boot_complete); + + /* Remove WDSP out of reset */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL, + 0x02, 0x02); + + /* + * In debug mode, DSP may not boot up normally, + * wait indefinitely for DSP to boot. + */ + if (cntl->debug_mode) { + wait_for_completion(&cntl->boot_complete); + dev_dbg(codec->dev, "%s: WDSP booted in dbg mode\n", __func__); + cntl->is_wdsp_booted = true; + goto done; + } + + /* Boot in normal mode */ + ret = wait_for_completion_timeout(&cntl->boot_complete, + msecs_to_jiffies(WCD_DSP_BOOT_TIMEOUT_MS)); + if (!ret) { + dev_err(codec->dev, "%s: WDSP boot timed out\n", + __func__); + ret = -ETIMEDOUT; + goto err_boot; + } else { + /* + * Re-initialize the return code to 0, as in success case, + * it will hold the remaining time for completion timeout + */ + ret = 0; + } + + dev_dbg(codec->dev, "%s: WDSP booted in normal mode\n", __func__); + cntl->is_wdsp_booted = true; + + /* Enable WDOG */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG, + 0x10, 0x10); +done: + /* If dsp booted up, then remove vote on SVS */ + if (cntl->is_wdsp_booted) + cntl->cdc_cb->cdc_vote_svs(codec, false); + + return ret; +err_boot: + /* call shutdown to perform cleanup */ + wcd_cntl_do_shutdown(cntl); + return ret; +} + +static irqreturn_t wcd_cntl_ipc_irq(int irq, void *data) +{ + struct wcd_dsp_cntl *cntl = data; + int ret; + + complete(&cntl->boot_complete); + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->signal_handler) + ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_IPC1_INTR, + NULL); + else + ret = -EINVAL; + + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: Failed to handle irq %d\n", __func__, irq); + + return IRQ_HANDLED; +} + +static irqreturn_t wcd_cntl_err_irq(int irq, void *data) +{ + struct wcd_dsp_cntl *cntl = data; + struct snd_soc_codec *codec = cntl->codec; + struct wdsp_err_signal_arg arg; + u16 status = 0; + u8 reg_val; + int ret = 0; + + reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A); + status = status | reg_val; + + reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B); + status = status | (reg_val << 8); + + dev_info(codec->dev, "%s: error interrupt status = 0x%x\n", + __func__, status); + + if ((status & cntl->irqs.fatal_irqs) && + (cntl->m_dev && cntl->m_ops && cntl->m_ops->signal_handler)) { + arg.mem_dumps_enabled = cntl->ramdump_enable; + arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR; + arg.dump_size = WCD_934X_RAMDUMP_SIZE; + ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_ERR_INTR, + &arg); + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: Failed to handle fatal irq 0x%x\n", + __func__, status & cntl->irqs.fatal_irqs); + wcd_cntl_change_online_state(cntl, 0); + } else { + dev_err(cntl->codec->dev, "%s: Invalid signal_handler\n", + __func__); + } + + return IRQ_HANDLED; +} + +static int wcd_control_handler(struct device *dev, void *priv_data, + enum wdsp_event_type event, void *data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + switch (event) { + case WDSP_EVENT_POST_INIT: + case WDSP_EVENT_POST_DLOAD_CODE: + case WDSP_EVENT_DLOAD_FAILED: + case WDSP_EVENT_POST_SHUTDOWN: + + if (event == WDSP_EVENT_POST_DLOAD_CODE) + /* Mark DSP online since code download is complete */ + wcd_cntl_change_online_state(cntl, 1); + + /* Disable CPAR */ + wcd_cntl_cpar_ctrl(cntl, false); + /* Disable all the clocks */ + ret = wcd_cntl_clocks_disable(cntl); + if (ret < 0) + dev_err(codec->dev, + "%s: Failed to disable clocks, err = %d\n", + __func__, ret); + break; + + case WDSP_EVENT_PRE_DLOAD_DATA: + case WDSP_EVENT_PRE_DLOAD_CODE: + + /* Enable all the clocks */ + ret = wcd_cntl_clocks_enable(cntl); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to enable clocks, err = %d\n", + __func__, ret); + goto done; + } + + /* Enable CPAR */ + wcd_cntl_cpar_ctrl(cntl, true); + + if (event == WDSP_EVENT_PRE_DLOAD_CODE) + wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_ALWAYS_ON); + else if (event == WDSP_EVENT_PRE_DLOAD_DATA) + wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE); + break; + + case WDSP_EVENT_DO_BOOT: + + ret = wcd_cntl_do_boot(cntl); + if (ret < 0) + dev_err(codec->dev, + "%s: WDSP boot failed, err = %d\n", + __func__, ret); + break; + + case WDSP_EVENT_DO_SHUTDOWN: + + wcd_cntl_do_shutdown(cntl); + wcd_cntl_disable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE); + break; + + default: + dev_dbg(codec->dev, "%s: unhandled event %d\n", + __func__, event); + } + +done: + return ret; +} + +static int wcd_cntl_sysfs_init(char *dir, struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + int ret = 0; + + ret = kobject_init_and_add(&cntl->wcd_kobj, &wcd_cntl_ktype, + kernel_kobj, dir); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to add kobject %s, err = %d\n", + __func__, dir, ret); + goto done; + } + + ret = sysfs_create_file(&cntl->wcd_kobj, &cntl_attr_boot.attr); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to add wdsp_boot sysfs entry to %s\n", + __func__, dir); + goto fail_create_file; + } + + return ret; + +fail_create_file: + kobject_put(&cntl->wcd_kobj); +done: + return ret; +} + +static void wcd_cntl_sysfs_remove(struct wcd_dsp_cntl *cntl) +{ + sysfs_remove_file(&cntl->wcd_kobj, &cntl_attr_boot.attr); + kobject_put(&cntl->wcd_kobj); +} + +static void wcd_cntl_debugfs_init(char *dir, struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + + cntl->entry = debugfs_create_dir(dir, NULL); + if (IS_ERR_OR_NULL(dir)) { + dev_err(codec->dev, "%s debugfs_create_dir failed for %s\n", + __func__, dir); + goto done; + } + + debugfs_create_u32("debug_mode", 0644, + cntl->entry, &cntl->debug_mode); + debugfs_create_bool("ramdump_enable", 0644, + cntl->entry, &cntl->ramdump_enable); +done: + return; +} + +static void wcd_cntl_debugfs_remove(struct wcd_dsp_cntl *cntl) +{ + if (cntl) + debugfs_remove(cntl->entry); +} + +static int wcd_miscdev_release(struct inode *inode, struct file *filep) +{ + struct wcd_dsp_cntl *cntl = container_of(filep->private_data, + struct wcd_dsp_cntl, miscdev); + if (!cntl->m_dev || !cntl->m_ops || + !cntl->m_ops->vote_for_dsp) { + dev_err(cntl->codec->dev, + "%s: DSP not ready to boot\n", __func__); + return -EINVAL; + } + + /* Make sure the DSP users goes to zero upon closing dev node */ + while (cntl->boot_reqs > 0) { + cntl->m_ops->vote_for_dsp(cntl->m_dev, false); + cntl->boot_reqs--; + } + + return 0; +} + +static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, + size_t count, loff_t *pos) +{ + struct wcd_dsp_cntl *cntl = container_of(filep->private_data, + struct wcd_dsp_cntl, miscdev); + char val[count]; + bool vote; + int ret = 0; + + if (count == 0 || count > 2) { + pr_err("%s: Invalid count = %zd\n", __func__, count); + ret = -EINVAL; + goto done; + } + + ret = copy_from_user(val, ubuf, count); + if (ret < 0) { + dev_err(cntl->codec->dev, + "%s: copy_from_user failed, err = %d\n", + __func__, ret); + ret = -EFAULT; + goto done; + } + + if (val[0] == '1') { + cntl->boot_reqs++; + vote = true; + } else if (val[0] == '0') { + if (cntl->boot_reqs == 0) { + dev_err(cntl->codec->dev, + "%s: WDSP already disabled\n", __func__); + ret = -EINVAL; + goto done; + } + cntl->boot_reqs--; + vote = false; + } else { + dev_err(cntl->codec->dev, "%s: Invalid value %s\n", + __func__, val); + ret = -EINVAL; + goto done; + } + + dev_dbg(cntl->codec->dev, + "%s: booted = %s, ref_cnt = %d, vote = %s\n", + __func__, cntl->is_wdsp_booted ? "true" : "false", + cntl->boot_reqs, vote ? "true" : "false"); + + if (cntl->m_dev && cntl->m_ops && + cntl->m_ops->vote_for_dsp) + ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote); + else + ret = -EINVAL; +done: + if (ret) + return ret; + else + return count; +} + +static const struct file_operations wcd_miscdev_fops = { + .write = wcd_miscdev_write, + .release = wcd_miscdev_release, +}; + +static int wcd_cntl_miscdev_create(struct wcd_dsp_cntl *cntl) +{ + snprintf(cntl->miscdev_name, ARRAY_SIZE(cntl->miscdev_name), + "wcd_dsp%u_control", cntl->dsp_instance); + cntl->miscdev.minor = MISC_DYNAMIC_MINOR; + cntl->miscdev.name = cntl->miscdev_name; + cntl->miscdev.fops = &wcd_miscdev_fops; + cntl->miscdev.parent = cntl->codec->dev; + + return misc_register(&cntl->miscdev); +} + +static void wcd_cntl_miscdev_destroy(struct wcd_dsp_cntl *cntl) +{ + misc_deregister(&cntl->miscdev); +} + +static int wcd_control_init(struct device *dev, void *priv_data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + int ret; + bool err_irq_requested = false; + + ret = wcd9xxx_request_irq(core_res, + cntl->irqs.cpe_ipc1_irq, + wcd_cntl_ipc_irq, "CPE IPC1", + cntl); + if (ret < 0) { + dev_err(codec->dev, + "%s: Failed to request cpe ipc irq, err = %d\n", + __func__, ret); + goto done; + } + + /* Unmask the fatal irqs */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, + ~(cntl->irqs.fatal_irqs & 0xFF)); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, + ~((cntl->irqs.fatal_irqs >> 8) & 0xFF)); + + /* + * CPE ERR irq is used only for error reporting from WCD DSP, + * even if this request fails, DSP can be function normally. + * Continuing with init even if the CPE ERR irq request fails. + */ + if (wcd9xxx_request_irq(core_res, cntl->irqs.cpe_err_irq, + wcd_cntl_err_irq, "CPE ERR", cntl)) + dev_info(codec->dev, "%s: Failed request_irq(cpe_err_irq)", + __func__); + else + err_irq_requested = true; + + + /* Enable all the clocks */ + ret = wcd_cntl_clocks_enable(cntl); + if (ret < 0) { + dev_err(codec->dev, "%s: Failed to enable clocks, err = %d\n", + __func__, ret); + goto err_clk_enable; + } + wcd_cntl_cpar_ctrl(cntl, true); + + return 0; + +err_clk_enable: + /* Mask all error interrupts */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF); + + /* Free the irq's requested */ + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl); + + if (err_irq_requested) + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl); +done: + return ret; +} + +static int wcd_control_deinit(struct device *dev, void *priv_data) +{ + struct wcd_dsp_cntl *cntl = priv_data; + struct snd_soc_codec *codec = cntl->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + + wcd_cntl_clocks_disable(cntl); + wcd_cntl_cpar_ctrl(cntl, false); + + /* Mask all error interrupts */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF); + + /* Free the irq's requested */ + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl); + wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl); + + return 0; +} + +static struct wdsp_cmpnt_ops control_ops = { + .init = wcd_control_init, + .deinit = wcd_control_deinit, + .event_handler = wcd_control_handler, +}; + +static int wcd_ctrl_component_bind(struct device *dev, + struct device *master, + void *data) +{ + struct wcd_dsp_cntl *cntl; + struct snd_soc_codec *codec; + struct snd_card *card; + struct snd_info_entry *entry; + char proc_name[WCD_PROCFS_ENTRY_MAX_LEN]; + char wcd_cntl_dir_name[WCD_CNTL_DIR_NAME_LEN_MAX]; + int ret = 0; + + if (!dev || !master || !data) { + pr_err("%s: Invalid parameters\n", __func__); + return -EINVAL; + } + + cntl = tavil_get_wcd_dsp_cntl(dev); + if (!cntl) { + dev_err(dev, "%s: Failed to get cntl reference\n", + __func__); + return -EINVAL; + } + + cntl->m_dev = master; + cntl->m_ops = data; + + if (!cntl->m_ops->register_cmpnt_ops) { + dev_err(dev, "%s: invalid master callback register_cmpnt_ops\n", + __func__); + ret = -EINVAL; + goto done; + } + + ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, &control_ops); + if (ret) { + dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_cntl_miscdev_create(cntl); + if (ret < 0) { + dev_err(dev, "%s: misc dev register failed, err = %d\n", + __func__, ret); + goto done; + } + + snprintf(wcd_cntl_dir_name, WCD_CNTL_DIR_NAME_LEN_MAX, + "%s%d", "wdsp", cntl->dsp_instance); + ret = wcd_cntl_sysfs_init(wcd_cntl_dir_name, cntl); + if (ret < 0) { + dev_err(dev, "%s: sysfs_init failed, err = %d\n", + __func__, ret); + goto err_sysfs_init; + } + + wcd_cntl_debugfs_init(wcd_cntl_dir_name, cntl); + + codec = cntl->codec; + card = codec->component.card->snd_card; + snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe", + cntl->dsp_instance, "_state"); + entry = snd_info_create_card_entry(card, proc_name, card->proc_root); + if (!entry) { + /* Do not treat this as Fatal error */ + dev_err(dev, "%s: Failed to create procfs entry %s\n", + __func__, proc_name); + goto err_sysfs_init; + } + + cntl->ssr_entry.entry = entry; + cntl->ssr_entry.offline = 1; + entry->size = WCD_PROCFS_ENTRY_MAX_LEN; + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->c.ops = &wdsp_ssr_entry_ops; + entry->private_data = cntl; + ret = snd_info_register(entry); + if (ret < 0) { + dev_err(dev, "%s: Failed to register entry %s, err = %d\n", + __func__, proc_name, ret); + snd_info_free_entry(entry); + /* Let bind still happen even if creating the entry failed */ + ret = 0; + } +done: + return ret; + +err_sysfs_init: + wcd_cntl_miscdev_destroy(cntl); + return ret; +} + +static void wcd_ctrl_component_unbind(struct device *dev, + struct device *master, + void *data) +{ + struct wcd_dsp_cntl *cntl; + + if (!dev) { + pr_err("%s: Invalid device\n", __func__); + return; + } + + cntl = tavil_get_wcd_dsp_cntl(dev); + if (!cntl) { + dev_err(dev, "%s: Failed to get cntl reference\n", + __func__); + return; + } + + cntl->m_dev = NULL; + cntl->m_ops = NULL; + + /* Remove the sysfs entries */ + wcd_cntl_sysfs_remove(cntl); + + /* Remove the debugfs entries */ + wcd_cntl_debugfs_remove(cntl); + + /* Remove the misc device */ + wcd_cntl_miscdev_destroy(cntl); +} + +static const struct component_ops wcd_ctrl_component_ops = { + .bind = wcd_ctrl_component_bind, + .unbind = wcd_ctrl_component_unbind, +}; + +/* + * wcd_dsp_ssr_event: handle the SSR event raised by caller. + * @cntl: Handle to the wcd_dsp_cntl structure + * @event: The SSR event to be handled + * + * Notifies the manager driver about the SSR event. + * Returns 0 on success and negative error code on error. + */ +int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event) +{ + int ret = 0; + + if (!cntl) { + pr_err("%s: Invalid handle to control\n", __func__); + return -EINVAL; + } + + if (!cntl->m_dev || !cntl->m_ops || !cntl->m_ops->signal_handler) { + dev_err(cntl->codec->dev, + "%s: Invalid signal_handler callback\n", __func__); + return -EINVAL; + } + + switch (event) { + case WCD_CDC_DOWN_EVENT: + ret = cntl->m_ops->signal_handler(cntl->m_dev, + WDSP_CDC_DOWN_SIGNAL, + NULL); + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: WDSP_CDC_DOWN_SIGNAL failed, err = %d\n", + __func__, ret); + wcd_cntl_change_online_state(cntl, 0); + break; + case WCD_CDC_UP_EVENT: + ret = cntl->m_ops->signal_handler(cntl->m_dev, + WDSP_CDC_UP_SIGNAL, + NULL); + if (ret < 0) + dev_err(cntl->codec->dev, + "%s: WDSP_CDC_UP_SIGNAL failed, err = %d\n", + __func__, ret); + break; + default: + dev_err(cntl->codec->dev, "%s: Invalid event %d\n", + __func__, event); + ret = -EINVAL; + break; + } + + return ret; +} +EXPORT_SYMBOL(wcd_dsp_ssr_event); + +/* + * wcd_dsp_cntl_init: Initialize the wcd-dsp control + * @codec: pointer to the codec handle + * @params: Parameters required to initialize wcd-dsp control + * + * This API is expected to be invoked by the codec driver and + * provide information essential for the wcd dsp control to + * configure and initialize the dsp + */ +void wcd_dsp_cntl_init(struct snd_soc_codec *codec, + struct wcd_dsp_params *params, + struct wcd_dsp_cntl **cntl) +{ + struct wcd_dsp_cntl *control; + int ret; + + if (!codec || !params) { + pr_err("%s: Invalid handle to %s\n", __func__, + (!codec) ? "codec" : "params"); + *cntl = NULL; + return; + } + + if (*cntl) { + pr_err("%s: cntl is non NULL, maybe already initialized ?\n", + __func__); + return; + } + + if (!params->cb || !params->cb->cdc_clk_en || + !params->cb->cdc_vote_svs) { + dev_err(codec->dev, + "%s: clk_en and vote_svs callbacks must be provided\n", + __func__); + return; + } + + control = kzalloc(sizeof(*control), GFP_KERNEL); + if (!(control)) + return; + + control->codec = codec; + control->clk_rate = params->clk_rate; + control->cdc_cb = params->cb; + control->dsp_instance = params->dsp_instance; + memcpy(&control->irqs, ¶ms->irqs, sizeof(control->irqs)); + init_completion(&control->boot_complete); + mutex_init(&control->clk_mutex); + mutex_init(&control->ssr_mutex); + init_waitqueue_head(&control->ssr_entry.offline_poll_wait); + + /* + * The default state of WDSP is in SVS mode. + * Vote for SVS now, the vote will be removed only + * after DSP is booted up. + */ + control->cdc_cb->cdc_vote_svs(codec, true); + + /* + * If this is the last component needed by master to be ready, + * then component_bind will be called within the component_add. + * Hence, the data pointer should be assigned before component_add, + * so that we can access it during this component's bind call. + */ + *cntl = control; + ret = component_add(codec->dev, &wcd_ctrl_component_ops); + if (ret) { + dev_err(codec->dev, "%s: component_add failed, err = %d\n", + __func__, ret); + kfree(*cntl); + *cntl = NULL; + } +} +EXPORT_SYMBOL(wcd_dsp_cntl_init); + +/* + * wcd_dsp_cntl_deinit: De-initialize the wcd-dsp control + * @cntl: The struct wcd_dsp_cntl to de-initialize + * + * This API is intended to be invoked by the codec driver + * to de-initialize the wcd dsp control + */ +void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl) +{ + struct wcd_dsp_cntl *control = *cntl; + struct snd_soc_codec *codec; + + /* If control is NULL, there is nothing to de-initialize */ + if (!control) + return; + codec = control->codec; + + /* + * Calling shutdown will cleanup all register states, + * irrespective of DSP was booted up or not. + */ + wcd_cntl_do_shutdown(control); + wcd_cntl_disable_memory(control, WCD_MEM_TYPE_SWITCHABLE); + wcd_cntl_disable_memory(control, WCD_MEM_TYPE_ALWAYS_ON); + + component_del(codec->dev, &wcd_ctrl_component_ops); + + mutex_destroy(&control->clk_mutex); + mutex_destroy(&control->ssr_mutex); + kfree(*cntl); + *cntl = NULL; +} +EXPORT_SYMBOL(wcd_dsp_cntl_deinit); diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h new file mode 100644 index 000000000000..e934638cc487 --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h @@ -0,0 +1,119 @@ +/* + * 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 __WCD934X_DSP_CNTL_H__ +#define __WCD934X_DSP_CNTL_H__ + +#include +#include + +enum cdc_ssr_event { + WCD_CDC_DOWN_EVENT, + WCD_CDC_UP_EVENT, +}; + +struct wcd_dsp_cdc_cb { + /* Callback to enable codec clock */ + int (*cdc_clk_en)(struct snd_soc_codec *, bool); + /* Callback to vote and unvote for SVS2 mode */ + void (*cdc_vote_svs)(struct snd_soc_codec *, bool); +}; + +struct wcd_dsp_irq_info { + /* IPC interrupt */ + int cpe_ipc1_irq; + + /* CPE error summary interrupt */ + int cpe_err_irq; + + /* + * Bit mask to indicate which of the + * error interrupts are to be considered + * as fatal. + */ + u16 fatal_irqs; +}; + +struct wcd_dsp_params { + struct wcd_dsp_cdc_cb *cb; + struct wcd_dsp_irq_info irqs; + + /* Rate at which the codec clock operates */ + u32 clk_rate; + + /* + * Represents the dsp instance, will be used + * to create sysfs and debugfs entries with + * directory wdsp + */ + u32 dsp_instance; +}; + +struct wdsp_ssr_entry { + u8 offline; + u8 offline_change; + wait_queue_head_t offline_poll_wait; + struct snd_info_entry *entry; +}; + +struct wcd_dsp_cntl { + /* Handle to codec */ + struct snd_soc_codec *codec; + + /* Clk rate of the codec clock */ + u32 clk_rate; + + /* Callbacks to codec driver */ + const struct wcd_dsp_cdc_cb *cdc_cb; + + /* Completion to indicate WDSP boot done */ + struct completion boot_complete; + + struct wcd_dsp_irq_info irqs; + u32 dsp_instance; + + /* Sysfs entries related */ + int boot_reqs; + struct kobject wcd_kobj; + + /* Debugfs related */ + struct dentry *entry; + u32 debug_mode; + bool ramdump_enable; + + /* WDSP manager drivers data */ + struct device *m_dev; + struct wdsp_mgr_ops *m_ops; + + /* clk related */ + struct mutex clk_mutex; + bool is_clk_enabled; + + /* Keep track of WDSP boot status */ + bool is_wdsp_booted; + + /* SSR related */ + struct wdsp_ssr_entry ssr_entry; + struct mutex ssr_mutex; + + /* Misc device related */ + char miscdev_name[256]; + struct miscdevice miscdev; +}; + +void wcd_dsp_cntl_init(struct snd_soc_codec *codec, + struct wcd_dsp_params *params, + struct wcd_dsp_cntl **cntl); +void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl); +int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event); +#endif /* end __WCD_DSP_CONTROL_H__ */ diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.c b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c new file mode 100644 index 000000000000..a1a5e2d65062 --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c @@ -0,0 +1,1098 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd934x.h" +#include "wcd934x-mbhc.h" +#include "../wcdcal-hwdep.h" +#include "../wcd-mbhc-v2-api.h" + +#define TAVIL_ZDET_SUPPORTED true +/* Z value defined in milliohm */ +#define TAVIL_ZDET_VAL_32 32000 +#define TAVIL_ZDET_VAL_400 400000 +#define TAVIL_ZDET_VAL_1200 1200000 +#define TAVIL_ZDET_VAL_100K 100000000 +/* Z floating defined in ohms */ +#define TAVIL_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE + +#define TAVIL_ZDET_NUM_MEASUREMENTS 150 +#define TAVIL_MBHC_GET_C1(c) ((c & 0xC000) >> 14) +#define TAVIL_MBHC_GET_X1(x) (x & 0x3FFF) +/* Z value compared in milliOhm */ +#define TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000)) +#define TAVIL_MBHC_ZDET_CONST (86 * 16384) +#define TAVIL_MBHC_MOISTURE_RREF R_24_KOHM + +static struct wcd_mbhc_register + wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = { + WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN", + WCD934X_ANA_MBHC_MECH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN", + WCD934X_ANA_MBHC_MECH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE", + WCD934X_ANA_MBHC_MECH, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL", + WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x30, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE", + WCD934X_ANA_MBHC_ELECT, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL", + WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL", + WCD934X_ANA_MBHC_MECH, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE", + WCD934X_ANA_MBHC_MECH, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE", + WCD934X_ANA_MBHC_MECH, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND", + WCD934X_ANA_MBHC_MECH, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC", + WCD934X_ANA_MBHC_ELECT, 0x06, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN", + WCD934X_ANA_MBHC_ELECT, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC", + WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC", + WCD934X_MBHC_NEW_CTL_1, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF", + WCD934X_MBHC_NEW_CTL_2, 0x03, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN", + WCD934X_HPH_OCP_CTL, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0x07, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL", + WCD934X_ANA_MBHC_ELECT, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT", + WCD934X_ANA_MBHC_RESULT_3, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL", + WCD934X_ANA_MICB2, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME", + WCD934X_HPH_CNP_WG_TIME, 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN", + WCD934X_ANA_HPH, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN", + WCD934X_ANA_HPH, 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN", + WCD934X_ANA_HPH, 0xC0, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE", + WCD934X_ANA_MBHC_RESULT_3, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL", + 0, 0, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN", + WCD934X_MBHC_CTL_BCS, 0x02, 1, 0), + WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS", + WCD934X_MBHC_STATUS_SPARE_1, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", + WCD934X_MBHC_NEW_CTL_2, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN", + WCD934X_HPH_L_TEST, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN", + WCD934X_HPH_R_TEST, 0x01, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_STATUS", + WCD934X_INTR_PIN1_STATUS0, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_STATUS", + WCD934X_INTR_PIN1_STATUS0, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_EN", + WCD934X_MBHC_NEW_CTL_1, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_COMPLETE", WCD934X_MBHC_NEW_FSM_STATUS, + 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_TIMEOUT", WCD934X_MBHC_NEW_FSM_STATUS, + 0x80, 7, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_RESULT", WCD934X_MBHC_NEW_ADC_RESULT, + 0xFF, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MICB2_VOUT", WCD934X_ANA_MICB2, 0x3F, 0, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ADC_MODE", + WCD934X_MBHC_NEW_CTL_1, 0x10, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_DETECTION_DONE", + WCD934X_MBHC_NEW_CTL_1, 0x04, 2, 0), + WCD_MBHC_REGISTER("WCD_MBHC_ELECT_ISRC_EN", + WCD934X_ANA_MBHC_ZDET, 0x02, 1, 0), +}; + +static const struct wcd_mbhc_intr intr_ids = { + .mbhc_sw_intr = WCD934X_IRQ_MBHC_SW_DET, + .mbhc_btn_press_intr = WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, + .mbhc_btn_release_intr = WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, + .mbhc_hs_ins_intr = WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, + .mbhc_hs_rem_intr = WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, + .hph_left_ocp = WCD934X_IRQ_HPH_PA_OCPL_FAULT, + .hph_right_ocp = WCD934X_IRQ_HPH_PA_OCPR_FAULT, +}; + + +static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = { + "cdc-vdd-mic-bias", +}; + +struct tavil_mbhc_zdet_param { + u16 ldo_ctl; + u16 noff; + u16 nshift; + u16 btn5; + u16 btn6; + u16 btn7; +}; + +static int tavil_mbhc_request_irq(struct snd_soc_codec *codec, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + return wcd9xxx_request_irq(core_res, irq, handler, name, data); +} + +static void tavil_mbhc_irq_control(struct snd_soc_codec *codec, + int irq, bool enable) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + if (enable) + wcd9xxx_enable_irq(core_res, irq); + else + wcd9xxx_disable_irq(core_res, irq); +} + +static int tavil_mbhc_free_irq(struct snd_soc_codec *codec, + int irq, void *data) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, irq, data); + return 0; +} + +static void tavil_mbhc_clk_setup(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, + 0x80, 0x80); + else + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, + 0x80, 0x00); +} + +static int tavil_mbhc_btn_to_num(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, WCD934X_ANA_MBHC_RESULT_3) & 0x7; +} + +static int tavil_enable_ext_mb_source(struct wcd_mbhc *mbhc, + bool turn_on) +{ + struct wcd934x_mbhc *wcd934x_mbhc; + struct snd_soc_codec *codec = mbhc->codec; + struct wcd934x_on_demand_supply *supply; + int ret = 0; + + wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc); + + supply = &wcd934x_mbhc->on_demand_list[WCD934X_ON_DEMAND_MICBIAS]; + if (!supply->supply) { + dev_dbg(codec->dev, "%s: warning supply not present ond for %s\n", + __func__, "onDemand Micbias"); + return ret; + } + + dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on, + supply->ondemand_supply_count); + + if (turn_on) { + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + supply->ondemand_supply_count++; + } else { + if (supply->ondemand_supply_count > 0) + supply->ondemand_supply_count--; + if (!(supply->ondemand_supply_count)) { + ret = snd_soc_dapm_disable_pin( + snd_soc_codec_get_dapm(codec), + "MICBIAS_REGULATOR"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + } + } + + if (ret) + dev_err(codec->dev, "%s: Failed to %s external micbias source\n", + __func__, turn_on ? "enable" : "disabled"); + else + dev_dbg(codec->dev, "%s: %s external micbias source\n", + __func__, turn_on ? "Enabled" : "Disabled"); + + return ret; +} + +static void tavil_mbhc_mbhc_bias_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_ELECT, + 0x01, 0x01); + else + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_ELECT, + 0x01, 0x00); +} + +static void tavil_mbhc_program_btn_thr(struct snd_soc_codec *codec, + s16 *btn_low, s16 *btn_high, + int num_btn, bool is_micbias) +{ + int i; + int vth; + + if (num_btn > WCD_MBHC_DEF_BUTTONS) { + dev_err(codec->dev, "%s: invalid number of buttons: %d\n", + __func__, num_btn); + return; + } + /* + * Tavil just needs one set of thresholds for button detection + * due to micbias voltage ramp to pullup upon button press. So + * btn_low and is_micbias are ignored and always program button + * thresholds using btn_high. + */ + for (i = 0; i < num_btn; i++) { + vth = ((btn_high[i] * 2) / 25) & 0x3F; + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN0 + i, + 0xFC, vth << 2); + dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n", + __func__, i, btn_high[i], vth); + } +} + +static bool tavil_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + bool ret = 0; + + if (lock) + ret = wcd9xxx_lock_sleep(core_res); + else + wcd9xxx_unlock_sleep(core_res); + + return ret; +} + +static int tavil_mbhc_register_notifier(struct wcd_mbhc *mbhc, + struct notifier_block *nblock, + bool enable) +{ + struct wcd934x_mbhc *wcd934x_mbhc; + + wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc); + + if (enable) + return blocking_notifier_chain_register(&wcd934x_mbhc->notifier, + nblock); + else + return blocking_notifier_chain_unregister( + &wcd934x_mbhc->notifier, nblock); +} + +static bool tavil_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num) +{ + u8 val; + + if (micb_num == MIC_BIAS_2) { + val = (snd_soc_read(mbhc->codec, WCD934X_ANA_MICB2) >> 6); + if (val == 0x01) + return true; + } + return false; +} + +static bool tavil_mbhc_hph_pa_on_status(struct snd_soc_codec *codec) +{ + return (snd_soc_read(codec, WCD934X_ANA_HPH) & 0xC0) ? true : false; +} + +static void tavil_mbhc_hph_l_pull_up_control( + struct snd_soc_codec *codec, + enum mbhc_hs_pullup_iref pull_up_cur) +{ + /* Default pull up current to 2uA */ + if (pull_up_cur < I_OFF || pull_up_cur > I_3P0_UA || + pull_up_cur == I_DEFAULT) + pull_up_cur = I_2P0_UA; + + dev_dbg(codec->dev, "%s: HS pull up current:%d\n", + __func__, pull_up_cur); + + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_PLUG_DETECT_CTL, + 0xC0, pull_up_cur << 6); +} + +static int tavil_mbhc_request_micbias(struct snd_soc_codec *codec, + int micb_num, int req) +{ + int ret; + + /* + * If micbias is requested, make sure that there + * is vote to enable mclk + */ + if (req == MICB_ENABLE) + tavil_cdc_mclk_enable(codec, true); + + ret = tavil_micbias_control(codec, micb_num, req, false); + + /* + * Release vote for mclk while requesting for + * micbias disable + */ + if (req == MICB_DISABLE) + tavil_cdc_mclk_enable(codec, false); + + return ret; +} + +static void tavil_mbhc_micb_ramp_control(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x1C, 0x0C); + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x80, 0x00); + snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP, + 0x1C, 0x00); + } +} + +static struct firmware_cal *tavil_get_hwdep_fw_cal(struct wcd_mbhc *mbhc, + enum wcd_cal_type type) +{ + struct wcd934x_mbhc *wcd934x_mbhc; + struct firmware_cal *hwdep_cal; + struct snd_soc_codec *codec = mbhc->codec; + + wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc); + + if (!codec) { + pr_err("%s: NULL codec pointer\n", __func__); + return NULL; + } + hwdep_cal = wcdcal_get_fw_cal(wcd934x_mbhc->fw_data, type); + if (!hwdep_cal) + dev_err(codec->dev, "%s: cal not sent by %d\n", + __func__, type); + + return hwdep_cal; +} + +static int tavil_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec, + int micb_num, bool req_en) +{ + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + int rc, micb_mv; + + if (micb_num != MIC_BIAS_2) + return -EINVAL; + + /* + * If device tree micbias level is already above the minimum + * voltage needed to detect threshold microphone, then do + * not change the micbias, just return. + */ + if (pdata->micbias.micb2_mv >= WCD_MBHC_THR_HS_MICB_MV) + return 0; + + micb_mv = req_en ? WCD_MBHC_THR_HS_MICB_MV : pdata->micbias.micb2_mv; + + rc = tavil_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_2); + + return rc; +} + +static inline void tavil_mbhc_get_result_params(struct wcd9xxx *wcd9xxx, + s16 *d1_a, u16 noff, + int32_t *zdet) +{ + int i; + int val, val1; + s16 c1; + s32 x1, d1; + int32_t denom; + int minCode_param[] = { + 3277, 1639, 820, 410, 205, 103, 52, 26 + }; + + regmap_update_bits(wcd9xxx->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x20); + for (i = 0; i < TAVIL_ZDET_NUM_MEASUREMENTS; i++) { + regmap_read(wcd9xxx->regmap, WCD934X_ANA_MBHC_RESULT_2, &val); + if (val & 0x80) + break; + } + val = val << 0x8; + regmap_read(wcd9xxx->regmap, WCD934X_ANA_MBHC_RESULT_1, &val1); + val |= val1; + regmap_update_bits(wcd9xxx->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x00); + x1 = TAVIL_MBHC_GET_X1(val); + c1 = TAVIL_MBHC_GET_C1(val); + /* If ramp is not complete, give additional 5ms */ + if ((c1 < 2) && x1) + usleep_range(5000, 5050); + + if (!c1 || !x1) { + dev_dbg(wcd9xxx->dev, + "%s: Impedance detect ramp error, c1=%d, x1=0x%x\n", + __func__, c1, x1); + goto ramp_down; + } + d1 = d1_a[c1]; + denom = (x1 * d1) - (1 << (14 - noff)); + if (denom > 0) + *zdet = (TAVIL_MBHC_ZDET_CONST * 1000) / denom; + else if (x1 < minCode_param[noff]) + *zdet = TAVIL_ZDET_FLOATING_IMPEDANCE; + + dev_dbg(wcd9xxx->dev, "%s: d1=%d, c1=%d, x1=0x%x, z_val=%d(milliOhm)\n", + __func__, d1, c1, x1, *zdet); +ramp_down: + i = 0; + while (x1) { + regmap_bulk_read(wcd9xxx->regmap, + WCD934X_ANA_MBHC_RESULT_1, (u8 *)&val, 2); + x1 = TAVIL_MBHC_GET_X1(val); + i++; + if (i == TAVIL_ZDET_NUM_MEASUREMENTS) + break; + } +} + +static void tavil_mbhc_zdet_ramp(struct snd_soc_codec *codec, + struct tavil_mbhc_zdet_param *zdet_param, + int32_t *zl, int32_t *zr, s16 *d1_a) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int32_t zdet = 0; + + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x70, + zdet_param->ldo_ctl << 4); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN5, 0xFC, + zdet_param->btn5); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN6, 0xFC, + zdet_param->btn6); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN7, 0xFC, + zdet_param->btn7); + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x0F, + zdet_param->noff); + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_RAMP_CTL, 0x0F, + zdet_param->nshift); + + if (!zl) + goto z_right; + /* Start impedance measurement for HPH_L */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x80, 0x80); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_L, noff = %d\n", + __func__, zdet_param->noff); + tavil_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x80, 0x00); + + *zl = zdet; + +z_right: + if (!zr) + return; + /* Start impedance measurement for HPH_R */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x40, 0x40); + dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_R, noff = %d\n", + __func__, zdet_param->noff); + tavil_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet); + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ZDET, 0x40, 0x00); + + *zr = zdet; +} + +static inline void tavil_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec, + int32_t *z_val, int flag_l_r) +{ + s16 q1; + int q1_cal; + + if (*z_val < (TAVIL_ZDET_VAL_400/1000)) + q1 = snd_soc_read(codec, + WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r)); + else + q1 = snd_soc_read(codec, + WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (2 * flag_l_r)); + if (q1 & 0x80) + q1_cal = (10000 - ((q1 & 0x7F) * 25)); + else + q1_cal = (10000 + (q1 * 25)); + if (q1_cal > 0) + *z_val = ((*z_val) * 10000) / q1_cal; +} + +static void tavil_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, + uint32_t *zr) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + s16 reg0, reg1, reg2, reg3, reg4; + int32_t z1L, z1R, z1Ls; + int zMono, z_diff1, z_diff2; + bool is_fsm_disable = false; + struct tavil_mbhc_zdet_param zdet_param[] = { + {4, 0, 4, 0x08, 0x14, 0x18}, /* < 32ohm */ + {2, 0, 3, 0x18, 0x7C, 0x90}, /* 32ohm < Z < 400ohm */ + {1, 4, 5, 0x18, 0x7C, 0x90}, /* 400ohm < Z < 1200ohm */ + {1, 6, 7, 0x18, 0x7C, 0x90}, /* >1200ohm */ + }; + struct tavil_mbhc_zdet_param *zdet_param_ptr = NULL; + s16 d1_a[][4] = { + {0, 30, 90, 30}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + {0, 30, 30, 5}, + }; + s16 *d1 = NULL; + + WCD_MBHC_RSC_ASSERT_LOCKED(mbhc); + + reg0 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN5); + reg1 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN6); + reg2 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN7); + reg3 = snd_soc_read(codec, WCD934X_MBHC_CTL_CLK); + reg4 = snd_soc_read(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL); + + if (snd_soc_read(codec, WCD934X_ANA_MBHC_ELECT) & 0x80) { + is_fsm_disable = true; + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ELECT, 0x80, 0x00); + } + + /* For NO-jack, disable L_DET_EN before Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x80, 0x00); + + /* Turn off 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x01, 0x00); + + /* First get impedance on Left */ + d1 = d1_a[1]; + zdet_param_ptr = &zdet_param[1]; + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + + if (!TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z1L)) + goto left_ch_impedance; + + /* Second ramp for left ch */ + if (z1L < TAVIL_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1L > TAVIL_ZDET_VAL_400) && (z1L <= TAVIL_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1L > TAVIL_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1); + +left_ch_impedance: + if ((z1L == TAVIL_ZDET_FLOATING_IMPEDANCE) || + (z1L > TAVIL_ZDET_VAL_100K)) { + *zl = TAVIL_ZDET_FLOATING_IMPEDANCE; + zdet_param_ptr = &zdet_param[1]; + d1 = d1_a[1]; + } else { + *zl = z1L/1000; + tavil_wcd_mbhc_qfuse_cal(codec, zl, 0); + } + dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n", + __func__, *zl); + + /* Start of right impedance ramp and calculation */ + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + if (TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) { + if (((z1R > TAVIL_ZDET_VAL_1200) && + (zdet_param_ptr->noff == 0x6)) || + ((*zl) != TAVIL_ZDET_FLOATING_IMPEDANCE)) + goto right_ch_impedance; + /* Second ramp for right ch */ + if (z1R < TAVIL_ZDET_VAL_32) { + zdet_param_ptr = &zdet_param[0]; + d1 = d1_a[0]; + } else if ((z1R > TAVIL_ZDET_VAL_400) && + (z1R <= TAVIL_ZDET_VAL_1200)) { + zdet_param_ptr = &zdet_param[2]; + d1 = d1_a[2]; + } else if (z1R > TAVIL_ZDET_VAL_1200) { + zdet_param_ptr = &zdet_param[3]; + d1 = d1_a[3]; + } + tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1); + } +right_ch_impedance: + if ((z1R == TAVIL_ZDET_FLOATING_IMPEDANCE) || + (z1R > TAVIL_ZDET_VAL_100K)) { + *zr = TAVIL_ZDET_FLOATING_IMPEDANCE; + } else { + *zr = z1R/1000; + tavil_wcd_mbhc_qfuse_cal(codec, zr, 1); + } + dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n", + __func__, *zr); + + /* Mono/stereo detection */ + if ((*zl == TAVIL_ZDET_FLOATING_IMPEDANCE) && + (*zr == TAVIL_ZDET_FLOATING_IMPEDANCE)) { + dev_dbg(codec->dev, + "%s: plug type is invalid or extension cable\n", + __func__); + goto zdet_complete; + } + if ((*zl == TAVIL_ZDET_FLOATING_IMPEDANCE) || + (*zr == TAVIL_ZDET_FLOATING_IMPEDANCE) || + ((*zl < WCD_MONO_HS_MIN_THR) && (*zr > WCD_MONO_HS_MIN_THR)) || + ((*zl > WCD_MONO_HS_MIN_THR) && (*zr < WCD_MONO_HS_MIN_THR))) { + dev_dbg(codec->dev, + "%s: Mono plug type with one ch floating or shorted to GND\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + goto zdet_complete; + } + snd_soc_update_bits(codec, WCD934X_HPH_R_ATEST, 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, 0x40, 0x01); + if (*zl < (TAVIL_ZDET_VAL_32/1000)) + tavil_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1); + else + tavil_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, 0x40, 0x00); + snd_soc_update_bits(codec, WCD934X_HPH_R_ATEST, 0x02, 0x00); + z1Ls /= 1000; + tavil_wcd_mbhc_qfuse_cal(codec, &z1Ls, 0); + /* Parallel of left Z and 9 ohm pull down resistor */ + zMono = ((*zl) * 9) / ((*zl) + 9); + z_diff1 = (z1Ls > zMono) ? (z1Ls - zMono) : (zMono - z1Ls); + z_diff2 = ((*zl) > z1Ls) ? ((*zl) - z1Ls) : (z1Ls - (*zl)); + if ((z_diff1 * (*zl + z1Ls)) > (z_diff2 * (z1Ls + zMono))) { + dev_dbg(codec->dev, "%s: stereo plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_STEREO; + } else { + dev_dbg(codec->dev, "%s: MONO plug type detected\n", + __func__); + mbhc->hph_type = WCD_MBHC_HPH_MONO; + } + +zdet_complete: + snd_soc_write(codec, WCD934X_ANA_MBHC_BTN5, reg0); + snd_soc_write(codec, WCD934X_ANA_MBHC_BTN6, reg1); + snd_soc_write(codec, WCD934X_ANA_MBHC_BTN7, reg2); + /* Turn on 100k pull down on HPHL */ + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x01, 0x01); + + /* For NO-jack, re-enable L_DET_EN after Z-det measurements */ + if (mbhc->hphl_swh) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_MECH, 0x80, 0x80); + + snd_soc_write(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, reg4); + snd_soc_write(codec, WCD934X_MBHC_CTL_CLK, reg3); + if (is_fsm_disable) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_ANA_MBHC_ELECT, 0x80, 0x80); +} + +static void tavil_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x02, 0x02); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x40, 0x40); + } else { + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH, + 0x02, 0x00); + } +} + +static void tavil_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x40, 0x40); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x10, 0x10); + } else { + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x40, 0x00); + snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, + 0x10, 0x00); + } +} +static void tavil_mbhc_moisture_config(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + + if ((mbhc->moist_rref == R_OFF) || + (mbhc->mbhc_cfg->enable_usbc_analog)) { + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_2, + 0x0C, R_OFF << 2); + return; + } + + /* Donot enable moisture detection if jack type is NC */ + if (!mbhc->hphl_swh) { + dev_dbg(codec->dev, "%s: disable moisture detection for NC\n", + __func__); + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_2, + 0x0C, R_OFF << 2); + return; + } + + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_2, + 0x0C, mbhc->moist_rref << 2); +} + +static bool tavil_hph_register_recovery(struct wcd_mbhc *mbhc) +{ + struct snd_soc_codec *codec = mbhc->codec; + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) + return false; + + wcd934x_mbhc->is_hph_recover = false; + snd_soc_dapm_force_enable_pin(snd_soc_codec_get_dapm(codec), + "RESET_HPH_REGISTERS"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + + snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec), + "RESET_HPH_REGISTERS"); + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + + return wcd934x_mbhc->is_hph_recover; +} + +static const struct wcd_mbhc_cb mbhc_cb = { + .request_irq = tavil_mbhc_request_irq, + .irq_control = tavil_mbhc_irq_control, + .free_irq = tavil_mbhc_free_irq, + .clk_setup = tavil_mbhc_clk_setup, + .map_btn_code_to_num = tavil_mbhc_btn_to_num, + .enable_mb_source = tavil_enable_ext_mb_source, + .mbhc_bias = tavil_mbhc_mbhc_bias_control, + .set_btn_thr = tavil_mbhc_program_btn_thr, + .lock_sleep = tavil_mbhc_lock_sleep, + .register_notifier = tavil_mbhc_register_notifier, + .micbias_enable_status = tavil_mbhc_micb_en_status, + .hph_pa_on_status = tavil_mbhc_hph_pa_on_status, + .hph_pull_up_control = tavil_mbhc_hph_l_pull_up_control, + .mbhc_micbias_control = tavil_mbhc_request_micbias, + .mbhc_micb_ramp_control = tavil_mbhc_micb_ramp_control, + .get_hwdep_fw_cal = tavil_get_hwdep_fw_cal, + .mbhc_micb_ctrl_thr_mic = tavil_mbhc_micb_ctrl_threshold_mic, + .compute_impedance = tavil_wcd_mbhc_calc_impedance, + .mbhc_gnd_det_ctrl = tavil_mbhc_gnd_det_ctrl, + .hph_pull_down_ctrl = tavil_mbhc_hph_pull_down_ctrl, + .mbhc_moisture_config = tavil_mbhc_moisture_config, + .hph_register_recovery = tavil_hph_register_recovery, +}; + +static struct regulator *tavil_codec_find_ondemand_regulator( + struct snd_soc_codec *codec, const char *name) +{ + int i; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + + for (i = 0; i < wcd9xxx->num_of_supplies; ++i) { + if (pdata->regulator[i].ondemand && + wcd9xxx->supplies[i].supply && + !strcmp(wcd9xxx->supplies[i].supply, name)) + return wcd9xxx->supplies[i].consumer; + } + + dev_dbg(codec->dev, "Warning: regulator not found:%s\n", + name); + return NULL; +} + +static int tavil_get_hph_type(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + struct wcd_mbhc *mbhc; + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + mbhc = &wcd934x_mbhc->wcd_mbhc; + + ucontrol->value.integer.value[0] = (u32) mbhc->hph_type; + dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type); + + return 0; +} + +static int tavil_hph_impedance_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t zl, zr; + bool hphr; + struct soc_multi_mixer_control *mc; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + mc = (struct soc_multi_mixer_control *)(kcontrol->private_value); + hphr = mc->shift; + wcd_mbhc_get_impedance(&wcd934x_mbhc->wcd_mbhc, &zl, &zr); + dev_dbg(codec->dev, "%s: zl=%u(ohms), zr=%u(ohms)\n", __func__, zl, zr); + ucontrol->value.integer.value[0] = hphr ? zr : zl; + + return 0; +} + +static const struct snd_kcontrol_new hph_type_detect_controls[] = { + SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0, + tavil_get_hph_type, NULL), +}; + +static const struct snd_kcontrol_new impedance_detect_controls[] = { + SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0, + tavil_hph_impedance_get, NULL), + SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0, + tavil_hph_impedance_get, NULL), +}; + +/* + * tavil_mbhc_get_impedance: get impedance of headphone left and right channels + * @wcd934x_mbhc: handle to struct wcd934x_mbhc * + * @zl: handle to left-ch impedance + * @zr: handle to right-ch impedance + * return 0 for success or error code in case of failure + */ +int tavil_mbhc_get_impedance(struct wcd934x_mbhc *wcd934x_mbhc, + uint32_t *zl, uint32_t *zr) +{ + if (!wcd934x_mbhc) { + pr_err("%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + if (!zl || !zr) { + pr_err("%s: zl or zr null!\n", __func__); + return -EINVAL; + } + + return wcd_mbhc_get_impedance(&wcd934x_mbhc->wcd_mbhc, zl, zr); +} +EXPORT_SYMBOL(tavil_mbhc_get_impedance); + +/* + * tavil_mbhc_hs_detect: starts mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + * @mbhc_cfg: handle to mbhc configuration structure + * return 0 if mbhc_start is success or error code in case of failure + */ +int tavil_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return -EINVAL; + } + + return wcd_mbhc_start(&wcd934x_mbhc->wcd_mbhc, mbhc_cfg); +} +EXPORT_SYMBOL(tavil_mbhc_hs_detect); + +/* + * tavil_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality + * @codec: handle to snd_soc_codec * + */ +void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (!wcd934x_mbhc) { + dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__); + return; + } + wcd_mbhc_stop(&wcd934x_mbhc->wcd_mbhc); +} +EXPORT_SYMBOL(tavil_mbhc_hs_detect_exit); + +/* + * tavil_mbhc_post_ssr_init: initialize mbhc for tavil post subsystem restart + * @mbhc: poniter to wcd934x_mbhc structure + * @codec: handle to snd_soc_codec * + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, + struct snd_soc_codec *codec) +{ + int ret; + + if (!mbhc || !codec) + return -EINVAL; + + wcd_mbhc_deinit(&mbhc->wcd_mbhc); + ret = wcd_mbhc_init(&mbhc->wcd_mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto done; + } + if (!WCD_MBHC_DETECTION) { + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01); + } + +done: + return ret; +} +EXPORT_SYMBOL(tavil_mbhc_post_ssr_init); + +/* + * tavil_mbhc_init: initialize mbhc for tavil + * @mbhc: poniter to wcd934x_mbhc struct pointer to store the configs + * @codec: handle to snd_soc_codec * + * @fw_data: handle to firmware data + * + * return 0 if mbhc_init is success or error code in case of failure + */ +int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, struct snd_soc_codec *codec, + struct fw_info *fw_data) +{ + struct regulator *supply; + struct wcd934x_mbhc *wcd934x_mbhc; + int ret; + + wcd934x_mbhc = devm_kzalloc(codec->dev, sizeof(struct wcd934x_mbhc), + GFP_KERNEL); + if (!wcd934x_mbhc) + return -ENOMEM; + + wcd934x_mbhc->wcd9xxx = dev_get_drvdata(codec->dev->parent); + wcd934x_mbhc->fw_data = fw_data; + BLOCKING_INIT_NOTIFIER_HEAD(&wcd934x_mbhc->notifier); + + ret = wcd_mbhc_init(&wcd934x_mbhc->wcd_mbhc, codec, &mbhc_cb, + &intr_ids, wcd_mbhc_registers, + TAVIL_ZDET_SUPPORTED); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto err; + } + + supply = tavil_codec_find_ondemand_regulator(codec, + on_demand_supply_name[WCD934X_ON_DEMAND_MICBIAS]); + if (supply) { + wcd934x_mbhc->on_demand_list[ + WCD934X_ON_DEMAND_MICBIAS].supply = + supply; + wcd934x_mbhc->on_demand_list[ + WCD934X_ON_DEMAND_MICBIAS].ondemand_supply_count = + 0; + } + + (*mbhc) = wcd934x_mbhc; + snd_soc_add_codec_controls(codec, impedance_detect_controls, + ARRAY_SIZE(impedance_detect_controls)); + snd_soc_add_codec_controls(codec, hph_type_detect_controls, + ARRAY_SIZE(hph_type_detect_controls)); + + if (!WCD_MBHC_DETECTION) { + snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01); + } + + return 0; +err: + devm_kfree(codec->dev, wcd934x_mbhc); + return ret; +} +EXPORT_SYMBOL(tavil_mbhc_init); + +/* + * tavil_mbhc_deinit: deinitialize mbhc for tavil + * @codec: handle to snd_soc_codec * + */ +void tavil_mbhc_deinit(struct snd_soc_codec *codec) +{ + struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec); + + if (wcd934x_mbhc) { + wcd_mbhc_deinit(&wcd934x_mbhc->wcd_mbhc); + devm_kfree(codec->dev, wcd934x_mbhc); + } +} +EXPORT_SYMBOL(tavil_mbhc_deinit); diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.h b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h new file mode 100644 index 000000000000..53c886da0f6b --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h @@ -0,0 +1,84 @@ +/* + * 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 __WCD934X_MBHC_H__ +#define __WCD934X_MBHC_H__ +#include "../wcd-mbhc-v2.h" + +enum wcd934x_on_demand_supply_name { + WCD934X_ON_DEMAND_MICBIAS = 0, + WCD934X_ON_DEMAND_SUPPLIES_MAX, +}; + +struct wcd934x_on_demand_supply { + struct regulator *supply; + int ondemand_supply_count; +}; + +struct wcd934x_mbhc { + struct wcd_mbhc wcd_mbhc; + struct blocking_notifier_head notifier; + struct wcd934x_on_demand_supply on_demand_list[ + WCD934X_ON_DEMAND_SUPPLIES_MAX]; + struct wcd9xxx *wcd9xxx; + struct fw_info *fw_data; + bool mbhc_started; + bool is_hph_recover; +}; + +#if IS_ENABLED(CONFIG_SND_SOC_WCD934X_MBHC) +extern int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, + struct snd_soc_codec *codec, + struct fw_info *fw_data); +extern void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec); +extern int tavil_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg); +extern void tavil_mbhc_deinit(struct snd_soc_codec *codec); +extern int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, + struct snd_soc_codec *codec); +extern int tavil_mbhc_get_impedance(struct wcd934x_mbhc *wcd934x_mbhc, + uint32_t *zl, uint32_t *zr); +#else +static inline int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, + struct snd_soc_codec *codec, + struct fw_info *fw_data) +{ + return 0; +} +static inline void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec) +{ +} +static inline int tavil_mbhc_hs_detect(struct snd_soc_codec *codec, + struct wcd_mbhc_config *mbhc_cfg) +{ + return 0; +} +static inline void tavil_mbhc_deinit(struct snd_soc_codec *codec) +{ +} +static inline int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, + struct snd_soc_codec *codec) +{ + return 0; +} +static inline int tavil_mbhc_get_impedance(struct wcd934x_mbhc *wcd934x_mbhc, + uint32_t *zl, uint32_t *zr) +{ + if (zl) + *zl = 0; + if (zr) + *zr = 0; + return -EINVAL; +} +#endif + +#endif /* __WCD934X_MBHC_H__ */ diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h new file mode 100644 index 000000000000..afd93b2cf56d --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h @@ -0,0 +1,1171 @@ +/* + * 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 WCD934X_ROUTING_H +#define WCD934X_ROUTING_H + +#include + +const struct snd_soc_dapm_route tavil_slim_audio_map[] = { + + /* Virtual input widgets */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, + + /* Virtual input widget Mixer */ + {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF1_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF1_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF2_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF2_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF2_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF3_CAP Mixer", "SLIM TX0", "SLIM TX0"}, + {"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1"}, + {"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2"}, + {"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3"}, + {"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4"}, + {"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5"}, + {"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6"}, + {"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7"}, + {"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8"}, + {"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9"}, + {"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10"}, + {"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11"}, + {"AIF3_CAP Mixer", "SLIM TX13", "SLIM TX13"}, + + {"AIF4_MAD Mixer", "SLIM TX13", "SLIM TX13"}, + + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"}, + + {"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"}, + {"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"}, + + {"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"}, + {"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"}, + + {"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"}, + {"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"}, + + {"SLIM RX0", NULL, "SLIM RX0 MUX"}, + {"SLIM RX1", NULL, "SLIM RX1 MUX"}, + {"SLIM RX2", NULL, "SLIM RX2 MUX"}, + {"SLIM RX3", NULL, "SLIM RX3 MUX"}, + {"SLIM RX4", NULL, "SLIM RX4 MUX"}, + {"SLIM RX5", NULL, "SLIM RX5 MUX"}, + {"SLIM RX6", NULL, "SLIM RX6 MUX"}, + {"SLIM RX7", NULL, "SLIM RX7 MUX"}, + +}; + +const struct snd_soc_dapm_route tavil_audio_map[] = { + + /* MAD */ + {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"}, + {"MAD_SEL MUX", "MSM", "MADINPUT"}, + + {"MAD_INP MUX", "MAD", "MAD_SEL MUX"}, + {"MAD_INP MUX", "DEC1", "ADC MUX1"}, + + {"MAD_BROADCAST", "Switch", "MAD_INP MUX"}, + {"MAD_CPE1", "Switch", "MAD_INP MUX"}, + {"MAD_CPE2", "Switch", "MAD_INP MUX"}, + + {"MAD_CPE_OUT1", NULL, "MAD_CPE1"}, + {"MAD_CPE_OUT2", NULL, "MAD_CPE2"}, + + /* VI Feedback */ + {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"}, + {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"}, + {"AIF4 VI", NULL, "AIF4_VI Mixer"}, + + /* CDC Tx interface with SLIMBUS */ + {"SLIM TX0", NULL, "CDC_IF TX0 MUX"}, + {"SLIM TX1", NULL, "CDC_IF TX1 MUX"}, + {"SLIM TX2", NULL, "CDC_IF TX2 MUX"}, + {"SLIM TX3", NULL, "CDC_IF TX3 MUX"}, + {"SLIM TX4", NULL, "CDC_IF TX4 MUX"}, + {"SLIM TX5", NULL, "CDC_IF TX5 MUX"}, + {"SLIM TX6", NULL, "CDC_IF TX6 MUX"}, + {"SLIM TX7", NULL, "CDC_IF TX7 MUX"}, + {"SLIM TX8", NULL, "CDC_IF TX8 MUX"}, + {"SLIM TX9", NULL, "CDC_IF TX9 MUX"}, + {"SLIM TX10", NULL, "CDC_IF TX10 MUX"}, + {"SLIM TX11", NULL, "CDC_IF TX11 MUX"}, + {"SLIM TX13", NULL, "CDC_IF TX13 MUX"}, + + {"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"}, + {"CDC_IF TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, + {"CDC_IF TX0 MUX", "DEC0_192", "ADC US MUX0"}, + + {"CDC_IF TX1 MUX", "DEC1", "ADC MUX1"}, + {"CDC_IF TX1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"}, + {"CDC_IF TX1 MUX", "DEC1_192", "ADC US MUX1"}, + + {"CDC_IF TX2 MUX", "DEC2", "ADC MUX2"}, + {"CDC_IF TX2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"}, + {"CDC_IF TX2 MUX", "DEC2_192", "ADC US MUX2"}, + + {"CDC_IF TX3 MUX", "DEC3", "ADC MUX3"}, + {"CDC_IF TX3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"}, + {"CDC_IF TX3 MUX", "DEC3_192", "ADC US MUX3"}, + + {"CDC_IF TX4 MUX", "DEC4", "ADC MUX4"}, + {"CDC_IF TX4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"}, + {"CDC_IF TX4 MUX", "DEC4_192", "ADC US MUX4"}, + + {"CDC_IF TX5 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + {"CDC_IF TX5 MUX", "DEC5_192", "ADC US MUX5"}, + + {"CDC_IF TX6 MUX", "DEC6", "ADC MUX6"}, + {"CDC_IF TX6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"}, + {"CDC_IF TX6 MUX", "DEC6_192", "ADC US MUX6"}, + + {"CDC_IF TX7 MUX", "DEC7", "ADC MUX7"}, + {"CDC_IF TX7 MUX", "RX_MIX_TX7", "RX MIX TX7 MUX"}, + {"CDC_IF TX7 MUX", "DEC7_192", "ADC US MUX7"}, + + {"CDC_IF TX8 MUX", "DEC8", "ADC MUX8"}, + {"CDC_IF TX8 MUX", "RX_MIX_TX8", "RX MIX TX8 MUX"}, + {"CDC_IF TX8 MUX", "DEC8_192", "ADC US MUX8"}, + + {"CDC_IF TX9 MUX", "DEC7", "ADC MUX7"}, + {"CDC_IF TX9 MUX", "DEC7_192", "ADC US MUX7"}, + {"CDC_IF TX10 MUX", "DEC6", "ADC MUX6"}, + {"CDC_IF TX10 MUX", "DEC6_192", "ADC US MUX6"}, + + {"CDC_IF TX11 MUX", "DEC_0_5", "CDC_IF TX11 INP1 MUX"}, + {"CDC_IF TX11 MUX", "DEC_9_12", "CDC_IF TX11 INP1 MUX"}, + {"CDC_IF TX11 INP1 MUX", "DEC0", "ADC MUX0"}, + {"CDC_IF TX11 INP1 MUX", "DEC1", "ADC MUX1"}, + {"CDC_IF TX11 INP1 MUX", "DEC2", "ADC MUX2"}, + {"CDC_IF TX11 INP1 MUX", "DEC3", "ADC MUX3"}, + {"CDC_IF TX11 INP1 MUX", "DEC4", "ADC MUX4"}, + {"CDC_IF TX11 INP1 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX11 INP1 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + + {"CDC_IF TX13 MUX", "MAD_BRDCST", "MAD_BROADCAST"}, + {"CDC_IF TX13 MUX", "CDC_DEC_5", "CDC_IF TX13 INP1 MUX"}, + {"CDC_IF TX13 INP1 MUX", "DEC5", "ADC MUX5"}, + {"CDC_IF TX13 INP1 MUX", "DEC5_192", "ADC US MUX5"}, + + {"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX1", "RX INT1 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX2", "RX INT2 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX3", "RX INT3 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX4", "RX INT4 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"}, + {"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"}, + + {"ADC US MUX0", "US_Switch", "ADC MUX0"}, + {"ADC US MUX1", "US_Switch", "ADC MUX1"}, + {"ADC US MUX2", "US_Switch", "ADC MUX2"}, + {"ADC US MUX3", "US_Switch", "ADC MUX3"}, + {"ADC US MUX4", "US_Switch", "ADC MUX4"}, + {"ADC US MUX5", "US_Switch", "ADC MUX5"}, + {"ADC US MUX6", "US_Switch", "ADC MUX6"}, + {"ADC US MUX7", "US_Switch", "ADC MUX7"}, + {"ADC US MUX8", "US_Switch", "ADC MUX8"}, + + {"ADC MUX0", "DMIC", "DMIC MUX0"}, + {"ADC MUX0", "AMIC", "AMIC MUX0"}, + {"ADC MUX1", "DMIC", "DMIC MUX1"}, + {"ADC MUX1", "AMIC", "AMIC MUX1"}, + {"ADC MUX2", "DMIC", "DMIC MUX2"}, + {"ADC MUX2", "AMIC", "AMIC MUX2"}, + {"ADC MUX3", "DMIC", "DMIC MUX3"}, + {"ADC MUX3", "AMIC", "AMIC MUX3"}, + {"ADC MUX4", "DMIC", "DMIC MUX4"}, + {"ADC MUX4", "AMIC", "AMIC MUX4"}, + {"ADC MUX5", "DMIC", "DMIC MUX5"}, + {"ADC MUX5", "AMIC", "AMIC MUX5"}, + {"ADC MUX6", "DMIC", "DMIC MUX6"}, + {"ADC MUX6", "AMIC", "AMIC MUX6"}, + {"ADC MUX7", "DMIC", "DMIC MUX7"}, + {"ADC MUX7", "AMIC", "AMIC MUX7"}, + {"ADC MUX8", "DMIC", "DMIC MUX8"}, + {"ADC MUX8", "AMIC", "AMIC MUX8"}, + {"ADC MUX10", "DMIC", "DMIC MUX10"}, + {"ADC MUX10", "AMIC", "AMIC MUX10"}, + {"ADC MUX11", "DMIC", "DMIC MUX11"}, + {"ADC MUX11", "AMIC", "AMIC MUX11"}, + {"ADC MUX12", "DMIC", "DMIC MUX12"}, + {"ADC MUX12", "AMIC", "AMIC MUX12"}, + {"ADC MUX13", "DMIC", "DMIC MUX13"}, + {"ADC MUX13", "AMIC", "AMIC MUX13"}, + + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"}, + {"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"}, + {"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"}, + + {"DMIC MUX0", "DMIC0", "DMIC0"}, + {"DMIC MUX0", "DMIC1", "DMIC1"}, + {"DMIC MUX0", "DMIC2", "DMIC2"}, + {"DMIC MUX0", "DMIC3", "DMIC3"}, + {"DMIC MUX0", "DMIC4", "DMIC4"}, + {"DMIC MUX0", "DMIC5", "DMIC5"}, + {"AMIC MUX0", "ADC1", "ADC1"}, + {"AMIC MUX0", "ADC2", "ADC2"}, + {"AMIC MUX0", "ADC3", "ADC3"}, + {"AMIC MUX0", "ADC4", "ADC4"}, + + {"DMIC MUX1", "DMIC0", "DMIC0"}, + {"DMIC MUX1", "DMIC1", "DMIC1"}, + {"DMIC MUX1", "DMIC2", "DMIC2"}, + {"DMIC MUX1", "DMIC3", "DMIC3"}, + {"DMIC MUX1", "DMIC4", "DMIC4"}, + {"DMIC MUX1", "DMIC5", "DMIC5"}, + {"AMIC MUX1", "ADC1", "ADC1"}, + {"AMIC MUX1", "ADC2", "ADC2"}, + {"AMIC MUX1", "ADC3", "ADC3"}, + {"AMIC MUX1", "ADC4", "ADC4"}, + + {"DMIC MUX2", "DMIC0", "DMIC0"}, + {"DMIC MUX2", "DMIC1", "DMIC1"}, + {"DMIC MUX2", "DMIC2", "DMIC2"}, + {"DMIC MUX2", "DMIC3", "DMIC3"}, + {"DMIC MUX2", "DMIC4", "DMIC4"}, + {"DMIC MUX2", "DMIC5", "DMIC5"}, + {"AMIC MUX2", "ADC1", "ADC1"}, + {"AMIC MUX2", "ADC2", "ADC2"}, + {"AMIC MUX2", "ADC3", "ADC3"}, + {"AMIC MUX2", "ADC4", "ADC4"}, + + {"DMIC MUX3", "DMIC0", "DMIC0"}, + {"DMIC MUX3", "DMIC1", "DMIC1"}, + {"DMIC MUX3", "DMIC2", "DMIC2"}, + {"DMIC MUX3", "DMIC3", "DMIC3"}, + {"DMIC MUX3", "DMIC4", "DMIC4"}, + {"DMIC MUX3", "DMIC5", "DMIC5"}, + {"AMIC MUX3", "ADC1", "ADC1"}, + {"AMIC MUX3", "ADC2", "ADC2"}, + {"AMIC MUX3", "ADC3", "ADC3"}, + {"AMIC MUX3", "ADC4", "ADC4"}, + + {"DMIC MUX4", "DMIC0", "DMIC0"}, + {"DMIC MUX4", "DMIC1", "DMIC1"}, + {"DMIC MUX4", "DMIC2", "DMIC2"}, + {"DMIC MUX4", "DMIC3", "DMIC3"}, + {"DMIC MUX4", "DMIC4", "DMIC4"}, + {"DMIC MUX4", "DMIC5", "DMIC5"}, + {"AMIC MUX4", "ADC1", "ADC1"}, + {"AMIC MUX4", "ADC2", "ADC2"}, + {"AMIC MUX4", "ADC3", "ADC3"}, + {"AMIC MUX4", "ADC4", "ADC4"}, + + {"DMIC MUX5", "DMIC0", "DMIC0"}, + {"DMIC MUX5", "DMIC1", "DMIC1"}, + {"DMIC MUX5", "DMIC2", "DMIC2"}, + {"DMIC MUX5", "DMIC3", "DMIC3"}, + {"DMIC MUX5", "DMIC4", "DMIC4"}, + {"DMIC MUX5", "DMIC5", "DMIC5"}, + {"AMIC MUX5", "ADC1", "ADC1"}, + {"AMIC MUX5", "ADC2", "ADC2"}, + {"AMIC MUX5", "ADC3", "ADC3"}, + {"AMIC MUX5", "ADC4", "ADC4"}, + + {"DMIC MUX6", "DMIC0", "DMIC0"}, + {"DMIC MUX6", "DMIC1", "DMIC1"}, + {"DMIC MUX6", "DMIC2", "DMIC2"}, + {"DMIC MUX6", "DMIC3", "DMIC3"}, + {"DMIC MUX6", "DMIC4", "DMIC4"}, + {"DMIC MUX6", "DMIC5", "DMIC5"}, + {"AMIC MUX6", "ADC1", "ADC1"}, + {"AMIC MUX6", "ADC2", "ADC2"}, + {"AMIC MUX6", "ADC3", "ADC3"}, + {"AMIC MUX6", "ADC4", "ADC4"}, + + {"DMIC MUX7", "DMIC0", "DMIC0"}, + {"DMIC MUX7", "DMIC1", "DMIC1"}, + {"DMIC MUX7", "DMIC2", "DMIC2"}, + {"DMIC MUX7", "DMIC3", "DMIC3"}, + {"DMIC MUX7", "DMIC4", "DMIC4"}, + {"DMIC MUX7", "DMIC5", "DMIC5"}, + {"AMIC MUX7", "ADC1", "ADC1"}, + {"AMIC MUX7", "ADC2", "ADC2"}, + {"AMIC MUX7", "ADC3", "ADC3"}, + {"AMIC MUX7", "ADC4", "ADC4"}, + + {"DMIC MUX8", "DMIC0", "DMIC0"}, + {"DMIC MUX8", "DMIC1", "DMIC1"}, + {"DMIC MUX8", "DMIC2", "DMIC2"}, + {"DMIC MUX8", "DMIC3", "DMIC3"}, + {"DMIC MUX8", "DMIC4", "DMIC4"}, + {"DMIC MUX8", "DMIC5", "DMIC5"}, + {"AMIC MUX8", "ADC1", "ADC1"}, + {"AMIC MUX8", "ADC2", "ADC2"}, + {"AMIC MUX8", "ADC3", "ADC3"}, + {"AMIC MUX8", "ADC4", "ADC4"}, + + {"DMIC MUX10", "DMIC0", "DMIC0"}, + {"DMIC MUX10", "DMIC1", "DMIC1"}, + {"DMIC MUX10", "DMIC2", "DMIC2"}, + {"DMIC MUX10", "DMIC3", "DMIC3"}, + {"DMIC MUX10", "DMIC4", "DMIC4"}, + {"DMIC MUX10", "DMIC5", "DMIC5"}, + {"AMIC MUX10", "ADC1", "ADC1"}, + {"AMIC MUX10", "ADC2", "ADC2"}, + {"AMIC MUX10", "ADC3", "ADC3"}, + {"AMIC MUX10", "ADC4", "ADC4"}, + + {"DMIC MUX11", "DMIC0", "DMIC0"}, + {"DMIC MUX11", "DMIC1", "DMIC1"}, + {"DMIC MUX11", "DMIC2", "DMIC2"}, + {"DMIC MUX11", "DMIC3", "DMIC3"}, + {"DMIC MUX11", "DMIC4", "DMIC4"}, + {"DMIC MUX11", "DMIC5", "DMIC5"}, + {"AMIC MUX11", "ADC1", "ADC1"}, + {"AMIC MUX11", "ADC2", "ADC2"}, + {"AMIC MUX11", "ADC3", "ADC3"}, + {"AMIC MUX11", "ADC4", "ADC4"}, + + {"DMIC MUX12", "DMIC0", "DMIC0"}, + {"DMIC MUX12", "DMIC1", "DMIC1"}, + {"DMIC MUX12", "DMIC2", "DMIC2"}, + {"DMIC MUX12", "DMIC3", "DMIC3"}, + {"DMIC MUX12", "DMIC4", "DMIC4"}, + {"DMIC MUX12", "DMIC5", "DMIC5"}, + {"AMIC MUX12", "ADC1", "ADC1"}, + {"AMIC MUX12", "ADC2", "ADC2"}, + {"AMIC MUX12", "ADC3", "ADC3"}, + {"AMIC MUX12", "ADC4", "ADC4"}, + + {"DMIC MUX13", "DMIC0", "DMIC0"}, + {"DMIC MUX13", "DMIC1", "DMIC1"}, + {"DMIC MUX13", "DMIC2", "DMIC2"}, + {"DMIC MUX13", "DMIC3", "DMIC3"}, + {"DMIC MUX13", "DMIC4", "DMIC4"}, + {"DMIC MUX13", "DMIC5", "DMIC5"}, + {"AMIC MUX13", "ADC1", "ADC1"}, + {"AMIC MUX13", "ADC2", "ADC2"}, + {"AMIC MUX13", "ADC3", "ADC3"}, + {"AMIC MUX13", "ADC4", "ADC4"}, + + {"AMIC4_5 SEL", "AMIC4", "AMIC4"}, + {"AMIC4_5 SEL", "AMIC5", "AMIC5"}, + + {"ADC1", NULL, "AMIC1"}, + {"ADC2", NULL, "AMIC2"}, + {"ADC3", NULL, "AMIC3"}, + {"ADC4", NULL, "AMIC4_5 SEL"}, + + /* CDC Rx interface with SLIMBUS */ + {"CDC_IF RX0 MUX", "SLIM RX0", "SLIM RX0"}, + {"CDC_IF RX1 MUX", "SLIM RX1", "SLIM RX1"}, + {"CDC_IF RX2 MUX", "SLIM RX2", "SLIM RX2"}, + {"CDC_IF RX3 MUX", "SLIM RX3", "SLIM RX3"}, + {"CDC_IF RX4 MUX", "SLIM RX4", "SLIM RX4"}, + {"CDC_IF RX5 MUX", "SLIM RX5", "SLIM RX5"}, + {"CDC_IF RX6 MUX", "SLIM RX6", "SLIM RX6"}, + {"CDC_IF RX7 MUX", "SLIM RX7", "SLIM RX7"}, + + {"RX INT0_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT0_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT1_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT1_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT2_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT3_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT3_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT3_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT4_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT4_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT4_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT7_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT7_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT7_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT8_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP0", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP0", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP1", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP1", "IIR1", "IIR1"}, + {"RX INT8_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_1 MIX1 INP2", "IIR0", "IIR0"}, + {"RX INT8_1 MIX1 INP2", "IIR1", "IIR1"}, + + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"}, + {"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"}, + {"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"}, + {"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP0"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP1"}, + {"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP2"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP0"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP1"}, + {"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP2"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP1"}, + {"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP2"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP0"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP1"}, + {"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP2"}, + + /* Mixing path INT0 */ + {"RX INT0_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT0_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT0_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT0_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT0_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT0_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT0_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT0_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT0_2 INTERP", NULL, "RX INT0_2 MUX"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_2 INTERP"}, + + /* Mixing path INT1 */ + {"RX INT1_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT1_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT1_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT1_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT1_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT1_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT1_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT1_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT1_2 INTERP", NULL, "RX INT1_2 MUX"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_2 INTERP"}, + + /* Mixing path INT2 */ + {"RX INT2_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT2_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT2_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT2_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT2_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT2_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT2_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT2_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT2_2 INTERP", NULL, "RX INT2_2 MUX"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_2 INTERP"}, + + /* Mixing path INT3 */ + {"RX INT3_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT3_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT3_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT3_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT3_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT3_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT3_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT3_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT3_2 INTERP", NULL, "RX INT3_2 MUX"}, + {"RX INT3 SEC MIX", NULL, "RX INT3_2 INTERP"}, + + /* Mixing path INT4 */ + {"RX INT4_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT4_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT4_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT4_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT4_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT4_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT4_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT4_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT4_2 INTERP", NULL, "RX INT4_2 MUX"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_2 INTERP"}, + + /* Mixing path INT7 */ + {"RX INT7_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT7_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT7_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT7_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT7_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT7_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT7_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT7_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT7_2 INTERP", NULL, "RX INT7_2 MUX"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_2 INTERP"}, + + /* Mixing path INT8 */ + {"RX INT8_2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"RX INT8_2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"RX INT8_2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"RX INT8_2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"RX INT8_2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"RX INT8_2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"RX INT8_2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"RX INT8_2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"RX INT8_2 INTERP", NULL, "RX INT8_2 MUX"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_2 INTERP"}, + + {"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"}, + {"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"}, + {"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"}, + {"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"}, + {"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"}, + {"RX INT0 DAC", NULL, "RX INT0 DEM MUX"}, + {"RX INT0 DAC", NULL, "RX_BIAS"}, + {"EAR PA", NULL, "RX INT0 DAC"}, + {"EAR", NULL, "EAR PA"}, + + {"RX INT1_1 INTERP", NULL, "RX INT1_1 MIX1"}, + {"RX INT1 SEC MIX", NULL, "RX INT1_1 INTERP"}, + {"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"}, + {"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"}, + {"RX INT1 MIX3", NULL, "RX INT1 MIX2"}, + {"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX3"}, + {"RX INT1 DAC", NULL, "RX INT1 DEM MUX"}, + {"RX INT1 DAC", NULL, "RX_BIAS"}, + {"HPHL PA", NULL, "RX INT1 DAC"}, + {"HPHL", NULL, "HPHL PA"}, + + {"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"}, + {"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"}, + {"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"}, + {"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"}, + {"RX INT2 MIX3", NULL, "RX INT2 MIX2"}, + {"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 MIX3"}, + {"RX INT2 DAC", NULL, "RX INT2 DEM MUX"}, + {"RX INT2 DAC", NULL, "RX_BIAS"}, + {"HPHR PA", NULL, "RX INT2 DAC"}, + {"HPHR", NULL, "HPHR PA"}, + + {"RX INT3_1 INTERP", NULL, "RX INT3_1 MIX1"}, + {"RX INT3 SEC MIX", NULL, "RX INT3_1 INTERP"}, + {"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"}, + {"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"}, + {"RX INT3 MIX3", NULL, "RX INT3 MIX2"}, + {"RX INT3 DAC", NULL, "RX INT3 MIX3"}, + {"RX INT3 DAC", NULL, "RX_BIAS"}, + {"LINEOUT1 PA", NULL, "RX INT3 DAC"}, + {"LINEOUT1", NULL, "LINEOUT1 PA"}, + + {"RX INT4_1 INTERP", NULL, "RX INT4_1 MIX1"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_1 INTERP"}, + {"RX INT4 SEC MIX", NULL, "RX INT4_1 MIX1"}, + {"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"}, + {"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"}, + {"RX INT4 MIX3", NULL, "RX INT4 MIX2"}, + {"RX INT4 DAC", NULL, "RX INT4 MIX3"}, + {"RX INT4 DAC", NULL, "RX_BIAS"}, + {"LINEOUT2 PA", NULL, "RX INT4 DAC"}, + {"LINEOUT2", NULL, "LINEOUT2 PA"}, + + {"RX INT7_1 INTERP", NULL, "RX INT7_1 MIX1"}, + {"RX INT7 SEC MIX", NULL, "RX INT7_1 INTERP"}, + {"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"}, + {"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"}, + {"RX INT7 CHAIN", NULL, "RX INT7 MIX2"}, + {"RX INT7 CHAIN", NULL, "RX_BIAS"}, + {"SPK1 OUT", NULL, "RX INT7 CHAIN"}, + + {"RX INT8_1 INTERP", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_1 INTERP"}, + {"RX INT8 SEC MIX", NULL, "RX INT8_1 MIX1"}, + {"RX INT8 CHAIN", NULL, "RX INT8 SEC MIX"}, + {"RX INT8 CHAIN", NULL, "RX_BIAS"}, + {"SPK2 OUT", NULL, "RX INT8 CHAIN"}, + + /* ANC Routing */ + {"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"}, + {"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"}, + {"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"}, + + {"ANC OUT EAR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR Enable", "Switch", "ADC MUX11"}, + {"RX INT0 MIX2", NULL, "ANC OUT EAR Enable"}, + + {"ANC OUT HPHL Enable", "Switch", "ADC MUX10"}, + {"ANC OUT HPHL Enable", "Switch", "ADC MUX11"}, + {"RX INT1 MIX2", NULL, "ANC OUT HPHL Enable"}, + + {"ANC OUT HPHR Enable", "Switch", "ADC MUX12"}, + {"ANC OUT HPHR Enable", "Switch", "ADC MUX13"}, + {"RX INT2 MIX2", NULL, "ANC OUT HPHR Enable"}, + + {"ANC EAR PA", NULL, "RX INT0 DAC"}, + {"ANC EAR", NULL, "ANC EAR PA"}, + + {"ANC HPHL PA", NULL, "RX INT1 DAC"}, + {"ANC HPHL", NULL, "ANC HPHL PA"}, + + {"ANC HPHR PA", NULL, "RX INT2 DAC"}, + {"ANC HPHR", NULL, "ANC HPHR PA"}, + + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"}, + {"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"}, + {"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"}, + + {"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"}, + {"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"}, + {"SPK1 OUT", NULL, "ANC SPK1 PA"}, + + /* + * SRC0, SRC1 inputs to Sidetone RX Mixer + * on RX0, RX1, RX2, RX3, RX4 and RX7 chains + */ + {"IIR0", NULL, "IIR0 INP0 MUX"}, + {"IIR0 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP0 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP0 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP0 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP0 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP0 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP0 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP0 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP0 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP1 MUX"}, + {"IIR0 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP1 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP1 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP1 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP1 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP1 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP1 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP1 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP1 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP2 MUX"}, + {"IIR0 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR0", NULL, "IIR0 INP3 MUX"}, + {"IIR0 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR0 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR0 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR0 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR0 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR0 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR0 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR0 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR0 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR0 INP3 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR0 INP3 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR0 INP3 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR0 INP3 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR0 INP3 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR0 INP3 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR0 INP3 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR0 INP3 MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"IIR1", NULL, "IIR1 INP0 MUX"}, + {"IIR1 INP0 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP0 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP0 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP0 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP0 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP0 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP0 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP0 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP0 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP0 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP0 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP0 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP0 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP0 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP0 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP0 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP0 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR1", NULL, "IIR1 INP1 MUX"}, + {"IIR1 INP1 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP1 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP1 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP1 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP1 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP1 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP1 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP1 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP1 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP1 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP1 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP1 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP1 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP1 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP1 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP1 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP1 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR1", NULL, "IIR1 INP2 MUX"}, + {"IIR1 INP2 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP2 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP2 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP2 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP2 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP2 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP2 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP2 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP2 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP2 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP2 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP2 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP2 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP2 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP2 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP2 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP2 MUX", "RX7", "CDC_IF RX7 MUX"}, + {"IIR1", NULL, "IIR1 INP3 MUX"}, + {"IIR1 INP3 MUX", "DEC0", "ADC MUX0"}, + {"IIR1 INP3 MUX", "DEC1", "ADC MUX1"}, + {"IIR1 INP3 MUX", "DEC2", "ADC MUX2"}, + {"IIR1 INP3 MUX", "DEC3", "ADC MUX3"}, + {"IIR1 INP3 MUX", "DEC4", "ADC MUX4"}, + {"IIR1 INP3 MUX", "DEC5", "ADC MUX5"}, + {"IIR1 INP3 MUX", "DEC6", "ADC MUX6"}, + {"IIR1 INP3 MUX", "DEC7", "ADC MUX7"}, + {"IIR1 INP3 MUX", "DEC8", "ADC MUX8"}, + {"IIR1 INP3 MUX", "RX0", "CDC_IF RX0 MUX"}, + {"IIR1 INP3 MUX", "RX1", "CDC_IF RX1 MUX"}, + {"IIR1 INP3 MUX", "RX2", "CDC_IF RX2 MUX"}, + {"IIR1 INP3 MUX", "RX3", "CDC_IF RX3 MUX"}, + {"IIR1 INP3 MUX", "RX4", "CDC_IF RX4 MUX"}, + {"IIR1 INP3 MUX", "RX5", "CDC_IF RX5 MUX"}, + {"IIR1 INP3 MUX", "RX6", "CDC_IF RX6 MUX"}, + {"IIR1 INP3 MUX", "RX7", "CDC_IF RX7 MUX"}, + + {"SRC0", NULL, "IIR0"}, + {"SRC1", NULL, "IIR1"}, + {"RX INT0 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT0 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT1 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT1 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT2 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT2 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT3 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT3 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT4 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT4 MIX2 INP", "SRC1", "SRC1"}, + {"RX INT7 MIX2 INP", "SRC0", "SRC0"}, + {"RX INT7 MIX2 INP", "SRC1", "SRC1"}, + + /* Native clk main path routing */ + {"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"}, + {"RX INT1_1 INTERP", NULL, "RX INT1_1 NATIVE MUX"}, + {"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, + + {"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"}, + {"RX INT2_1 INTERP", NULL, "RX INT2_1 NATIVE MUX"}, + {"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, + + {"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"}, + {"RX INT3_1 INTERP", NULL, "RX INT3_1 NATIVE MUX"}, + {"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, + + {"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"}, + {"RX INT4_1 INTERP", NULL, "RX INT4_1 NATIVE MUX"}, + {"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, + + /* Native clk mix path routing */ + {"RX INT1_2 NATIVE MUX", "ON", "RX INT1_2 MUX"}, + {"RX INT1_2 INTERP", NULL, "RX INT1_2 NATIVE MUX"}, + {"RX INT1_2 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"}, + + {"RX INT2_2 NATIVE MUX", "ON", "RX INT2_2 MUX"}, + {"RX INT2_2 INTERP", NULL, "RX INT2_2 NATIVE MUX"}, + {"RX INT2_2 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"}, + + {"RX INT3_2 NATIVE MUX", "ON", "RX INT3_2 MUX"}, + {"RX INT3_2 INTERP", NULL, "RX INT3_2 NATIVE MUX"}, + {"RX INT3_2 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"}, + + {"RX INT4_2 NATIVE MUX", "ON", "RX INT4_2 MUX"}, + {"RX INT4_2 INTERP", NULL, "RX INT4_2 NATIVE MUX"}, + {"RX INT4_2 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"}, + + {"RX INT7_2 NATIVE MUX", "ON", "RX INT7_2 MUX"}, + {"RX INT7_2 INTERP", NULL, "RX INT7_2 NATIVE MUX"}, + {"RX INT7_2 NATIVE MUX", NULL, "RX INT7 NATIVE SUPPLY"}, + + {"RX INT8_2 NATIVE MUX", "ON", "RX INT8_2 MUX"}, + {"RX INT8_2 INTERP", NULL, "RX INT8_2 NATIVE MUX"}, + {"RX INT8_2 NATIVE MUX", NULL, "RX INT8 NATIVE SUPPLY"}, + + /* ASRC Routing */ + {"ASRC0 MUX", "ASRC_IN_HPHL", "RX INT1_2 INTERP"}, + {"RX INT1 SEC MIX", "HPHL Switch", "ASRC0 MUX"}, + + {"ASRC1 MUX", "ASRC_IN_HPHR", "RX INT2_2 INTERP"}, + {"RX INT2 SEC MIX", "HPHR Switch", "ASRC1 MUX"}, + + {"ASRC0 MUX", "ASRC_IN_LO1", "RX INT3_2 INTERP"}, + {"RX INT3 SEC MIX", "LO1 Switch", "ASRC0 MUX"}, + + {"ASRC1 MUX", "ASRC_IN_LO2", "RX INT4_2 INTERP"}, + {"RX INT4 SEC MIX", "LO2 Switch", "ASRC1 MUX"}, + + {"ASRC2 MUX", "ASRC_IN_SPKR1", "RX INT7_2 INTERP"}, + {"RX INT7 SEC MIX", NULL, "ASRC2 MUX"}, + + {"ASRC3 MUX", "ASRC_IN_SPKR2", "RX INT8_2 INTERP"}, + {"RX INT8 SEC MIX", NULL, "ASRC3 MUX"}, +}; + +#endif diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c new file mode 100644 index 000000000000..ca16ed8c8ae3 --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -0,0 +1,9938 @@ +/* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wcd934x.h" +#include "wcd934x-mbhc.h" +#include "wcd934x-routing.h" +#include "wcd934x-dsp-cntl.h" +#include "../wcd9xxx-common-v2.h" +#include "../wcd9xxx-resmgr-v2.h" +#include "../wcdcal-hwdep.h" +#include "wcd934x-dsd.h" + +#define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define WCD934X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400) + +#define WCD934X_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +#define WCD934X_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +#define WCD934X_FORMATS_S16_LE (SNDRV_PCM_FMTBIT_S16_LE) + +/* Macros for packing register writes into a U32 */ +#define WCD934X_PACKED_REG_SIZE sizeof(u32) +#define WCD934X_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \ + do { \ + ((reg) = ((packed >> 16) & (0xffff))); \ + ((mask) = ((packed >> 8) & (0xff))); \ + ((val) = ((packed) & (0xff))); \ + } while (0) + +#define STRING(name) #name +#define WCD_DAPM_ENUM(name, reg, offset, text) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM(STRING(name), name##_enum) + +#define WCD_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \ +static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \ +static const struct snd_kcontrol_new name##_mux = \ + SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname) + +#define WCD_DAPM_MUX(name, shift, kctl) \ + SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux) + +/* + * Timeout in milli seconds and it is the wait time for + * slim channel removal interrupt to receive. + */ +#define WCD934X_SLIM_CLOSE_TIMEOUT 1000 +#define WCD934X_SLIM_IRQ_OVERFLOW (1 << 0) +#define WCD934X_SLIM_IRQ_UNDERFLOW (1 << 1) +#define WCD934X_SLIM_IRQ_PORT_CLOSED (1 << 2) +#define WCD934X_MCLK_CLK_12P288MHZ 12288000 +#define WCD934X_MCLK_CLK_9P6MHZ 9600000 + +#define WCD934X_INTERP_MUX_NUM_INPUTS 3 +#define WCD934X_NUM_INTERPOLATORS 9 +#define WCD934X_NUM_DECIMATORS 9 +#define WCD934X_RX_PATH_CTL_OFFSET 20 + +#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE)) + +#define WCD934X_REG_BITS 8 +#define WCD934X_MAX_VALID_ADC_MUX 13 +#define WCD934X_INVALID_ADC_MUX 9 + +#define WCD934X_AMIC_PWR_LEVEL_LP 0 +#define WCD934X_AMIC_PWR_LEVEL_DEFAULT 1 +#define WCD934X_AMIC_PWR_LEVEL_HP 2 +#define WCD934X_AMIC_PWR_LVL_MASK 0x60 +#define WCD934X_AMIC_PWR_LVL_SHIFT 0x5 + +#define WCD934X_DEC_PWR_LVL_MASK 0x06 +#define WCD934X_DEC_PWR_LVL_LP 0x02 +#define WCD934X_DEC_PWR_LVL_HP 0x04 +#define WCD934X_DEC_PWR_LVL_DF 0x00 +#define WCD934X_STRING_LEN 100 + +#define WCD934X_CDC_SIDETONE_IIR_COEFF_MAX 5 +#define WCD934X_DIG_CORE_REG_MIN WCD934X_CDC_ANC0_CLK_RESET_CTL +#define WCD934X_DIG_CORE_REG_MAX 0xFFF + +#define WCD934X_MAX_MICBIAS 4 +#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" +#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone" +#define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone" +#define DAPM_MICBIAS4_STANDALONE "MIC BIAS4 Standalone" + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +#define CPE_ERR_WDOG_BITE BIT(0) +#define CPE_FATAL_IRQS CPE_ERR_WDOG_BITE + +#define WCD934X_MAD_AUDIO_FIRMWARE_PATH "wcd934x/wcd934x_mad_audio.bin" + +#define TAVIL_VERSION_ENTRY_SIZE 17 + +#define WCD934X_DIG_CORE_COLLAPSE_TIMER_MS (5 * 1000) + +enum { + POWER_COLLAPSE, + POWER_RESUME, +}; + +static int dig_core_collapse_enable = 1; +module_param(dig_core_collapse_enable, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating"); + +/* dig_core_collapse timer in seconds */ +static int dig_core_collapse_timer = (WCD934X_DIG_CORE_COLLAPSE_TIMER_MS/1000); +module_param(dig_core_collapse_timer, int, 0664); +MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating"); + +#define TAVIL_HPH_REG_RANGE_1 (WCD934X_HPH_R_DAC_CTL - WCD934X_HPH_CNP_EN + 1) +#define TAVIL_HPH_REG_RANGE_2 (WCD934X_HPH_NEW_ANA_HPH3 -\ + WCD934X_HPH_NEW_ANA_HPH2 + 1) +#define TAVIL_HPH_REG_RANGE_3 (WCD934X_HPH_NEW_INT_PA_RDAC_MISC3 -\ + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL + 1) +#define TAVIL_HPH_TOTAL_REG (TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2 +\ + TAVIL_HPH_REG_RANGE_3) + +enum { + VI_SENSE_1, + VI_SENSE_2, + AUDIO_NOMINAL, + HPH_PA_DELAY, + CLSH_Z_CONFIG, + ANC_MIC_AMIC1, + ANC_MIC_AMIC2, + ANC_MIC_AMIC3, + ANC_MIC_AMIC4, +}; + +enum { + AIF1_PB = 0, + AIF1_CAP, + AIF2_PB, + AIF2_CAP, + AIF3_PB, + AIF3_CAP, + AIF4_PB, + AIF4_VIFEED, + AIF4_MAD_TX, + NUM_CODEC_DAIS, +}; + +enum { + INTn_1_INP_SEL_ZERO = 0, + INTn_1_INP_SEL_DEC0, + INTn_1_INP_SEL_DEC1, + INTn_1_INP_SEL_IIR0, + INTn_1_INP_SEL_IIR1, + INTn_1_INP_SEL_RX0, + INTn_1_INP_SEL_RX1, + INTn_1_INP_SEL_RX2, + INTn_1_INP_SEL_RX3, + INTn_1_INP_SEL_RX4, + INTn_1_INP_SEL_RX5, + INTn_1_INP_SEL_RX6, + INTn_1_INP_SEL_RX7, +}; + +enum { + INTn_2_INP_SEL_ZERO = 0, + INTn_2_INP_SEL_RX0, + INTn_2_INP_SEL_RX1, + INTn_2_INP_SEL_RX2, + INTn_2_INP_SEL_RX3, + INTn_2_INP_SEL_RX4, + INTn_2_INP_SEL_RX5, + INTn_2_INP_SEL_RX6, + INTn_2_INP_SEL_RX7, + INTn_2_INP_SEL_PROXIMITY, +}; + +enum { + INTERP_MAIN_PATH, + INTERP_MIX_PATH, +}; + +struct tavil_idle_detect_config { + u8 hph_idle_thr; + u8 hph_idle_detect_en; +}; + +struct tavil_cpr_reg_defaults { + int wr_data; + int wr_addr; +}; + +struct interp_sample_rate { + int sample_rate; + int rate_val; +}; + +static struct interp_sample_rate sr_val_tbl[] = { + {8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5}, + {192000, 0x6}, {384000, 0x7}, {44100, 0x9}, {88200, 0xA}, + {176400, 0xB}, {352800, 0xC}, +}; + +static const struct wcd9xxx_ch tavil_rx_chs[WCD934X_RX_MAX] = { + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER, 0), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 1, 1), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 2, 2), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 3, 3), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 4, 4), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 5, 5), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 6, 6), + WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 7, 7), +}; + +static const struct wcd9xxx_ch tavil_tx_chs[WCD934X_TX_MAX] = { + WCD9XXX_CH(0, 0), + WCD9XXX_CH(1, 1), + WCD9XXX_CH(2, 2), + WCD9XXX_CH(3, 3), + WCD9XXX_CH(4, 4), + WCD9XXX_CH(5, 5), + WCD9XXX_CH(6, 6), + WCD9XXX_CH(7, 7), + WCD9XXX_CH(8, 8), + WCD9XXX_CH(9, 9), + WCD9XXX_CH(10, 10), + WCD9XXX_CH(11, 11), + WCD9XXX_CH(12, 12), + WCD9XXX_CH(13, 13), + WCD9XXX_CH(14, 14), + WCD9XXX_CH(15, 15), +}; + +static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = { + 0, /* AIF1_PB */ + BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */ + 0, /* AIF2_PB */ + BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */ + 0, /* AIF3_PB */ + BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */ + 0, /* AIF4_PB */ +}; + +/* Codec supports 2 IIR filters */ +enum { + IIR0 = 0, + IIR1, + IIR_MAX, +}; + +/* Each IIR has 5 Filter Stages */ +enum { + BAND1 = 0, + BAND2, + BAND3, + BAND4, + BAND5, + BAND_MAX, +}; + +enum { + COMPANDER_1, /* HPH_L */ + COMPANDER_2, /* HPH_R */ + COMPANDER_3, /* LO1_DIFF */ + COMPANDER_4, /* LO2_DIFF */ + COMPANDER_5, /* LO3_SE - not used in Tavil */ + COMPANDER_6, /* LO4_SE - not used in Tavil */ + COMPANDER_7, /* SWR SPK CH1 */ + COMPANDER_8, /* SWR SPK CH2 */ + COMPANDER_MAX, +}; + +enum { + ASRC_IN_HPHL, + ASRC_IN_LO1, + ASRC_IN_HPHR, + ASRC_IN_LO2, + ASRC_IN_SPKR1, + ASRC_IN_SPKR2, + ASRC_INVALID, +}; + +enum { + ASRC0, + ASRC1, + ASRC2, + ASRC3, + ASRC_MAX, +}; + +enum { + CONV_88P2K_TO_384K, + CONV_96K_TO_352P8K, + CONV_352P8K_TO_384K, + CONV_384K_TO_352P8K, + CONV_384K_TO_384K, + CONV_96K_TO_384K, +}; + +static struct afe_param_slimbus_slave_port_cfg tavil_slimbus_slave_port_cfg = { + .minor_version = 1, + .slimbus_dev_id = AFE_SLIMBUS_DEVICE_1, + .slave_dev_pgd_la = 0, + .slave_dev_intfdev_la = 0, + .bit_width = 16, + .data_format = 0, + .num_channels = 1 +}; + +static struct afe_param_cdc_reg_page_cfg tavil_cdc_reg_page_cfg = { + .minor_version = AFE_API_VERSION_CDC_REG_PAGE_CFG, + .enable = 1, + .proc_id = AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1, +}; + +static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = { + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_MAIN_CTL_1), + HW_MAD_AUDIO_ENABLE, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_AUDIO_CTL_3), + HW_MAD_AUDIO_SLEEP_TIME, 0xF, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_AUDIO_CTL_4), + HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_CFG), + MAD_AUDIO_INT_DEST_SELECT_REG, 0x2, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_MASK3), + MAD_AUDIO_INT_MASK_REG, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_STATUS3), + MAD_AUDIO_INT_STATUS_REG, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_CLEAR3), + MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_WATERMARK_N, 0x1E, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_TX_BASE), + SB_PGD_PORT_TX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_WATERMARK_N, 0x1E, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE), + SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FF_GAIN_ADAPTIVE, 0x4, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_IIR_ADAPT_CTL), + AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + WCD934X_CDC_ANC0_FF_A_GAIN_CTL), + AANC_GAIN_CONTROL, 0xFF, WCD934X_REG_BITS, 0 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_TX_PORT_MULTI_CHANNEL_0(0)), + SB_PGD_TX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_TX_PORT_MULTI_CHANNEL_1(0)), + SB_PGD_TX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x180, 0)), + SB_PGD_RX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4 + }, + { + 1, + (WCD934X_REGISTER_START_OFFSET + + SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x181, 0)), + SB_PGD_RX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4 + }, +}; + +static struct afe_param_cdc_reg_cfg_data tavil_audio_reg_cfg = { + .num_registers = ARRAY_SIZE(audio_reg_cfg), + .reg_data = audio_reg_cfg, +}; + +static struct afe_param_id_cdc_aanc_version tavil_cdc_aanc_version = { + .cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION, + .aanc_hw_version = AANC_HW_BLOCK_VERSION_2, +}; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1); +static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1); + +#define WCD934X_TX_UNMUTE_DELAY_MS 40 + +static int tx_unmute_delay = WCD934X_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + +static void tavil_codec_set_tx_hold(struct snd_soc_codec *, u16, bool); + +/* Hold instance to soundwire platform device */ +struct tavil_swr_ctrl_data { + struct platform_device *swr_pdev; +}; + +struct wcd_swr_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, void *data), + void *swrm_handle, int action); +}; + +/* Holds all Soundwire and speaker related information */ +struct wcd934x_swr { + struct tavil_swr_ctrl_data *ctrl_data; + struct wcd_swr_ctrl_platform_data plat_data; + struct mutex read_mutex; + struct mutex write_mutex; + struct mutex clk_mutex; + int spkr_gain_offset; + int spkr_mode; + int clk_users; + int rx_7_count; + int rx_8_count; +}; + +struct tx_mute_work { + struct tavil_priv *tavil; + u8 decimator; + struct delayed_work dwork; +}; + +#define WCD934X_SPK_ANC_EN_DELAY_MS 350 +static int spk_anc_en_delay = WCD934X_SPK_ANC_EN_DELAY_MS; +module_param(spk_anc_en_delay, int, 0664); +MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path"); + +struct spk_anc_work { + struct tavil_priv *tavil; + struct delayed_work dwork; +}; + +struct hpf_work { + struct tavil_priv *tavil; + u8 decimator; + u8 hpf_cut_off_freq; + struct delayed_work dwork; +}; + +struct tavil_priv { + struct device *dev; + struct wcd9xxx *wcd9xxx; + struct snd_soc_codec *codec; + u32 rx_bias_count; + s32 dmic_0_1_clk_cnt; + s32 dmic_2_3_clk_cnt; + s32 dmic_4_5_clk_cnt; + s32 micb_ref[TAVIL_MAX_MICBIAS]; + s32 pullup_ref[TAVIL_MAX_MICBIAS]; + + /* ANC related */ + u32 anc_slot; + bool anc_func; + + /* compander */ + int comp_enabled[COMPANDER_MAX]; + int ear_spkr_gain; + + /* class h specific data */ + struct wcd_clsh_cdc_data clsh_d; + /* Tavil Interpolator Mode Select for EAR, HPH_L and HPH_R */ + u32 hph_mode; + + /* Mad switch reference count */ + int mad_switch_cnt; + + /* track tavil interface type */ + u8 intf_type; + + /* to track the status */ + unsigned long status_mask; + + struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg; + + /* num of slim ports required */ + struct wcd9xxx_codec_dai_data dai[NUM_CODEC_DAIS]; + /* Port values for Rx and Tx codec_dai */ + unsigned int rx_port_value[WCD934X_RX_MAX]; + unsigned int tx_port_value; + + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd934x_swr swr; + struct mutex micb_lock; + + struct delayed_work power_gate_work; + struct mutex power_lock; + + struct clk *wcd_ext_clk; + + /* mbhc module */ + struct wcd934x_mbhc *mbhc; + + struct mutex codec_mutex; + struct work_struct tavil_add_child_devices_work; + struct hpf_work tx_hpf_work[WCD934X_NUM_DECIMATORS]; + struct tx_mute_work tx_mute_dwork[WCD934X_NUM_DECIMATORS]; + struct spk_anc_work spk_anc_dwork; + + unsigned int vi_feed_value; + + /* DSP control */ + struct wcd_dsp_cntl *wdsp_cntl; + + /* cal info for codec */ + struct fw_info *fw_data; + + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + + /* SVS voting related */ + struct mutex svs_mutex; + int svs_ref_cnt; + + int native_clk_users; + /* ASRC users count */ + int asrc_users[ASRC_MAX]; + int asrc_output_mode[ASRC_MAX]; + /* Main path clock users count */ + int main_clk_users[WCD934X_NUM_INTERPOLATORS]; + struct tavil_dsd_config *dsd_config; + struct tavil_idle_detect_config idle_det_cfg; + + int power_active_ref; + int sidetone_coeff_array[IIR_MAX][BAND_MAX] + [WCD934X_CDC_SIDETONE_IIR_COEFF_MAX]; +}; + +static const struct tavil_reg_mask_val tavil_spkr_default[] = { + {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50}, + {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50}, +}; + +static const struct tavil_reg_mask_val tavil_spkr_mode1[] = { + {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x00}, + {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x00}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x00}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x00}, + {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44}, + {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44}, +}; + +static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil); + +/** + * tavil_set_spkr_gain_offset - offset the speaker path + * gain with the given offset value. + * + * @codec: codec instance + * @offset: Indicates speaker path gain offset value. + * + * Returns 0 on success or -EINVAL on error. + */ +int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + + if (!priv) + return -EINVAL; + + priv->swr.spkr_gain_offset = offset; + return 0; +} +EXPORT_SYMBOL(tavil_set_spkr_gain_offset); + +/** + * tavil_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @codec: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + int i; + const struct tavil_reg_mask_val *regs; + int size; + + if (!priv) + return -EINVAL; + + switch (mode) { + case WCD934X_SPKR_MODE_1: + regs = tavil_spkr_mode1; + size = ARRAY_SIZE(tavil_spkr_mode1); + break; + default: + regs = tavil_spkr_default; + size = ARRAY_SIZE(tavil_spkr_default); + break; + } + + priv->swr.spkr_mode = mode; + for (i = 0; i < size; i++) + snd_soc_update_bits(codec, regs[i].reg, + regs[i].mask, regs[i].val); + return 0; +} +EXPORT_SYMBOL(tavil_set_spkr_mode); + +/** + * tavil_get_afe_config - returns specific codec configuration to afe to write + * + * @codec: codec instance + * @config_type: Indicates type of configuration to write. + */ +void *tavil_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + + switch (config_type) { + case AFE_SLIMBUS_SLAVE_CONFIG: + return &priv->slimbus_slave_cfg; + case AFE_CDC_REGISTERS_CONFIG: + return &tavil_audio_reg_cfg; + case AFE_SLIMBUS_SLAVE_PORT_CONFIG: + return &tavil_slimbus_slave_port_cfg; + case AFE_AANC_VERSION: + return &tavil_cdc_aanc_version; + case AFE_CDC_REGISTER_PAGE_CONFIG: + return &tavil_cdc_reg_page_cfg; + default: + dev_info(codec->dev, "%s: Unknown config_type 0x%x\n", + __func__, config_type); + return NULL; + } +} +EXPORT_SYMBOL(tavil_get_afe_config); + +static bool is_tavil_playback_dai(int dai_id) +{ + if ((dai_id == AIF1_PB) || (dai_id == AIF2_PB) || + (dai_id == AIF3_PB) || (dai_id == AIF4_PB)) + return true; + + return false; +} + +static int tavil_find_playback_dai_id_for_port(int port_id, + struct tavil_priv *tavil) +{ + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx_ch *ch; + int i, slv_port_id; + + for (i = AIF1_PB; i < NUM_CODEC_DAIS; i++) { + if (!is_tavil_playback_dai(i)) + continue; + + dai = &tavil->dai[i]; + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + slv_port_id = wcd9xxx_get_slave_port(ch->ch_num); + if ((slv_port_id > 0) && (slv_port_id == port_id)) + return i; + } + } + + return -EINVAL; +} + +static void tavil_vote_svs(struct tavil_priv *tavil, bool vote) +{ + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tavil->wcd9xxx; + + mutex_lock(&tavil->svs_mutex); + if (vote) { + tavil->svs_ref_cnt++; + if (tavil->svs_ref_cnt == 1) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, + 0x01, 0x01); + } else { + /* Do not decrement ref count if it is already 0 */ + if (tavil->svs_ref_cnt == 0) + goto done; + + tavil->svs_ref_cnt--; + if (tavil->svs_ref_cnt == 0) + regmap_update_bits(wcd9xxx->regmap, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, + 0x01, 0x00); + } +done: + dev_dbg(tavil->dev, "%s: vote = %s, updated ref cnt = %u\n", __func__, + vote ? "vote" : "Unvote", tavil->svs_ref_cnt); + mutex_unlock(&tavil->svs_mutex); +} + +static int tavil_get_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->anc_slot; + return 0; +} + +static int tavil_put_anc_slot(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + tavil->anc_slot = ucontrol->value.integer.value[0]; + return 0; +} + +static int tavil_get_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = (tavil->anc_func == true ? 1 : 0); + return 0; +} + +static int tavil_put_anc_func(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + mutex_lock(&tavil->codec_mutex); + tavil->anc_func = (!ucontrol->value.integer.value[0] ? false : true); + dev_dbg(codec->dev, "%s: anc_func %x", __func__, tavil->anc_func); + + if (tavil->anc_func == true) { + snd_soc_dapm_enable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC EAR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_enable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_disable_pin(dapm, "EAR PA"); + snd_soc_dapm_disable_pin(dapm, "EAR"); + snd_soc_dapm_disable_pin(dapm, "HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "HPHL"); + snd_soc_dapm_disable_pin(dapm, "HPHR"); + } else { + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_enable_pin(dapm, "EAR PA"); + snd_soc_dapm_enable_pin(dapm, "EAR"); + snd_soc_dapm_enable_pin(dapm, "HPHL"); + snd_soc_dapm_enable_pin(dapm, "HPHR"); + snd_soc_dapm_enable_pin(dapm, "HPHL PA"); + snd_soc_dapm_enable_pin(dapm, "HPHR PA"); + } + mutex_unlock(&tavil->codec_mutex); + + snd_soc_dapm_sync(dapm); + return 0; +} + +static int tavil_codec_enable_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + const char *filename; + const struct firmware *fw; + int i; + int ret = 0; + int num_anc_slots; + struct wcd9xxx_anc_header *anc_head; + struct firmware_cal *hwdep_cal = NULL; + u32 anc_writes_size = 0; + u32 anc_cal_size = 0; + int anc_size_remaining; + u32 *anc_ptr; + u16 reg; + u8 mask, val; + size_t cal_size; + const void *data; + + if (!tavil->anc_func) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_ANC_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration, cal_size %zd", + __func__, cal_size); + } else { + filename = "WCD934X/WCD934X_anc.bin"; + ret = request_firmware(&fw, filename, codec->dev); + if (ret < 0) { + dev_err(codec->dev, "%s: Failed to acquire ANC data: %d\n", + __func__, ret); + return ret; + } + if (!fw) { + dev_err(codec->dev, "%s: Failed to get anc fw\n", + __func__); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + if (cal_size < sizeof(struct wcd9xxx_anc_header)) { + dev_err(codec->dev, "%s: Invalid cal_size %zd\n", + __func__, cal_size); + ret = -EINVAL; + goto err; + } + /* First number is the number of register writes */ + anc_head = (struct wcd9xxx_anc_header *)(data); + anc_ptr = (u32 *)(data + sizeof(struct wcd9xxx_anc_header)); + anc_size_remaining = cal_size - + sizeof(struct wcd9xxx_anc_header); + num_anc_slots = anc_head->num_anc_slots; + + if (tavil->anc_slot >= num_anc_slots) { + dev_err(codec->dev, "%s: Invalid ANC slot selected\n", + __func__); + ret = -EINVAL; + goto err; + } + for (i = 0; i < num_anc_slots; i++) { + if (anc_size_remaining < WCD934X_PACKED_REG_SIZE) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + anc_writes_size = (u32)(*anc_ptr); + anc_size_remaining -= sizeof(u32); + anc_ptr += 1; + + if ((anc_writes_size * WCD934X_PACKED_REG_SIZE) > + anc_size_remaining) { + dev_err(codec->dev, "%s: Invalid register format\n", + __func__); + ret = -EINVAL; + goto err; + } + + if (tavil->anc_slot == i) + break; + + anc_size_remaining -= (anc_writes_size * + WCD934X_PACKED_REG_SIZE); + anc_ptr += anc_writes_size; + } + if (i == num_anc_slots) { + dev_err(codec->dev, "%s: Selected ANC slot not present\n", + __func__); + ret = -EINVAL; + goto err; + } + + anc_cal_size = anc_writes_size; + for (i = 0; i < anc_writes_size; i++) { + WCD934X_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val); + snd_soc_write(codec, reg, (val & mask)); + } + + /* Rate converter clk enable and set bypass mode */ + if (!strcmp(w->name, "RX INT0 DAC") || + !strcmp(w->name, "RX INT1 DAC") || + !strcmp(w->name, "ANC SPK1 PA")) { + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_RC_COMMON_CTL, + 0x05, 0x05); + if (!strcmp(w->name, "RX INT1 DAC")) { + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_FIFO_COMMON_CTL, + 0x66, 0x66); + } + } else if (!strcmp(w->name, "RX INT2 DAC")) { + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_RC_COMMON_CTL, + 0x05, 0x05); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_FIFO_COMMON_CTL, + 0x66, 0x66); + } + if (!strcmp(w->name, "RX INT1 DAC")) + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, 0x08, 0x08); + else if (!strcmp(w->name, "RX INT2 DAC")) + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_CLK_RESET_CTL, 0x08, 0x08); + + if (!hwdep_cal) + release_firmware(fw); + break; + + case SND_SOC_DAPM_POST_PMU: + if (!strcmp(w->name, "ANC HPHL PA") || + !strcmp(w->name, "ANC HPHR PA")) { + /* Remove ANC Rx from reset */ + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x08, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_CLK_RESET_CTL, + 0x08, 0x00); + } + + break; + + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_RC_COMMON_CTL, + 0x05, 0x00); + if (!strcmp(w->name, "ANC EAR PA") || + !strcmp(w->name, "ANC SPK1 PA") || + !strcmp(w->name, "ANC HPHL PA")) { + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL, + 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x38); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x07, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC0_CLK_RESET_CTL, + 0x38, 0x00); + } else if (!strcmp(w->name, "ANC HPHR PA")) { + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_1_CTL, + 0x30, 0x00); + msleep(50); + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_1_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_CLK_RESET_CTL, + 0x38, 0x38); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_CLK_RESET_CTL, + 0x07, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_ANC1_CLK_RESET_CTL, + 0x38, 0x00); + } + break; + } + + return 0; +err: + if (!hwdep_cal) + release_firmware(fw); + return ret; +} + +static int tavil_vi_feed_mixer_get(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_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil_p->vi_feed_value; + + return 0; +} + +static int tavil_vi_feed_mixer_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 snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n", + __func__, enable, port_id, dai_id); + + tavil_p->vi_feed_value = ucontrol->value.integer.value[0]; + + mutex_lock(&tavil_p->codec_mutex); + if (enable) { + if (port_id == WCD934X_TX14 && !test_bit(VI_SENSE_1, + &tavil_p->status_mask)) { + list_add_tail(&core->tx_chs[WCD934X_TX14].list, + &tavil_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_1, &tavil_p->status_mask); + } + if (port_id == WCD934X_TX15 && !test_bit(VI_SENSE_2, + &tavil_p->status_mask)) { + list_add_tail(&core->tx_chs[WCD934X_TX15].list, + &tavil_p->dai[dai_id].wcd9xxx_ch_list); + set_bit(VI_SENSE_2, &tavil_p->status_mask); + } + } else { + if (port_id == WCD934X_TX14 && test_bit(VI_SENSE_1, + &tavil_p->status_mask)) { + list_del_init(&core->tx_chs[WCD934X_TX14].list); + clear_bit(VI_SENSE_1, &tavil_p->status_mask); + } + if (port_id == WCD934X_TX15 && test_bit(VI_SENSE_2, + &tavil_p->status_mask)) { + list_del_init(&core->tx_chs[WCD934X_TX15].list); + clear_bit(VI_SENSE_2, &tavil_p->status_mask); + } + } + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +static int slim_tx_mixer_get(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_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil_p->tx_port_value; + return 0; +} + +static int slim_tx_mixer_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 snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + u32 vtable; + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, + widget->name, ucontrol->id.name, tavil_p->tx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tavil_p->codec_mutex); + if (dai_id >= ARRAY_SIZE(vport_slim_check_table)) { + dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n", + __func__, dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + vtable = vport_slim_check_table[dai_id]; + + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && !(tavil_p->tx_port_value & 1 << port_id)) { + if (wcd9xxx_tx_vport_validation(vtable, port_id, + tavil_p->dai, NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n", + __func__, port_id); + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + tavil_p->tx_port_value |= 1 << port_id; + list_add_tail(&core->tx_chs[port_id].list, + &tavil_p->dai[dai_id].wcd9xxx_ch_list); + } else if (!enable && (tavil_p->tx_port_value & + 1 << port_id)) { + tavil_p->tx_port_value &= ~(1 << port_id); + list_del_init(&core->tx_chs[port_id].list); + } else { + if (enable) + dev_dbg(codec->dev, "%s: TX%u port is used by\n" + "this virtual port\n", + __func__, port_id); + else + dev_dbg(codec->dev, "%s: TX%u port is not used by\n" + "this virtual port\n", + __func__, port_id); + /* avoid update power function */ + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + break; + case AIF4_MAD_TX: + break; + default: + dev_err(codec->dev, "Unknown AIF %d\n", dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: name %s sname %s updated value %u shift %d\n", + __func__, widget->name, widget->sname, tavil_p->tx_port_value, + widget->shift); + + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + +static int slim_rx_mux_get(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_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = + tavil_p->rx_port_value[widget->shift]; + return 0; +} + +static int slim_rx_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 snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + unsigned int rx_port_value; + u32 port_id = widget->shift; + + tavil_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + rx_port_value = tavil_p->rx_port_value[port_id]; + + mutex_lock(&tavil_p->codec_mutex); + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, widget->name, ucontrol->id.name, + rx_port_value, widget->shift, + ucontrol->value.integer.value[0]); + + /* value need to match the Virtual port and AIF number */ + switch (rx_port_value) { + case 0: + list_del_init(&core->rx_chs[port_id].list); + break; + case 1: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF1_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF1_PB].wcd9xxx_ch_list); + break; + case 2: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF2_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF2_PB].wcd9xxx_ch_list); + break; + case 3: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF3_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF3_PB].wcd9xxx_ch_list); + break; + case 4: + if (wcd9xxx_rx_vport_validation(port_id + + WCD934X_RX_PORT_START_NUMBER, + &tavil_p->dai[AIF4_PB].wcd9xxx_ch_list)) { + dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n", + __func__, port_id); + goto rtn; + } + list_add_tail(&core->rx_chs[port_id].list, + &tavil_p->dai[AIF4_PB].wcd9xxx_ch_list); + break; + default: + dev_err(codec->dev, "Unknown AIF %d\n", rx_port_value); + goto err; + } +rtn: + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + + return 0; +err: + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; +} + +static void tavil_codec_enable_slim_port_intr( + struct wcd9xxx_codec_dai_data *dai, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_ch *ch; + int port_num = 0; + unsigned short reg = 0; + u8 val = 0; + struct tavil_priv *tavil_p; + + if (!dai || !codec) { + pr_err("%s: Invalid params\n", __func__); + return; + } + + tavil_p = snd_soc_codec_get_drvdata(codec); + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + if (ch->port >= WCD934X_RX_PORT_START_NUMBER) { + port_num = ch->port - WCD934X_RX_PORT_START_NUMBER; + reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tavil_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write( + tavil_p->wcd9xxx, reg, val); + val = wcd9xxx_interface_reg_read( + tavil_p->wcd9xxx, reg); + } + } else { + port_num = ch->port; + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8); + val = wcd9xxx_interface_reg_read(tavil_p->wcd9xxx, + reg); + if (!(val & BYTE_BIT_MASK(port_num))) { + val |= BYTE_BIT_MASK(port_num); + wcd9xxx_interface_reg_write(tavil_p->wcd9xxx, + reg, val); + val = wcd9xxx_interface_reg_read( + tavil_p->wcd9xxx, reg); + } + } + } +} + +static int tavil_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai, + bool up) +{ + int ret = 0; + struct wcd9xxx_ch *ch; + + if (up) { + list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) { + ret = wcd9xxx_get_slave_port(ch->ch_num); + if (ret < 0) { + pr_err("%s: Invalid slave port ID: %d\n", + __func__, ret); + ret = -EINVAL; + } else { + set_bit(ret, &dai->ch_mask); + } + } + } else { + ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0), + msecs_to_jiffies( + WCD934X_SLIM_CLOSE_TIMEOUT)); + if (!ret) { + pr_err("%s: Slim close tx/rx wait timeout, ch_mask:0x%lx\n", + __func__, dai->ch_mask); + ret = -ETIMEDOUT; + } else { + ret = 0; + } + } + return ret; +} + +static void tavil_codec_mute_dsd(struct snd_soc_codec *codec, + struct list_head *ch_list) +{ + u8 dsd0_in; + u8 dsd1_in; + struct wcd9xxx_ch *ch; + + /* Read DSD Input Ports */ + dsd0_in = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x3C) >> 2; + dsd1_in = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x3C) >> 2; + + if ((dsd0_in == 0) && (dsd1_in == 0)) + return; + + /* + * Check if the ports getting disabled are connected to DSD inputs. + * If connected, enable DSD mute to avoid DC entering into DSD Filter + */ + list_for_each_entry(ch, ch_list, list) { + if (ch->port == (dsd0_in + WCD934X_RX_PORT_START_NUMBER - 1)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x04); + if (ch->port == (dsd1_in + WCD934X_RX_PORT_START_NUMBER - 1)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x04); + } +} + +static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + int ret = 0; + struct wcd9xxx_codec_dai_data *dai; + struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config; + + core = dev_get_drvdata(codec->dev->parent); + + dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d\n" + "stream name %s event %d\n", + __func__, codec->component.name, + codec->component.num_dai, w->sname, event); + + dai = &tavil_p->dai[w->shift]; + dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n", + __func__, w->name, w->shift, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tavil_codec_enable_slim_port_intr(dai, codec); + (void) tavil_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + if (dsd_conf) + tavil_codec_mute_dsd(codec, &dai->wcd9xxx_ch_list); + + ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n", + __func__, ret); + + if (!dai->bus_down_in_recovery) + ret = tavil_codec_enable_slim_chmask(dai, false); + else + dev_dbg(codec->dev, + "%s: bus in recovery skip enable slim_chmask", + __func__); + ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list, + dai->grph); + break; + } + return ret; +} + +static int tavil_codec_enable_slimtx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + struct wcd9xxx *core; + int ret = 0; + + dev_dbg(codec->dev, + "%s: w->name %s, w->shift = %d, num_dai %d stream name %s\n", + __func__, w->name, w->shift, + codec->component.num_dai, w->sname); + + dai = &tavil_p->dai[w->shift]; + core = dev_get_drvdata(codec->dev->parent); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dai->bus_down_in_recovery = false; + tavil_codec_enable_slim_port_intr(dai, codec); + (void) tavil_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (!dai->bus_down_in_recovery) + ret = tavil_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n", + __func__, ret); + } + break; + } + return ret; +} + +static int tavil_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct wcd9xxx *core = NULL; + struct snd_soc_codec *codec = NULL; + struct tavil_priv *tavil_p = NULL; + int ret = 0; + struct wcd9xxx_codec_dai_data *dai = NULL; + + codec = snd_soc_dapm_to_codec(w->dapm); + tavil_p = snd_soc_codec_get_drvdata(codec); + core = dev_get_drvdata(codec->dev->parent); + + dev_dbg(codec->dev, + "%s: num_dai %d stream name %s w->name %s event %d shift %d\n", + __func__, codec->component.num_dai, w->sname, + w->name, event, w->shift); + + if (w->shift != AIF4_VIFEED) { + pr_err("%s Error in enabling the tx path\n", __func__); + ret = -EINVAL; + goto done; + } + dai = &tavil_p->dai[w->shift]; + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) { + dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) { + pr_debug("%s: spkr2 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + dai->bus_down_in_recovery = false; + tavil_codec_enable_slim_port_intr(dai, codec); + (void) tavil_codec_enable_slim_chmask(dai, true); + ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->rate, dai->bit_width, + &dai->grph); + break; + case SND_SOC_DAPM_POST_PMD: + ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list, + dai->grph); + if (ret) + dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n", + __func__, ret); + if (!dai->bus_down_in_recovery) + ret = tavil_codec_enable_slim_chmask(dai, false); + if (ret < 0) { + ret = wcd9xxx_disconnect_port(core, + &dai->wcd9xxx_ch_list, + dai->grph); + dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n", + __func__, ret); + } + if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + break; + } +done: + return ret; +} + +static int tavil_codec_enable_rx_bias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil->rx_bias_count++; + if (tavil->rx_bias_count == 1) { + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x01, 0x01); + } + break; + case SND_SOC_DAPM_POST_PMD: + tavil->rx_bias_count--; + if (!tavil->rx_bias_count) + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x01, 0x00); + break; + }; + dev_dbg(codec->dev, "%s: Current RX BIAS user count: %d\n", __func__, + tavil->rx_bias_count); + + return 0; +} + +static void tavil_spk_anc_update_callback(struct work_struct *work) +{ + struct spk_anc_work *spk_anc_dwork; + struct tavil_priv *tavil; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + + delayed_work = to_delayed_work(work); + spk_anc_dwork = container_of(delayed_work, struct spk_anc_work, dwork); + tavil = spk_anc_dwork->tavil; + codec = tavil->codec; + + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0, 0x10, 0x10); +} + +static int tavil_codec_enable_spkr_anc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil->anc_func) + return 0; + + dev_dbg(codec->dev, "%s: w: %s event: %d anc: %d\n", __func__, + w->name, event, tavil->anc_func); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = tavil_codec_enable_anc(w, kcontrol, event); + schedule_delayed_work(&tavil->spk_anc_dwork.dwork, + msecs_to_jiffies(spk_anc_en_delay)); + break; + case SND_SOC_DAPM_POST_PMD: + cancel_delayed_work_sync(&tavil->spk_anc_dwork.dwork); + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0, + 0x10, 0x00); + ret = tavil_codec_enable_anc(w, kcontrol, event); + break; + } + return ret; +} + +static int tavil_codec_enable_ear_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* + * 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CTL, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD934X_CDC_RX0_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX0_RX_PATH_MIX_CTL, + 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + + if (!(strcmp(w->name, "ANC EAR PA"))) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0, + 0x10, 0x00); + } + break; + }; + + return ret; +} + +static void tavil_codec_override(struct snd_soc_codec *codec, int mode, + int event) +{ + if (mode == CLS_AB || mode == CLS_AB_HIFI) { + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + case SND_SOC_DAPM_POST_PMU: + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, + WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00); + break; + } + } +} + +static void tavil_codec_clear_anc_tx_hold(struct tavil_priv *tavil) +{ + if (test_and_clear_bit(ANC_MIC_AMIC1, &tavil->status_mask)) + tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC1, false); + if (test_and_clear_bit(ANC_MIC_AMIC2, &tavil->status_mask)) + tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC2, false); + if (test_and_clear_bit(ANC_MIC_AMIC3, &tavil->status_mask)) + tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC3, false); + if (test_and_clear_bit(ANC_MIC_AMIC4, &tavil->status_mask)) + tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC4, false); +} + +static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, (0x03 << 1)); + + if ((!(strcmp(w->name, "ANC HPHR PA"))) && + (test_bit(HPH_PA_DELAY, &tavil->status_mask))) + snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0xC0, 0xC0); + + set_bit(HPH_PA_DELAY, &tavil->status_mask); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) { + /* Set regulator mode to AB if DSD is enabled */ + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x02, 0x02); + } + break; + case SND_SOC_DAPM_POST_PMU: + if ((!(strcmp(w->name, "ANC HPHR PA")))) { + if ((snd_soc_read(codec, WCD934X_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC case) + * then do nothing for POST_PMU and let left + * channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + if (test_bit(HPH_PA_DELAY, &tavil->status_mask)) { + if (!tavil->comp_enabled[COMPANDER_2]) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tavil->status_mask); + } + if (tavil->anc_func) { + /* Clear Tx FE HOLD if both PAs are enabled */ + if ((snd_soc_read(tavil->codec, WCD934X_ANA_HPH) & + 0xC0) == 0xC0) + tavil_codec_clear_anc_tx_hold(tavil); + } + + snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x01); + + /* Remove mute */ + snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + /* Enable GM3 boost */ + snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL, + 0x80, 0x80); + /* Enable AutoChop timer at the end of power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x02); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x00); + if (!(strcmp(w->name, "ANC HPHR PA"))) { + pr_debug("%s:Do everything needed for left channel\n", + __func__); + /* Do everything needed for left channel */ + snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, + 0x01, 0x01); + + /* Remove mute */ + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + + if (dsd_conf && (snd_soc_read(codec, + WCD934X_CDC_DSD0_PATH_CTL) & + 0x01)) + snd_soc_update_bits(codec, + WCD934X_CDC_DSD0_CFG2, + 0x04, 0x00); + /* Remove ANC Rx from reset */ + ret = tavil_codec_enable_anc(w, kcontrol, event); + } + tavil_codec_override(codec, tavil->hph_mode, event); + break; + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_PRE_HPHR_PA_OFF, + &tavil->mbhc->wcd_mbhc); + /* Enable DSD Mute before PA disable */ + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x10); + if (!(strcmp(w->name, "ANC HPHR PA"))) + snd_soc_update_bits(codec, WCD934X_ANA_HPH, 0x40, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + if (!tavil->comp_enabled[COMPANDER_2]) + usleep_range(20000, 20100); + else + usleep_range(5000, 5100); + tavil_codec_override(codec, tavil->hph_mode, event); + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_POST_HPHR_PA_OFF, + &tavil->mbhc->wcd_mbhc); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, 0x0); + if (!(strcmp(w->name, "ANC HPHR PA"))) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD934X_CDC_RX2_RX_PATH_CFG0, + 0x10, 0x00); + } + break; + }; + + return ret; +} + +static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + int ret = 0; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, (0x03 << 1)); + if ((!(strcmp(w->name, "ANC HPHL PA"))) && + (test_bit(HPH_PA_DELAY, &tavil->status_mask))) + snd_soc_update_bits(codec, WCD934X_ANA_HPH, + 0xC0, 0xC0); + set_bit(HPH_PA_DELAY, &tavil->status_mask); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) { + /* Set regulator mode to AB if DSD is enabled */ + snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES, + 0x02, 0x02); + } + break; + case SND_SOC_DAPM_POST_PMU: + if (!(strcmp(w->name, "ANC HPHL PA"))) { + if ((snd_soc_read(codec, WCD934X_ANA_HPH) & 0xC0) + != 0xC0) + /* + * If PA_EN is not set (potentially in ANC + * case) then do nothing for POST_PMU and + * let right channel handle everything. + */ + break; + } + /* + * 7ms sleep is required after PA is enabled as per + * HW requirement. If compander is disabled, then + * 20ms delay is needed. + */ + if (test_bit(HPH_PA_DELAY, &tavil->status_mask)) { + if (!tavil->comp_enabled[COMPANDER_1]) + usleep_range(20000, 20100); + else + usleep_range(7000, 7100); + clear_bit(HPH_PA_DELAY, &tavil->status_mask); + } + if (tavil->anc_func) { + /* Clear Tx FE HOLD if both PAs are enabled */ + if ((snd_soc_read(tavil->codec, WCD934X_ANA_HPH) & + 0xC0) == 0xC0) + tavil_codec_clear_anc_tx_hold(tavil); + } + + snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x01); + /* Remove Mute on primary path */ + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL, + 0x10, 0x00); + /* Enable GM3 boost */ + snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL, + 0x80, 0x80); + /* Enable AutoChop timer at the end of power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x02); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, WCD934X_CDC_RX1_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x00); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x00); + if (!(strcmp(w->name, "ANC HPHL PA"))) { + pr_debug("%s:Do everything needed for right channel\n", + __func__); + + /* Do everything needed for right channel */ + snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, + 0x01, 0x01); + + /* Remove mute */ + snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL, + 0x10, 0x00); + + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) & + 0x10) + snd_soc_update_bits(codec, + WCD934X_CDC_RX2_RX_PATH_MIX_CTL, + 0x10, 0x00); + if (dsd_conf && (snd_soc_read(codec, + WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, + WCD934X_CDC_DSD1_CFG2, + 0x04, 0x00); + /* Remove ANC Rx from reset */ + ret = tavil_codec_enable_anc(w, kcontrol, event); + } + tavil_codec_override(codec, tavil->hph_mode, event); + break; + case SND_SOC_DAPM_PRE_PMD: + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_PRE_HPHL_PA_OFF, + &tavil->mbhc->wcd_mbhc); + /* Enable DSD Mute before PA disable */ + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) + snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, + 0x04, 0x04); + + snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL, + 0x10, 0x10); + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_MIX_CTL, + 0x10, 0x10); + if (!(strcmp(w->name, "ANC HPHL PA"))) + snd_soc_update_bits(codec, WCD934X_ANA_HPH, + 0x80, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA disable. If compander is + * disabled, then 20ms delay is needed after PA disable. + */ + if (!tavil->comp_enabled[COMPANDER_1]) + usleep_range(20000, 20100); + else + usleep_range(5000, 5100); + tavil_codec_override(codec, tavil->hph_mode, event); + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_POST_HPHL_PA_OFF, + &tavil->mbhc->wcd_mbhc); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, + 0x06, 0x0); + if (!(strcmp(w->name, "ANC HPHL PA"))) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + snd_soc_update_bits(codec, + WCD934X_CDC_RX1_RX_PATH_CFG0, 0x10, 0x00); + } + break; + }; + + return ret; +} + +static int tavil_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 lineout_vol_reg = 0, lineout_mix_vol_reg = 0; + u16 dsd_mute_reg = 0, dsd_clk_reg = 0; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (w->reg == WCD934X_ANA_LO_1_2) { + if (w->shift == 7) { + lineout_vol_reg = WCD934X_CDC_RX3_RX_PATH_CTL; + lineout_mix_vol_reg = WCD934X_CDC_RX3_RX_PATH_MIX_CTL; + dsd_mute_reg = WCD934X_CDC_DSD0_CFG2; + dsd_clk_reg = WCD934X_CDC_DSD0_PATH_CTL; + } else if (w->shift == 6) { + lineout_vol_reg = WCD934X_CDC_RX4_RX_PATH_CTL; + lineout_mix_vol_reg = WCD934X_CDC_RX4_RX_PATH_MIX_CTL; + dsd_mute_reg = WCD934X_CDC_DSD1_CFG2; + dsd_clk_reg = WCD934X_CDC_DSD1_PATH_CTL; + } + } else { + dev_err(codec->dev, "%s: Error enabling lineout PA\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_override(codec, CLS_AB, event); + break; + case SND_SOC_DAPM_POST_PMU: + /* + * 5ms sleep is required after PA is enabled as per + * HW requirement + */ + usleep_range(5000, 5500); + snd_soc_update_bits(codec, lineout_vol_reg, + 0x10, 0x00); + /* Remove mix path mute if it is enabled */ + if ((snd_soc_read(codec, lineout_mix_vol_reg)) & 0x10) + snd_soc_update_bits(codec, + lineout_mix_vol_reg, + 0x10, 0x00); + if (dsd_conf && (snd_soc_read(codec, dsd_clk_reg) & 0x01)) + snd_soc_update_bits(codec, dsd_mute_reg, 0x04, 0x00); + break; + case SND_SOC_DAPM_PRE_PMD: + if (dsd_conf && (snd_soc_read(codec, dsd_clk_reg) & 0x01)) + snd_soc_update_bits(codec, dsd_mute_reg, 0x04, 0x04); + break; + case SND_SOC_DAPM_POST_PMD: + /* + * 5ms sleep is required after PA is disabled as per + * HW requirement + */ + usleep_range(5000, 5500); + tavil_codec_override(codec, CLS_AB, event); + default: + break; + }; + + return 0; +} + +static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + + if (tavil->anc_func) + ret = tavil_codec_enable_anc(w, kcontrol, event); + + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + if (tavil->anc_func) + snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_EAR, + CLS_H_NORMAL); + break; + default: + break; + }; + + return ret; +} + +static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int hph_mode = tavil->hph_mode; + u8 dem_inp; + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + int ret = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tavil->anc_func) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, WCD934X_CDC_RX2_RX_PATH_SEC0) & + 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control enable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x01); + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + /* Set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x40); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) + hph_mode = CLS_H_HIFI; + + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHR, + hph_mode); + if (tavil->anc_func) + snd_soc_update_bits(codec, + WCD934X_CDC_RX2_RX_PATH_CFG0, + 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHR, + hph_mode); + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control disable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x0); + /* Re-set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x0); + break; + default: + break; + }; + + return 0; +} + +static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int hph_mode = tavil->hph_mode; + u8 dem_inp; + int ret = 0; + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + uint32_t impedl = 0, impedr = 0; + + dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__, + w->name, event, hph_mode); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tavil->anc_func) { + ret = tavil_codec_enable_anc(w, kcontrol, event); + /* 40 msec delay is needed to avoid click and pop */ + msleep(40); + } + /* Read DEM INP Select */ + dem_inp = snd_soc_read(codec, WCD934X_CDC_RX1_RX_PATH_SEC0) & + 0x03; + if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) || + (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) { + dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n", + __func__, hph_mode); + return -EINVAL; + } + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control enable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x01); + /* Disable AutoChop timer during power up */ + snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1, + 0x02, 0x00); + /* Set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x40); + if (dsd_conf && + (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) + hph_mode = CLS_H_HIFI; + + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_HPHL, + hph_mode); + + if (tavil->anc_func) + snd_soc_update_bits(codec, + WCD934X_CDC_RX1_RX_PATH_CFG0, + 0x10, 0x10); + + ret = tavil_mbhc_get_impedance(tavil->mbhc, + &impedl, &impedr); + if (!ret) { + wcd_clsh_imped_config(codec, impedl, false); + set_bit(CLSH_Z_CONFIG, &tavil->status_mask); + } else { + dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n", + __func__, ret); + ret = 0; + } + + break; + case SND_SOC_DAPM_POST_PMD: + /* 1000us required as per HW requirement */ + usleep_range(1000, 1100); + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_HPHL, + hph_mode); + if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP)) + /* Ripple freq control disable */ + snd_soc_update_bits(codec, + WCD934X_SIDO_NEW_VOUT_D_FREQ2, + 0x01, 0x0); + /* Re-set RDAC gain */ + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + snd_soc_update_bits(codec, + WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + 0xF0, 0x0); + + if (test_bit(CLSH_Z_CONFIG, &tavil->status_mask)) { + wcd_clsh_imped_config(codec, impedl, true); + clear_bit(CLSH_Z_CONFIG, &tavil->status_mask); + } + break; + default: + break; + }; + + return ret; +} + +static int tavil_codec_lineout_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_PRE_DAC, + WCD_CLSH_STATE_LO, + CLS_AB); + break; + case SND_SOC_DAPM_POST_PMD: + wcd_clsh_fsm(codec, &tavil->clsh_d, + WCD_CLSH_EVENT_POST_PA, + WCD_CLSH_STATE_LO, + CLS_AB); + break; + } + + return 0; +} + +static int tavil_codec_spk_boost_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); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg, reg_mix; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (!strcmp(w->name, "RX INT7 CHAIN")) { + boost_path_ctl = WCD934X_CDC_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = WCD934X_CDC_RX7_RX_PATH_CFG1; + reg = WCD934X_CDC_RX7_RX_PATH_CTL; + reg_mix = WCD934X_CDC_RX7_RX_PATH_MIX_CTL; + } else if (!strcmp(w->name, "RX INT8 CHAIN")) { + boost_path_ctl = WCD934X_CDC_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = WCD934X_CDC_RX8_RX_PATH_CFG1; + reg = WCD934X_CDC_RX8_RX_PATH_CTL; + reg_mix = WCD934X_CDC_RX8_RX_PATH_MIX_CTL; + } else { + dev_err(codec->dev, "%s: unknown widget: %s\n", + __func__, w->name); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01); + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, reg, 0x10, 0x00); + if ((snd_soc_read(codec, reg_mix)) & 0x10) + snd_soc_update_bits(codec, reg_mix, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00); + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00); + break; + }; + + return 0; +} + +static int __tavil_codec_enable_swr(struct snd_soc_dapm_widget *w, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil; + int ch_cnt = 0; + + tavil = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) || + (strnstr(w->name, "INT7 MIX2", + sizeof("RX INT7 MIX2"))))) + tavil->swr.rx_7_count++; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + !tavil->swr.rx_8_count) + tavil->swr.rx_8_count++; + ch_cnt = !!(tavil->swr.rx_7_count) + tavil->swr.rx_8_count; + + swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_UP, NULL); + swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + break; + case SND_SOC_DAPM_POST_PMD: + if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) || + (strnstr(w->name, "INT7 MIX2", + sizeof("RX INT7 MIX2")))) + tavil->swr.rx_7_count--; + if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) && + tavil->swr.rx_8_count) + tavil->swr.rx_8_count--; + ch_cnt = !!(tavil->swr.rx_7_count) + tavil->swr.rx_8_count; + + swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + + break; + } + dev_dbg(tavil->dev, "%s: %s: current swr ch cnt: %d\n", + __func__, w->name, ch_cnt); + + return 0; +} + +static int tavil_codec_enable_swr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __tavil_codec_enable_swr(w, event); +} + +static int tavil_codec_config_mad(struct snd_soc_codec *codec) +{ + int ret = 0; + int idx; + const struct firmware *fw; + struct firmware_cal *hwdep_cal = NULL; + struct wcd_mad_audio_cal *mad_cal = NULL; + const void *data; + const char *filename = WCD934X_MAD_AUDIO_FIRMWARE_PATH; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + size_t cal_size; + + hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_MAD_CAL); + if (hwdep_cal) { + data = hwdep_cal->data; + cal_size = hwdep_cal->size; + dev_dbg(codec->dev, "%s: using hwdep calibration\n", + __func__); + } else { + ret = request_firmware(&fw, filename, codec->dev); + if (ret || !fw) { + dev_err(codec->dev, + "%s: MAD firmware acquire failed, err = %d\n", + __func__, ret); + return -ENODEV; + } + data = fw->data; + cal_size = fw->size; + dev_dbg(codec->dev, "%s: using request_firmware calibration\n", + __func__); + } + + if (cal_size < sizeof(*mad_cal)) { + dev_err(codec->dev, + "%s: Incorrect size %zd for MAD Cal, expected %zd\n", + __func__, cal_size, sizeof(*mad_cal)); + ret = -ENOMEM; + goto done; + } + + mad_cal = (struct wcd_mad_audio_cal *) (data); + if (!mad_cal) { + dev_err(codec->dev, + "%s: Invalid calibration data\n", + __func__); + ret = -EINVAL; + goto done; + } + + snd_soc_write(codec, WCD934X_SOC_MAD_MAIN_CTL_2, + mad_cal->microphone_info.cycle_time); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_MAIN_CTL_1, 0xFF << 3, + ((uint16_t)mad_cal->microphone_info.settle_time) + << 3); + + /* Audio */ + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_8, + mad_cal->audio_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_1, + 0x07 << 4, mad_cal->audio_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, 0x03 << 2, + mad_cal->audio_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_7, + mad_cal->audio_info.rms_diff_threshold & 0x3F); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_5, + mad_cal->audio_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_6, + mad_cal->audio_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL, + mad_cal->audio_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->audio_info.iir_coefficients[idx]); + } + + /* Beacon */ + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_8, + mad_cal->beacon_info.rms_omit_samples); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_1, + 0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_2, 0x03 << 2, + mad_cal->beacon_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_7, + mad_cal->beacon_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_5, + mad_cal->beacon_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_6, + mad_cal->beacon_info.rms_threshold_msb); + + for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients); + idx++) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR, + 0x3F, idx); + snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL, + mad_cal->beacon_info.iir_coefficients[idx]); + dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x", + __func__, idx, + mad_cal->beacon_info.iir_coefficients[idx]); + } + + /* Ultrasound */ + snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_1, + 0x07 << 4, + mad_cal->ultrasound_info.rms_comp_time << 4); + snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_2, 0x03 << 2, + mad_cal->ultrasound_info.detection_mechanism << 2); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_7, + mad_cal->ultrasound_info.rms_diff_threshold & 0x1F); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_5, + mad_cal->ultrasound_info.rms_threshold_lsb); + snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_6, + mad_cal->ultrasound_info.rms_threshold_msb); + +done: + if (!hwdep_cal) + release_firmware(fw); + + return ret; +} + +static int __tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable) +{ + int rc = 0; + + /* Return if CPE INPUT is DEC1 */ + if (snd_soc_read(codec, WCD934X_CPE_SS_SVA_CFG) & 0x04) { + dev_dbg(codec->dev, "%s: MAD is bypassed, skip mad %s\n", + __func__, enable ? "enable" : "disable"); + return rc; + } + + dev_dbg(codec->dev, "%s: enable = %s\n", __func__, + enable ? "enable" : "disable"); + + if (enable) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x03); + rc = tavil_codec_config_mad(codec); + if (rc < 0) { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x00); + goto done; + } + + /* Turn on MAD clk */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x01, 0x01); + + /* Undo reset for MAD */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x04, 0x04); + } else { + snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, + 0x03, 0x00); + /* Reset the MAD block */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x02, 0x02); + /* Turn off MAD clk */ + snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x04, 0x00); + } +done: + return rc; +} + +static int tavil_codec_ape_enable_mad(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int rc = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x40); + rc = __tavil_codec_enable_mad(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x00); + __tavil_codec_enable_mad(codec, false); + break; + } + + dev_dbg(tavil->dev, "%s: event = %d\n", __func__, event); + return rc; +} + +static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int rc = 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil->mad_switch_cnt++; + if (tavil->mad_switch_cnt != 1) + goto done; + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x20); + rc = __tavil_codec_enable_mad(codec, true); + if (rc < 0) { + tavil->mad_switch_cnt--; + goto done; + } + + break; + case SND_SOC_DAPM_PRE_PMD: + tavil->mad_switch_cnt--; + if (tavil->mad_switch_cnt != 0) + goto done; + + snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x00); + __tavil_codec_enable_mad(codec, false); + break; + } +done: + dev_dbg(tavil->dev, "%s: event = %d, mad_switch_cnt = %d\n", + __func__, event, tavil->mad_switch_cnt); + return rc; +} + +static int tavil_get_asrc_mode(struct tavil_priv *tavil, int asrc, + u8 main_sr, u8 mix_sr) +{ + u8 asrc_output_mode; + int asrc_mode = CONV_88P2K_TO_384K; + + if ((asrc < 0) || (asrc >= ASRC_MAX)) + return 0; + + asrc_output_mode = tavil->asrc_output_mode[asrc]; + + if (asrc_output_mode) { + /* + * If Mix sample rate is < 96KHz, use 96K to 352.8K + * conversion, or else use 384K to 352.8K conversion + */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else { + /* Integer main and Fractional mix path */ + if (main_sr < 8 && mix_sr > 9) { + asrc_mode = CONV_352P8K_TO_384K; + } else if (main_sr > 8 && mix_sr < 8) { + /* Fractional main and Integer mix path */ + if (mix_sr < 5) + asrc_mode = CONV_96K_TO_352P8K; + else + asrc_mode = CONV_384K_TO_352P8K; + } else if (main_sr < 8 && mix_sr < 8) { + /* Integer main and Integer mix path */ + asrc_mode = CONV_96K_TO_384K; + } + } + + return asrc_mode; +} + +static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, + int asrc_in, int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 cfg_reg, ctl_reg, clk_reg, asrc_ctl, mix_ctl_reg; + int asrc, ret = 0; + u8 main_sr, mix_sr, asrc_mode = 0; + + switch (asrc_in) { + case ASRC_IN_HPHL: + cfg_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX1_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC0_CTL1; + asrc = ASRC0; + break; + case ASRC_IN_LO1: + cfg_reg = WCD934X_CDC_RX3_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX3_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC0_CTL1; + asrc = ASRC0; + break; + case ASRC_IN_HPHR: + cfg_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX2_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC1_CTL1; + asrc = ASRC1; + break; + case ASRC_IN_LO2: + cfg_reg = WCD934X_CDC_RX4_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX4_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC1_CTL1; + asrc = ASRC1; + break; + case ASRC_IN_SPKR1: + cfg_reg = WCD934X_CDC_RX7_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX7_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC2_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC2_CTL1; + asrc = ASRC2; + break; + case ASRC_IN_SPKR2: + cfg_reg = WCD934X_CDC_RX8_RX_PATH_CFG0; + ctl_reg = WCD934X_CDC_RX8_RX_PATH_CTL; + clk_reg = WCD934X_MIXING_ASRC3_CLK_RST_CTL; + asrc_ctl = WCD934X_MIXING_ASRC3_CTL1; + asrc = ASRC3; + break; + default: + dev_err(codec->dev, "%s: Invalid asrc input :%d\n", __func__, + asrc_in); + ret = -EINVAL; + goto done; + }; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (tavil->asrc_users[asrc] == 0) { + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x80); + snd_soc_update_bits(codec, clk_reg, 0x01, 0x01); + main_sr = snd_soc_read(codec, ctl_reg) & 0x0F; + mix_ctl_reg = ctl_reg + 5; + mix_sr = snd_soc_read(codec, mix_ctl_reg) & 0x0F; + asrc_mode = tavil_get_asrc_mode(tavil, asrc, + main_sr, mix_sr); + dev_dbg(codec->dev, "%s: main_sr:%d mix_sr:%d asrc_mode %d\n", + __func__, main_sr, mix_sr, asrc_mode); + snd_soc_update_bits(codec, asrc_ctl, 0x07, asrc_mode); + } + tavil->asrc_users[asrc]++; + break; + case SND_SOC_DAPM_POST_PMD: + tavil->asrc_users[asrc]--; + if (tavil->asrc_users[asrc] <= 0) { + tavil->asrc_users[asrc] = 0; + snd_soc_update_bits(codec, asrc_ctl, 0x07, 0x00); + snd_soc_update_bits(codec, cfg_reg, 0x80, 0x00); + snd_soc_update_bits(codec, clk_reg, 0x01, 0x00); + } + break; + }; + + dev_dbg(codec->dev, "%s: ASRC%d, users: %d\n", + __func__, asrc, tavil->asrc_users[asrc]); + +done: + return ret; +} + +static int tavil_codec_enable_asrc_resampler(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int ret = 0; + u8 cfg, asrc_in; + + cfg = snd_soc_read(codec, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0); + if (!(cfg & 0xFF)) { + dev_err(codec->dev, "%s: ASRC%u input not selected\n", + __func__, w->shift); + return -EINVAL; + } + + switch (w->shift) { + case ASRC0: + asrc_in = ((cfg & 0x03) == 1) ? ASRC_IN_HPHL : ASRC_IN_LO1; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC1: + asrc_in = ((cfg & 0x0C) == 4) ? ASRC_IN_HPHR : ASRC_IN_LO2; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC2: + asrc_in = ((cfg & 0x30) == 0x20) ? ASRC_IN_SPKR1 : ASRC_INVALID; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + case ASRC3: + asrc_in = ((cfg & 0xC0) == 0x80) ? ASRC_IN_SPKR2 : ASRC_INVALID; + ret = tavil_codec_enable_asrc(codec, asrc_in, event); + break; + default: + dev_err(codec->dev, "%s: Invalid asrc:%u\n", __func__, + w->shift); + ret = -EINVAL; + break; + }; + + return ret; +} + +static int tavil_enable_native_supply(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (++tavil->native_clk_users == 1) { + snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES, + 0x01, 0x01); + usleep_range(100, 120); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x06, 0x02); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, + 0x04, 0x00); + usleep_range(30, 50); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x02); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x10); + } + break; + case SND_SOC_DAPM_PRE_PMD: + if (tavil->native_clk_users && + (--tavil->native_clk_users == 0)) { + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x10, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x02, 0x00); + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, + 0x04, 0x04); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1, + 0x06, 0x00); + snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES, + 0x01, 0x00); + } + break; + } + + dev_dbg(codec->dev, "%s: native_clk_users: %d, event: %d\n", + __func__, tavil->native_clk_users, event); + + return 0; +} + +static void tavil_codec_hphdelay_lutbypass(struct snd_soc_codec *codec, + u16 interp_idx, int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u8 hph_dly_mask; + u16 hph_lut_bypass_reg = 0; + u16 hph_comp_ctrl7 = 0; + + + switch (interp_idx) { + case INTERP_HPHL: + hph_dly_mask = 1; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHL_COMP_LUT; + hph_comp_ctrl7 = WCD934X_CDC_COMPANDER1_CTL7; + break; + case INTERP_HPHR: + hph_dly_mask = 2; + hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHR_COMP_LUT; + hph_comp_ctrl7 = WCD934X_CDC_COMPANDER2_CTL7; + break; + default: + break; + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, 0x0); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x80); + if (tavil->hph_mode == CLS_H_ULP) + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x20); + } + + if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0, + hph_dly_mask, hph_dly_mask); + snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00); + snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x0); + } +} + +static void tavil_codec_hd2_control(struct tavil_priv *priv, + u16 interp_idx, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + struct snd_soc_codec *codec = priv->codec; + + if (TAVIL_IS_1_1(priv->wcd9xxx)) + return; + + switch (interp_idx) { + case INTERP_HPHL: + hd2_scale_reg = WCD934X_CDC_RX1_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + break; + case INTERP_HPHR: + hd2_scale_reg = WCD934X_CDC_RX2_RX_PATH_SEC3; + hd2_enable_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + break; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x14); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int tavil_codec_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg) +{ + int comp_gain_offset, val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + switch (tavil->swr.spkr_mode) { + /* Compander gain in SPKR_MODE1 case is 12 dB */ + case WCD934X_SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (tavil->comp_enabled[COMPANDER_7] && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) && + (tavil->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + tavil->ear_spkr_gain - 1; + snd_soc_write(codec, gain_reg, val); + + dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX7 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (tavil->comp_enabled[COMPANDER_7] && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) && + (tavil->ear_spkr_gain != 0)) { + snd_soc_write(codec, gain_reg, 0x0); + + dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + +static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n, + int event) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int comp; + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + /* EAR does not have compander */ + if (!interp_n) + return 0; + + comp = interp_n - 1; + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, tavil->comp_enabled[comp]); + + if (!tavil->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = WCD934X_CDC_COMPANDER1_CTL0 + (comp * 8); + rx_path_cfg0_reg = WCD934X_CDC_RX1_RX_PATH_CFG0 + (comp * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static void tavil_codec_idle_detect_control(struct snd_soc_codec *codec, + int interp, int event) +{ + int reg = 0, mask, val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil->idle_det_cfg.hph_idle_detect_en) + return; + + if (interp == INTERP_HPHL) { + reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL; + mask = 0x01; + val = 0x01; + } + if (interp == INTERP_HPHR) { + reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL; + mask = 0x02; + val = 0x02; + } + + if (reg && SND_SOC_DAPM_EVENT_ON(event)) + snd_soc_update_bits(codec, reg, mask, val); + + if (reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, reg, mask, 0x00); + tavil->idle_det_cfg.hph_idle_thr = 0; + snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, 0x0); + } +} + +/** + * tavil_codec_enable_interp_clk - Enable main path Interpolator + * clock. + * + * @codec: Codec instance + * @event: Indicates speaker path gain offset value + * @intp_idx: Interpolator index + * Returns number of main clock users + */ +int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, + int event, int interp_idx) +{ + struct tavil_priv *tavil; + u16 main_reg; + + if (!codec) { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + + tavil = snd_soc_codec_get_drvdata(codec); + main_reg = WCD934X_CDC_RX0_RX_PATH_CTL + (interp_idx * 20); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + if (tavil->main_clk_users[interp_idx] == 0) { + /* Main path PGA mute enable */ + snd_soc_update_bits(codec, main_reg, 0x10, 0x10); + /* Clk enable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x20); + tavil_codec_idle_detect_control(codec, interp_idx, + event); + tavil_codec_hd2_control(tavil, interp_idx, event); + tavil_codec_hphdelay_lutbypass(codec, interp_idx, + event); + tavil_config_compander(codec, interp_idx, event); + } + tavil->main_clk_users[interp_idx]++; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + tavil->main_clk_users[interp_idx]--; + if (tavil->main_clk_users[interp_idx] <= 0) { + tavil->main_clk_users[interp_idx] = 0; + tavil_config_compander(codec, interp_idx, event); + tavil_codec_hphdelay_lutbypass(codec, interp_idx, + event); + tavil_codec_hd2_control(tavil, interp_idx, event); + tavil_codec_idle_detect_control(codec, interp_idx, + event); + /* Clk Disable */ + snd_soc_update_bits(codec, main_reg, 0x20, 0x00); + /* Reset enable and disable */ + snd_soc_update_bits(codec, main_reg, 0x40, 0x40); + snd_soc_update_bits(codec, main_reg, 0x40, 0x00); + /* Reset rate to 48K*/ + snd_soc_update_bits(codec, main_reg, 0x0F, 0x04); + } + } + + dev_dbg(codec->dev, "%s event %d main_clk_users %d\n", + __func__, event, tavil->main_clk_users[interp_idx]); + + return tavil->main_clk_users[interp_idx]; +} +EXPORT_SYMBOL(tavil_codec_enable_interp_clk); + +static int tavil_anc_out_switch_cb(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + tavil_codec_enable_interp_clk(codec, event, w->shift); + + return 0; +} +static int tavil_codec_set_idle_detect_thr(struct snd_soc_codec *codec, + int interp, int path_type) +{ + int port_id[4] = { 0, 0, 0, 0 }; + int *port_ptr, num_ports; + int bit_width = 0, i; + int mux_reg, mux_reg_val; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int dai_id, idle_thr; + + if ((interp != INTERP_HPHL) && (interp != INTERP_HPHR)) + return 0; + + if (!tavil->idle_det_cfg.hph_idle_detect_en) + return 0; + + port_ptr = &port_id[0]; + num_ports = 0; + + /* + * Read interpolator MUX input registers and find + * which slimbus port is connected and store the port + * numbers in port_id array. + */ + if (path_type == INTERP_MIX_PATH) { + mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1 + + 2 * (interp - 1); + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + + if ((mux_reg_val >= INTn_2_INP_SEL_RX0) && + (mux_reg_val < INTn_2_INP_SEL_PROXIMITY)) { + *port_ptr++ = mux_reg_val + + WCD934X_RX_PORT_START_NUMBER - 1; + num_ports++; + } + } + + if (path_type == INTERP_MAIN_PATH) { + mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0 + + 2 * (interp - 1); + mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f; + i = WCD934X_INTERP_MUX_NUM_INPUTS; + + while (i) { + if ((mux_reg_val >= INTn_1_INP_SEL_RX0) && + (mux_reg_val <= INTn_1_INP_SEL_RX7)) { + *port_ptr++ = mux_reg_val + + WCD934X_RX_PORT_START_NUMBER - + INTn_1_INP_SEL_RX0; + num_ports++; + } + mux_reg_val = (snd_soc_read(codec, mux_reg) & + 0xf0) >> 4; + mux_reg += 1; + i--; + } + } + + dev_dbg(codec->dev, "%s: num_ports: %d, ports[%d %d %d %d]\n", + __func__, num_ports, port_id[0], port_id[1], + port_id[2], port_id[3]); + + i = 0; + while (num_ports) { + dai_id = tavil_find_playback_dai_id_for_port(port_id[i++], + tavil); + + if ((dai_id >= 0) && (dai_id < NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: dai_id: %d bit_width: %d\n", + __func__, dai_id, + tavil->dai[dai_id].bit_width); + + if (tavil->dai[dai_id].bit_width > bit_width) + bit_width = tavil->dai[dai_id].bit_width; + } + + num_ports--; + } + + switch (bit_width) { + case 16: + idle_thr = 0xff; /* F16 */ + break; + case 24: + case 32: + idle_thr = 0x03; /* F22 */ + break; + default: + idle_thr = 0x00; + break; + } + + dev_dbg(codec->dev, "%s: (new) idle_thr: %d, (cur) idle_thr: %d\n", + __func__, idle_thr, tavil->idle_det_cfg.hph_idle_thr); + + if ((tavil->idle_det_cfg.hph_idle_thr == 0) || + (idle_thr < tavil->idle_det_cfg.hph_idle_thr)) { + snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, idle_thr); + tavil->idle_det_cfg.hph_idle_thr = idle_thr; + } + + return 0; +} + +static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 gain_reg, mix_reg; + int offset_val = 0; + int val = 0; + + if (w->shift >= WCD934X_NUM_INTERPOLATORS || + w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + }; + + gain_reg = WCD934X_CDC_RX0_RX_VOL_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + mix_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL + + (w->shift * WCD934X_RX_PATH_CTL_OFFSET); + + if (w->shift == INTERP_SPKR1 || w->shift == INTERP_SPKR2) + __tavil_codec_enable_swr(w, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_set_idle_detect_thr(codec, w->shift, + INTERP_MIX_PATH); + tavil_codec_enable_interp_clk(codec, event, w->shift); + /* Clk enable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x20); + break; + case SND_SOC_DAPM_POST_PMU: + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + /* Clk Disable */ + snd_soc_update_bits(codec, mix_reg, 0x20, 0x00); + tavil_codec_enable_interp_clk(codec, event, w->shift); + /* Reset enable and disable */ + snd_soc_update_bits(codec, mix_reg, 0x40, 0x40); + snd_soc_update_bits(codec, mix_reg, 0x40, 0x00); + + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + dev_dbg(codec->dev, "%s event %d name %s\n", __func__, event, w->name); + + return 0; +} + +/** + * tavil_get_dsd_config - Get pointer to dsd config structure + * + * @codec: pointer to snd_soc_codec structure + * + * Returns pointer to tavil_dsd_config structure + */ +struct tavil_dsd_config *tavil_get_dsd_config(struct snd_soc_codec *codec) +{ + struct tavil_priv *tavil; + + if (!codec) + return NULL; + + tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil) + return NULL; + + return tavil->dsd_config; +} +EXPORT_SYMBOL(tavil_get_dsd_config); + +static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (w->shift >= WCD934X_NUM_INTERPOLATORS || + w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) { + dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n", + __func__, w->shift, w->name); + return -EINVAL; + }; + + reg = WCD934X_CDC_RX0_RX_PATH_CTL + (w->shift * + WCD934X_RX_PATH_CTL_OFFSET); + gain_reg = WCD934X_CDC_RX0_RX_VOL_CTL + (w->shift * + WCD934X_RX_PATH_CTL_OFFSET); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_set_idle_detect_thr(codec, w->shift, + INTERP_MAIN_PATH); + tavil_codec_enable_interp_clk(codec, event, w->shift); + break; + case SND_SOC_DAPM_POST_PMU: + /* apply gain after int clk is enabled */ + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + case SND_SOC_DAPM_POST_PMD: + tavil_codec_enable_interp_clk(codec, event, w->shift); + + if ((tavil->swr.spkr_gain_offset == + WCD934X_RX_GAIN_OFFSET_M1P5_DB) && + (tavil->comp_enabled[COMPANDER_7] || + tavil->comp_enabled[COMPANDER_8]) && + (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL || + gain_reg == WCD934X_CDC_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + WCD934X_CDC_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + tavil_codec_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int tavil_codec_set_iir_gain(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event = %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: /* fall through */ + case SND_SOC_DAPM_PRE_PMD: + if (strnstr(w->name, "IIR0", sizeof("IIR0"))) { + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)); + } else { + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)); + snd_soc_write(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, + snd_soc_read(codec, + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)); + } + break; + } + return 0; +} + +static int tavil_codec_find_amic_input(struct snd_soc_codec *codec, + int adc_mux_n) +{ + u16 mask, shift, adc_mux_in_reg; + u16 amic_mux_sel_reg; + bool is_amic; + + if (adc_mux_n < 0 || adc_mux_n > WCD934X_MAX_VALID_ADC_MUX || + adc_mux_n == WCD934X_INVALID_ADC_MUX) + return 0; + + if (adc_mux_n < 3) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + adc_mux_n; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 4) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x03; + shift = 0; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + 2 * adc_mux_n; + } else if (adc_mux_n < 7) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + (adc_mux_n - 4); + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 8) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x0C; + shift = 2; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 12) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + + ((adc_mux_n == 8) ? (adc_mux_n - 8) : + (adc_mux_n - 9)); + mask = 0x30; + shift = 4; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else if (adc_mux_n < 13) { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; + mask = 0x30; + shift = 4; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } else { + adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1; + mask = 0xC0; + shift = 6; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_n - 4; + } + + is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask) >> shift) + == 1); + if (!is_amic) + return 0; + + return snd_soc_read(codec, amic_mux_sel_reg) & 0x07; +} + +static void tavil_codec_set_tx_hold(struct snd_soc_codec *codec, + u16 amic_reg, bool set) +{ + u8 mask = 0x20; + u8 val; + + if (amic_reg == WCD934X_ANA_AMIC1 || + amic_reg == WCD934X_ANA_AMIC3) + mask = 0x40; + + val = set ? mask : 0x00; + + switch (amic_reg) { + case WCD934X_ANA_AMIC1: + case WCD934X_ANA_AMIC2: + snd_soc_update_bits(codec, WCD934X_ANA_AMIC2, mask, val); + break; + case WCD934X_ANA_AMIC3: + case WCD934X_ANA_AMIC4: + snd_soc_update_bits(codec, WCD934X_ANA_AMIC4, mask, val); + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic_reg); + break; + } +} + +static int tavil_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + int adc_mux_n = w->shift; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int amic_n; + + dev_dbg(codec->dev, "%s: event: %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + amic_n = tavil_codec_find_amic_input(codec, adc_mux_n); + if (amic_n) { + /* + * Prevent ANC Rx pop by leaving Tx FE in HOLD + * state until PA is up. Track AMIC being used + * so we can release the HOLD later. + */ + set_bit(ANC_MIC_AMIC1 + amic_n - 1, + &tavil->status_mask); + } + break; + default: + break; + } + + return 0; +} + +static u16 tavil_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, int amic) +{ + u16 pwr_level_reg = 0; + + switch (amic) { + case 1: + case 2: + pwr_level_reg = WCD934X_ANA_AMIC1; + break; + + case 3: + case 4: + pwr_level_reg = WCD934X_ANA_AMIC3; + break; + default: + dev_dbg(codec->dev, "%s: invalid amic: %d\n", + __func__, amic); + break; + } + + return pwr_level_reg; +} + +#define TX_HPF_CUT_OFF_FREQ_MASK 0x60 +#define CF_MIN_3DB_4HZ 0x0 +#define CF_MIN_3DB_75HZ 0x1 +#define CF_MIN_3DB_150HZ 0x2 + +static void tavil_tx_hpf_corner_freq_callback(struct work_struct *work) +{ + struct delayed_work *hpf_delayed_work; + struct hpf_work *hpf_work; + struct tavil_priv *tavil; + struct snd_soc_codec *codec; + u16 dec_cfg_reg, amic_reg, go_bit_reg; + u8 hpf_cut_off_freq; + int amic_n; + + hpf_delayed_work = to_delayed_work(work); + hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork); + tavil = hpf_work->tavil; + codec = tavil->codec; + hpf_cut_off_freq = hpf_work->hpf_cut_off_freq; + + dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator; + go_bit_reg = dec_cfg_reg + 7; + + dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n", + __func__, hpf_work->decimator, hpf_cut_off_freq); + + amic_n = tavil_codec_find_amic_input(codec, hpf_work->decimator); + if (amic_n) { + amic_reg = WCD934X_ANA_AMIC1 + amic_n - 1; + tavil_codec_set_tx_hold(codec, amic_reg, false); + } + snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x02); + /* Minimum 1 clk cycle delay is required as per HW spec */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x00); +} + +static void tavil_tx_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct tavil_priv *tavil; + struct delayed_work *delayed_work; + struct snd_soc_codec *codec; + u16 tx_vol_ctl_reg, hpf_gate_reg; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + tavil = tx_mute_dwork->tavil; + codec = tavil->codec; + + tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + + 16 * tx_mute_dwork->decimator; + hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + + 16 * tx_mute_dwork->decimator; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); +} + +static int tavil_codec_enable_rx_path_clk(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + u16 sidetone_reg; + + dev_dbg(codec->dev, "%s %d %d\n", __func__, event, w->shift); + sidetone_reg = WCD934X_CDC_RX0_RX_PATH_CFG1 + 0x14*(w->shift); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!strcmp(w->name, "RX INT7 MIX2 INP")) + __tavil_codec_enable_swr(w, event); + tavil_codec_enable_interp_clk(codec, event, w->shift); + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x00); + tavil_codec_enable_interp_clk(codec, event, w->shift); + if (!strcmp(w->name, "RX INT7 MIX2 INP")) + __tavil_codec_enable_swr(w, event); + break; + default: + break; + }; + return 0; +} + +static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + unsigned int decimator; + char *dec_adc_mux_name = NULL; + char *widget_name = NULL; + char *wname; + int ret = 0, amic_n; + u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg; + u16 tx_gain_ctl_reg; + char *dec; + u8 hpf_cut_off_freq; + + dev_dbg(codec->dev, "%s %d\n", __func__, event); + + widget_name = kstrndup(w->name, 15, GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + wname = widget_name; + dec_adc_mux_name = strsep(&widget_name, " "); + if (!dec_adc_mux_name) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, w->name); + ret = -EINVAL; + goto out; + } + dec_adc_mux_name = widget_name; + + dec = strpbrk(dec_adc_mux_name, "012345678"); + if (!dec) { + dev_err(codec->dev, "%s: decimator index not found\n", + __func__); + ret = -EINVAL; + goto out; + } + + ret = kstrtouint(dec, 10, &decimator); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid decimator = %s\n", + __func__, wname); + ret = -EINVAL; + goto out; + } + + dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__, + w->name, decimator); + + tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + 16 * decimator; + hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + 16 * decimator; + dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * decimator; + tx_gain_ctl_reg = WCD934X_CDC_TX0_TX_VOL_CTL + 16 * decimator; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + amic_n = tavil_codec_find_amic_input(codec, decimator); + if (amic_n) + pwr_level_reg = tavil_codec_get_amic_pwlvl_reg(codec, + amic_n); + + if (pwr_level_reg) { + switch ((snd_soc_read(codec, pwr_level_reg) & + WCD934X_AMIC_PWR_LVL_MASK) >> + WCD934X_AMIC_PWR_LVL_SHIFT) { + case WCD934X_AMIC_PWR_LEVEL_LP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_LP); + break; + + case WCD934X_AMIC_PWR_LEVEL_HP: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_HP); + break; + case WCD934X_AMIC_PWR_LEVEL_DEFAULT: + default: + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + } + } + /* Enable TX PGA Mute */ + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + break; + case SND_SOC_DAPM_POST_PMU: + hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) & + TX_HPF_CUT_OFF_FREQ_MASK) >> 5; + + tavil->tx_hpf_work[decimator].hpf_cut_off_freq = + hpf_cut_off_freq; + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + CF_MIN_3DB_150HZ << 5); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x00); + } + /* schedule work queue to Remove Mute */ + schedule_delayed_work(&tavil->tx_mute_dwork[decimator].dwork, + msecs_to_jiffies(tx_unmute_delay)); + if (tavil->tx_hpf_work[decimator].hpf_cut_off_freq != + CF_MIN_3DB_150HZ) + schedule_delayed_work( + &tavil->tx_hpf_work[decimator].dwork, + msecs_to_jiffies(300)); + /* apply gain after decimator is enabled */ + snd_soc_write(codec, tx_gain_ctl_reg, + snd_soc_read(codec, tx_gain_ctl_reg)); + break; + case SND_SOC_DAPM_PRE_PMD: + hpf_cut_off_freq = + tavil->tx_hpf_work[decimator].hpf_cut_off_freq; + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10); + if (cancel_delayed_work_sync( + &tavil->tx_hpf_work[decimator].dwork)) { + if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) { + snd_soc_update_bits(codec, dec_cfg_reg, + TX_HPF_CUT_OFF_FREQ_MASK, + hpf_cut_off_freq << 5); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x02); + /* + * Minimum 1 clk cycle delay is required as per + * HW spec. + */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, hpf_gate_reg, + 0x02, 0x00); + } + } + cancel_delayed_work_sync( + &tavil->tx_mute_dwork[decimator].dwork); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00); + snd_soc_update_bits(codec, dec_cfg_reg, + WCD934X_DEC_PWR_LVL_MASK, + WCD934X_DEC_PWR_LVL_DF); + break; + }; +out: + kfree(wname); + return ret; +} + +static u32 tavil_get_dmic_sample_rate(struct snd_soc_codec *codec, + unsigned int dmic, + struct wcd9xxx_pdata *pdata) +{ + u8 tx_stream_fs; + u8 adc_mux_index = 0, adc_mux_sel = 0; + bool dec_found = false; + u16 adc_mux_ctl_reg, tx_fs_reg; + u32 dmic_fs; + + while (dec_found == 0 && adc_mux_index < WCD934X_MAX_VALID_ADC_MUX) { + if (adc_mux_index < 4) { + adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + + (adc_mux_index * 2); + } else if (adc_mux_index < WCD934X_INVALID_ADC_MUX) { + adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + + adc_mux_index - 4; + } else if (adc_mux_index == WCD934X_INVALID_ADC_MUX) { + ++adc_mux_index; + continue; + } + adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) & + 0xF8) >> 3) - 1; + + if (adc_mux_sel == dmic) { + dec_found = true; + break; + } + + ++adc_mux_index; + } + + if (dec_found && adc_mux_index <= 8) { + tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index); + tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F; + if (tx_stream_fs <= 4) { + if (pdata->dmic_sample_rate <= + WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ) + dmic_fs = pdata->dmic_sample_rate; + else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ; + } else + dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + } else { + dmic_fs = pdata->dmic_sample_rate; + } + + return dmic_fs; +} + +static u8 tavil_get_dmic_clk_val(struct snd_soc_codec *codec, + u32 mclk_rate, u32 dmic_clk_rate) +{ + u32 div_factor; + u8 dmic_ctl_val; + + dev_dbg(codec->dev, + "%s: mclk_rate = %d, dmic_sample_rate = %d\n", + __func__, mclk_rate, dmic_clk_rate); + + /* Default value to return in case of error */ + if (mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2; + else + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3; + + if (dmic_clk_rate == 0) { + dev_err(codec->dev, + "%s: dmic_sample_rate cannot be 0\n", + __func__); + goto done; + } + + div_factor = mclk_rate / dmic_clk_rate; + switch (div_factor) { + case 2: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2; + break; + case 3: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3; + break; + case 4: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_4; + break; + case 6: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_6; + break; + case 8: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_8; + break; + case 16: + dmic_ctl_val = WCD934X_DMIC_CLK_DIV_16; + break; + default: + dev_err(codec->dev, + "%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n", + __func__, div_factor, mclk_rate, dmic_clk_rate); + break; + } + +done: + return dmic_ctl_val; +} + +static int tavil_codec_enable_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + dev_dbg(codec->dev, "%s: event:%d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + tavil_codec_set_tx_hold(codec, w->reg, true); + break; + default: + break; + } + + return 0; +} + +static int tavil_codec_enable_dmic(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent); + u8 dmic_clk_en = 0x01; + u16 dmic_clk_reg; + s32 *dmic_clk_cnt; + u8 dmic_rate_val, dmic_rate_shift = 1; + unsigned int dmic; + u32 dmic_sample_rate; + int ret; + char *wname; + + wname = strpbrk(w->name, "012345"); + if (!wname) { + dev_err(codec->dev, "%s: widget not found\n", __func__); + return -EINVAL; + } + + ret = kstrtouint(wname, 10, &dmic); + if (ret < 0) { + dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n", + __func__); + return -EINVAL; + } + + switch (dmic) { + case 0: + case 1: + dmic_clk_cnt = &(tavil->dmic_0_1_clk_cnt); + dmic_clk_reg = WCD934X_CPE_SS_DMIC0_CTL; + break; + case 2: + case 3: + dmic_clk_cnt = &(tavil->dmic_2_3_clk_cnt); + dmic_clk_reg = WCD934X_CPE_SS_DMIC1_CTL; + break; + case 4: + case 5: + dmic_clk_cnt = &(tavil->dmic_4_5_clk_cnt); + dmic_clk_reg = WCD934X_CPE_SS_DMIC2_CTL; + break; + default: + dev_err(codec->dev, "%s: Invalid DMIC Selection\n", + __func__); + return -EINVAL; + }; + dev_dbg(codec->dev, "%s: event %d DMIC%d dmic_clk_cnt %d\n", + __func__, event, dmic, *dmic_clk_cnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dmic_sample_rate = tavil_get_dmic_sample_rate(codec, dmic, + pdata); + dmic_rate_val = + tavil_get_dmic_clk_val(codec, + pdata->mclk_rate, + dmic_sample_rate); + + (*dmic_clk_cnt)++; + if (*dmic_clk_cnt == 1) { + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, dmic_clk_en); + } + + break; + case SND_SOC_DAPM_POST_PMD: + dmic_rate_val = + tavil_get_dmic_clk_val(codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + (*dmic_clk_cnt)--; + if (*dmic_clk_cnt == 0) { + snd_soc_update_bits(codec, dmic_clk_reg, + dmic_clk_en, 0); + snd_soc_update_bits(codec, dmic_clk_reg, + 0x07 << dmic_rate_shift, + dmic_rate_val << dmic_rate_shift); + } + break; + }; + + return 0; +} + +/* + * tavil_mbhc_micb_adjust_voltage: adjust specific micbias voltage + * @codec: handle to snd_soc_codec * + * @req_volt: micbias voltage to be set + * @micb_num: micbias to be set, e.g. micbias1 or micbias2 + * + * return 0 if adjustment is success or error code in case of failure + */ +int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, int micb_num) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int cur_vout_ctl, req_vout_ctl; + int micb_reg, micb_val, micb_en; + int ret = 0; + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD934X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD934X_ANA_MICB2; + break; + case MIC_BIAS_3: + micb_reg = WCD934X_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD934X_ANA_MICB4; + break; + default: + return -EINVAL; + } + mutex_lock(&tavil->micb_lock); + + /* + * If requested micbias voltage is same as current micbias + * voltage, then just return. Otherwise, adjust voltage as + * per requested value. If micbias is already enabled, then + * to avoid slow micbias ramp-up or down enable pull-up + * momentarily, change the micbias value and then re-enable + * micbias. + */ + micb_val = snd_soc_read(codec, micb_reg); + micb_en = (micb_val & 0xC0) >> 6; + cur_vout_ctl = micb_val & 0x3F; + + req_vout_ctl = wcd934x_get_micb_vout_ctl_val(req_volt); + if (req_vout_ctl < 0) { + ret = -EINVAL; + goto exit; + } + if (cur_vout_ctl == req_vout_ctl) { + ret = 0; + goto exit; + } + + dev_dbg(codec->dev, "%s: micb_num: %d, cur_mv: %d, req_mv: %d, micb_en: %d\n", + __func__, micb_num, WCD_VOUT_CTL_TO_MICB(cur_vout_ctl), + req_volt, micb_en); + + if (micb_en == 0x1) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + + snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl); + + if (micb_en == 0x1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + /* + * Add 2ms delay as per HW requirement after enabling + * micbias + */ + usleep_range(2000, 2100); + } +exit: + mutex_unlock(&tavil->micb_lock); + return ret; +} +EXPORT_SYMBOL(tavil_mbhc_micb_adjust_voltage); + +/* + * tavil_micbias_control: enable/disable micbias + * @codec: handle to snd_soc_codec * + * @micb_num: micbias to be enabled/disabled, e.g. micbias1 or micbias2 + * @req: control requested, enable/disable or pullup enable/disable + * @is_dapm: triggered by dapm or not + * + * return 0 if control is success or error code in case of failure + */ +int tavil_micbias_control(struct snd_soc_codec *codec, + int micb_num, int req, bool is_dapm) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int micb_index = micb_num - 1; + u16 micb_reg; + int pre_off_event = 0, post_off_event = 0; + int post_on_event = 0, post_dapm_off = 0; + int post_dapm_on = 0; + + if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + switch (micb_num) { + case MIC_BIAS_1: + micb_reg = WCD934X_ANA_MICB1; + break; + case MIC_BIAS_2: + micb_reg = WCD934X_ANA_MICB2; + pre_off_event = WCD_EVENT_PRE_MICBIAS_2_OFF; + post_off_event = WCD_EVENT_POST_MICBIAS_2_OFF; + post_on_event = WCD_EVENT_POST_MICBIAS_2_ON; + post_dapm_on = WCD_EVENT_POST_DAPM_MICBIAS_2_ON; + post_dapm_off = WCD_EVENT_POST_DAPM_MICBIAS_2_OFF; + break; + case MIC_BIAS_3: + micb_reg = WCD934X_ANA_MICB3; + break; + case MIC_BIAS_4: + micb_reg = WCD934X_ANA_MICB4; + break; + default: + dev_err(codec->dev, "%s: Invalid micbias number: %d\n", + __func__, micb_num); + return -EINVAL; + } + mutex_lock(&tavil->micb_lock); + + switch (req) { + case MICB_PULLUP_ENABLE: + tavil->pullup_ref[micb_index]++; + if ((tavil->pullup_ref[micb_index] == 1) && + (tavil->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + break; + case MICB_PULLUP_DISABLE: + if (tavil->pullup_ref[micb_index] > 0) + tavil->pullup_ref[micb_index]--; + if ((tavil->pullup_ref[micb_index] == 0) && + (tavil->micb_ref[micb_index] == 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + break; + case MICB_ENABLE: + tavil->micb_ref[micb_index]++; + if (tavil->micb_ref[micb_index] == 1) { + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40); + if (post_on_event && tavil->mbhc) + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + post_on_event, + &tavil->mbhc->wcd_mbhc); + } + if (is_dapm && post_dapm_on && tavil->mbhc) + blocking_notifier_call_chain(&tavil->mbhc->notifier, + post_dapm_on, &tavil->mbhc->wcd_mbhc); + break; + case MICB_DISABLE: + if (tavil->micb_ref[micb_index] > 0) + tavil->micb_ref[micb_index]--; + if ((tavil->micb_ref[micb_index] == 0) && + (tavil->pullup_ref[micb_index] > 0)) + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80); + else if ((tavil->micb_ref[micb_index] == 0) && + (tavil->pullup_ref[micb_index] == 0)) { + if (pre_off_event && tavil->mbhc) + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + pre_off_event, + &tavil->mbhc->wcd_mbhc); + snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00); + if (post_off_event && tavil->mbhc) + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + post_off_event, + &tavil->mbhc->wcd_mbhc); + } + if (is_dapm && post_dapm_off && tavil->mbhc) + blocking_notifier_call_chain(&tavil->mbhc->notifier, + post_dapm_off, &tavil->mbhc->wcd_mbhc); + break; + }; + + dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n", + __func__, micb_num, tavil->micb_ref[micb_index], + tavil->pullup_ref[micb_index]); + + mutex_unlock(&tavil->micb_lock); + + return 0; +} +EXPORT_SYMBOL(tavil_micbias_control); + +static int __tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + int micb_num; + + dev_dbg(codec->dev, "%s: wname: %s, event: %d\n", + __func__, w->name, event); + + if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) + micb_num = MIC_BIAS_1; + else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) + micb_num = MIC_BIAS_2; + else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) + micb_num = MIC_BIAS_3; + else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) + micb_num = MIC_BIAS_4; + else + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* + * MIC BIAS can also be requested by MBHC, + * so use ref count to handle micbias pullup + * and enable requests + */ + tavil_micbias_control(codec, micb_num, MICB_ENABLE, true); + break; + case SND_SOC_DAPM_POST_PMU: + /* wait for cnp time */ + usleep_range(1000, 1100); + break; + case SND_SOC_DAPM_POST_PMD: + tavil_micbias_control(codec, micb_num, MICB_DISABLE, true); + break; + }; + + return 0; +} + +/* + * tavil_codec_enable_standalone_micbias - enable micbias standalone + * @codec: pointer to codec instance + * @micb_num: number of micbias to be enabled + * @enable: true to enable micbias or false to disable + * + * This function is used to enable micbias (1, 2, 3 or 4) during + * standalone independent of whether TX use-case is running or not + * + * Return: error code in case of failure or 0 for success + */ +int tavil_codec_enable_standalone_micbias(struct snd_soc_codec *codec, + int micb_num, + bool enable) +{ + const char * const micb_names[] = { + DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE, + DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE + }; + int micb_index = micb_num - 1; + int rc; + + if (!codec) { + pr_err("%s: Codec memory is NULL\n", __func__); + return -EINVAL; + } + + if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) { + dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n", + __func__, micb_index); + return -EINVAL; + } + + if (enable) + rc = snd_soc_dapm_force_enable_pin( + snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + else + rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec), + micb_names[micb_index]); + + if (!rc) + snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec)); + else + dev_err(codec->dev, "%s: micbias%d force %s pin failed\n", + __func__, micb_num, (enable ? "enable" : "disable")); + + return rc; +} +EXPORT_SYMBOL(tavil_codec_enable_standalone_micbias); + +static int tavil_codec_force_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wcd_resmgr_enable_master_bias(tavil->resmgr); + tavil_cdc_mclk_enable(codec, true); + ret = __tavil_codec_enable_micbias(w, SND_SOC_DAPM_PRE_PMU); + /* Wait for 1ms for better cnp */ + usleep_range(1000, 1100); + tavil_cdc_mclk_enable(codec, false); + break; + case SND_SOC_DAPM_POST_PMD: + ret = __tavil_codec_enable_micbias(w, SND_SOC_DAPM_POST_PMD); + wcd_resmgr_disable_master_bias(tavil->resmgr); + break; + } + + return ret; +} + +static int tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + return __tavil_codec_enable_micbias(w, event); +} + + +static const struct reg_sequence tavil_hph_reset_tbl[] = { + { WCD934X_HPH_CNP_EN, 0x80 }, + { WCD934X_HPH_CNP_WG_CTL, 0x9A }, + { WCD934X_HPH_CNP_WG_TIME, 0x14 }, + { WCD934X_HPH_OCP_CTL, 0x28 }, + { WCD934X_HPH_AUTO_CHOP, 0x16 }, + { WCD934X_HPH_CHOP_CTL, 0x83 }, + { WCD934X_HPH_PA_CTL1, 0x46 }, + { WCD934X_HPH_PA_CTL2, 0x50 }, + { WCD934X_HPH_L_EN, 0x80 }, + { WCD934X_HPH_L_TEST, 0xE0 }, + { WCD934X_HPH_L_ATEST, 0x50 }, + { WCD934X_HPH_R_EN, 0x80 }, + { WCD934X_HPH_R_TEST, 0xE0 }, + { WCD934X_HPH_R_ATEST, 0x54 }, + { WCD934X_HPH_RDAC_CLK_CTL1, 0x99 }, + { WCD934X_HPH_RDAC_CLK_CTL2, 0x9B }, + { WCD934X_HPH_RDAC_LDO_CTL, 0x33 }, + { WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 }, + { WCD934X_HPH_REFBUFF_UHQA_CTL, 0xA8 }, +}; + +static const struct reg_sequence tavil_hph_reset_tbl_1_0[] = { + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0A }, + { WCD934X_HPH_L_DAC_CTL, 0x00 }, + { WCD934X_HPH_R_DAC_CTL, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0xA0 }, + { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE }, + { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e}, + { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, +}; + +static const struct reg_sequence tavil_hph_reset_tbl_1_1[] = { + { WCD934X_HPH_REFBUFF_LP_CTL, 0x0E }, + { WCD934X_HPH_L_DAC_CTL, 0x00 }, + { WCD934X_HPH_R_DAC_CTL, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH2, 0x00 }, + { WCD934X_HPH_NEW_ANA_HPH3, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 }, + { WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0x81 }, + { WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 }, + { WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 }, + { WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x81 }, + { WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE }, + { WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 }, + { WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e}, + { WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 }, +}; + +static const struct tavil_reg_mask_val tavil_pa_disable[] = { + { WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x10 }, /* RX1 mute enable */ + { WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x10 }, /* RX2 mute enable */ + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, /* GM3 boost disable */ + { WCD934X_ANA_HPH, 0x80, 0x00 }, /* HPHL PA disable */ + { WCD934X_ANA_HPH, 0x40, 0x00 }, /* HPHR PA disable */ + { WCD934X_ANA_HPH, 0x20, 0x00 }, /* HPHL REF dsable */ + { WCD934X_ANA_HPH, 0x10, 0x00 }, /* HPHR REF disable */ +}; + +static const struct tavil_reg_mask_val tavil_ocp_en_seq[] = { + { WCD934X_RX_OCP_CTL, 0x0F, 0x02 }, /* OCP number of attempts is 2 */ + { WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */ + { WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */ + { WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */ +}; + +static const struct tavil_reg_mask_val tavil_ocp_en_seq_1[] = { + { WCD934X_RX_OCP_CTL, 0x0F, 0x02 }, /* OCP number of attempts is 2 */ + { WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */ +}; + +/* LO-HIFI */ +static const struct tavil_reg_mask_val tavil_pre_pa_en_lohifi[] = { + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 }, + { WCD934X_FLYBACK_VNEG_CTRL_4, 0xf0, 0x80 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x20 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 }, + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, + { WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0xc0 }, + { WCD934X_HPH_PA_CTL1, 0x0e, 0x02 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 }, +}; + +static const struct tavil_reg_mask_val tavil_pre_pa_en[] = { + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 }, + { WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x0 }, + { WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 }, + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, + { WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0x80 }, + { WCD934X_HPH_PA_CTL1, 0x0e, 0x06 }, + { WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 }, +}; + +static const struct tavil_reg_mask_val tavil_post_pa_en[] = { + { WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */ + { WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */ + { WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x20 }, /* RX1 mute disable */ + { WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x20 }, /* RX2 mute disable */ + { WCD934X_HPH_CNP_WG_CTL, 0x80, 0x80 }, /* GM3 boost enable */ + { WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x02 }, +}; + +static void tavil_codec_hph_reg_range_read(struct regmap *map, u8 *buf) +{ + regmap_bulk_read(map, WCD934X_HPH_CNP_EN, buf, TAVIL_HPH_REG_RANGE_1); + regmap_bulk_read(map, WCD934X_HPH_NEW_ANA_HPH2, + buf + TAVIL_HPH_REG_RANGE_1, TAVIL_HPH_REG_RANGE_2); + regmap_bulk_read(map, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, + buf + TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2, + TAVIL_HPH_REG_RANGE_3); +} + +static void tavil_codec_hph_reg_recover(struct tavil_priv *tavil, + struct regmap *map, int pa_status) +{ + int i; + unsigned int reg; + + blocking_notifier_call_chain(&tavil->mbhc->notifier, + WCD_EVENT_OCP_OFF, + &tavil->mbhc->wcd_mbhc); + + if (pa_status & 0xC0) + goto pa_en_restore; + + dev_dbg(tavil->dev, "%s: HPH PA in disable state (0x%x)\n", + __func__, pa_status); + + regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x10); + regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x10); + regmap_write_bits(map, WCD934X_ANA_HPH, 0xC0, 0x00); + regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x00); + regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x00); + regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x00); + + /* Restore to HW defaults */ + regmap_multi_reg_write(map, tavil_hph_reset_tbl, + ARRAY_SIZE(tavil_hph_reset_tbl)); + if (TAVIL_IS_1_1(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_1, + ARRAY_SIZE(tavil_hph_reset_tbl_1_1)); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_0, + ARRAY_SIZE(tavil_hph_reset_tbl_1_0)); + + for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq); i++) + regmap_write_bits(map, tavil_ocp_en_seq[i].reg, + tavil_ocp_en_seq[i].mask, + tavil_ocp_en_seq[i].val); + goto end; + + +pa_en_restore: + dev_dbg(tavil->dev, "%s: HPH PA in enable state (0x%x)\n", + __func__, pa_status); + + /* Disable PA and other registers before restoring */ + for (i = 0; i < ARRAY_SIZE(tavil_pa_disable); i++) { + if (TAVIL_IS_1_1(tavil->wcd9xxx) && + (tavil_pa_disable[i].reg == WCD934X_HPH_CNP_WG_CTL)) + continue; + regmap_write_bits(map, tavil_pa_disable[i].reg, + tavil_pa_disable[i].mask, + tavil_pa_disable[i].val); + } + + regmap_multi_reg_write(map, tavil_hph_reset_tbl, + ARRAY_SIZE(tavil_hph_reset_tbl)); + if (TAVIL_IS_1_1(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_1, + ARRAY_SIZE(tavil_hph_reset_tbl_1_1)); + if (TAVIL_IS_1_0(tavil->wcd9xxx)) + regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_0, + ARRAY_SIZE(tavil_hph_reset_tbl_1_0)); + + for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq_1); i++) + regmap_write_bits(map, tavil_ocp_en_seq_1[i].reg, + tavil_ocp_en_seq_1[i].mask, + tavil_ocp_en_seq_1[i].val); + + if (tavil->hph_mode == CLS_H_LOHIFI) { + for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en_lohifi); i++) { + reg = tavil_pre_pa_en_lohifi[i].reg; + if ((TAVIL_IS_1_1(tavil->wcd9xxx)) && + ((reg == WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL) || + (reg == WCD934X_HPH_CNP_WG_CTL) || + (reg == WCD934X_HPH_REFBUFF_LP_CTL))) + continue; + regmap_write_bits(map, + tavil_pre_pa_en_lohifi[i].reg, + tavil_pre_pa_en_lohifi[i].mask, + tavil_pre_pa_en_lohifi[i].val); + } + } else { + for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en); i++) { + reg = tavil_pre_pa_en[i].reg; + if ((TAVIL_IS_1_1(tavil->wcd9xxx)) && + ((reg == WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL) || + (reg == WCD934X_HPH_CNP_WG_CTL) || + (reg == WCD934X_HPH_REFBUFF_LP_CTL))) + continue; + regmap_write_bits(map, tavil_pre_pa_en[i].reg, + tavil_pre_pa_en[i].mask, + tavil_pre_pa_en[i].val); + } + } + + if (TAVIL_IS_1_1(tavil->wcd9xxx)) { + regmap_write(map, WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x84); + regmap_write(map, WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x84); + } + + regmap_write_bits(map, WCD934X_ANA_HPH, 0x0C, pa_status & 0x0C); + regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x30); + /* wait for 100usec after HPH DAC is enabled */ + usleep_range(100, 110); + regmap_write(map, WCD934X_ANA_HPH, pa_status); + /* Sleep for 7msec after PA is enabled */ + usleep_range(7000, 7100); + + for (i = 0; i < ARRAY_SIZE(tavil_post_pa_en); i++) { + if ((TAVIL_IS_1_1(tavil->wcd9xxx)) && + (tavil_post_pa_en[i].reg == WCD934X_HPH_CNP_WG_CTL)) + continue; + regmap_write_bits(map, tavil_post_pa_en[i].reg, + tavil_post_pa_en[i].mask, + tavil_post_pa_en[i].val); + } + +end: + tavil->mbhc->is_hph_recover = true; + blocking_notifier_call_chain( + &tavil->mbhc->notifier, + WCD_EVENT_OCP_ON, + &tavil->mbhc->wcd_mbhc); +} + +static int tavil_codec_reset_hph_registers(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + u8 cache_val[TAVIL_HPH_TOTAL_REG]; + u8 hw_val[TAVIL_HPH_TOTAL_REG]; + int pa_status; + int ret; + + dev_dbg(wcd9xxx->dev, "%s: event: %d\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + memset(cache_val, 0, TAVIL_HPH_TOTAL_REG); + memset(hw_val, 0, TAVIL_HPH_TOTAL_REG); + + regmap_read(wcd9xxx->regmap, WCD934X_ANA_HPH, &pa_status); + + tavil_codec_hph_reg_range_read(wcd9xxx->regmap, cache_val); + + /* Read register values from HW directly */ + regcache_cache_bypass(wcd9xxx->regmap, true); + tavil_codec_hph_reg_range_read(wcd9xxx->regmap, hw_val); + regcache_cache_bypass(wcd9xxx->regmap, false); + + /* compare both the registers to know if there is corruption */ + ret = memcmp(cache_val, hw_val, TAVIL_HPH_TOTAL_REG); + + /* If both the values are same, it means no corruption */ + if (ret) { + dev_dbg(codec->dev, "%s: cache and hw reg are not same\n", + __func__); + tavil_codec_hph_reg_recover(tavil, wcd9xxx->regmap, + pa_status); + } else { + dev_dbg(codec->dev, "%s: cache and hw reg are same\n", + __func__); + tavil->mbhc->is_hph_recover = false; + } + break; + default: + break; + }; + + return 0; +} + +static int tavil_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + /* IIR filter band registers are at integer multiples of 16 */ + u16 iir_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0; + + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0]); + return 0; +} + +static int tavil_iir_enable_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + bool iir_band_en_status; + int value = ucontrol->value.integer.value[0]; + u16 iir_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + + /* Mask first 5 bits, 6-8 are reserved */ + snd_soc_update_bits(codec, iir_reg, (1 << band_idx), + (value << band_idx)); + + iir_band_en_status = ((snd_soc_read(codec, iir_reg) & + (1 << band_idx)) != 0); + dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__, + iir_idx, band_idx, iir_band_en_status); + return 0; +} + +static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + int coeff_idx) +{ + uint32_t value = 0; + + /* Address does not automatically update if reading */ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t)) & 0x7F); + + value |= snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx)); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 1) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 8); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 2) & 0x7F); + + value |= (snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) << 16); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + ((band_idx * BAND_MAX + coeff_idx) + * sizeof(uint32_t) + 3) & 0x7F); + + /* Mask bits top 2 bits since they are reserved */ + value |= ((snd_soc_read(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + + 16 * iir_idx)) & 0x3F) << 24); + + return value; +} + +static int tavil_iir_band_audio_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + ucontrol->value.integer.value[0] = + get_iir_band_coeff(codec, iir_idx, band_idx, 0); + ucontrol->value.integer.value[1] = + get_iir_band_coeff(codec, iir_idx, band_idx, 1); + ucontrol->value.integer.value[2] = + get_iir_band_coeff(codec, iir_idx, band_idx, 2); + ucontrol->value.integer.value[3] = + get_iir_band_coeff(codec, iir_idx, band_idx, 3); + ucontrol->value.integer.value[4] = + get_iir_band_coeff(codec, iir_idx, band_idx, 4); + + dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[0], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[1], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[2], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[3], + __func__, iir_idx, band_idx, + (uint32_t)ucontrol->value.integer.value[4]); + return 0; +} + +static void set_iir_band_coeff(struct snd_soc_codec *codec, + int iir_idx, int band_idx, + uint32_t value) +{ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value & 0xFF)); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 8) & 0xFF); + + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 16) & 0xFF); + + /* Mask top 2 bits, 7-8 are reserved */ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx), + (value >> 24) & 0x3F); +} + +static int tavil_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int iir_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->reg; + int band_idx = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int coeff_idx; + + /* + * Mask top bit it is reserved + * Updates addr automatically for each B2 write + */ + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + /* Store the coefficients in sidetone coeff array */ + for (coeff_idx = 0; coeff_idx < WCD934X_CDC_SIDETONE_IIR_COEFF_MAX; + coeff_idx++) { + tavil->sidetone_coeff_array[iir_idx][band_idx][coeff_idx] = + ucontrol->value.integer.value[coeff_idx]; + set_iir_band_coeff(codec, iir_idx, band_idx, + tavil->sidetone_coeff_array[iir_idx][band_idx] + [coeff_idx]); + } + + pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" + "%s: IIR #%d band #%d b1 = 0x%x\n" + "%s: IIR #%d band #%d b2 = 0x%x\n" + "%s: IIR #%d band #%d a1 = 0x%x\n" + "%s: IIR #%d band #%d a2 = 0x%x\n", + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 0), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 1), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 2), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 3), + __func__, iir_idx, band_idx, + get_iir_band_coeff(codec, iir_idx, band_idx, 4)); + return 0; +} + +static void tavil_restore_iir_coeff(struct tavil_priv *tavil, int iir_idx) +{ + int band_idx = 0, coeff_idx = 0; + struct snd_soc_codec *codec = tavil->codec; + + for (band_idx = 0; band_idx < BAND_MAX; band_idx++) { + snd_soc_write(codec, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + for (coeff_idx = 0; + coeff_idx < WCD934X_CDC_SIDETONE_IIR_COEFF_MAX; + coeff_idx++) { + set_iir_band_coeff(codec, iir_idx, band_idx, + tavil->sidetone_coeff_array[iir_idx][band_idx] + [coeff_idx]); + } + } +} + +static int tavil_compander_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->comp_enabled[comp]; + return 0; +} + +static int tavil_compander_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, tavil->comp_enabled[comp], value); + tavil->comp_enabled[comp] = value; + + /* Any specific register configuration for compander */ + switch (comp) { + case COMPANDER_1: + /* Set Gain Source Select based on compander enable/disable */ + snd_soc_update_bits(codec, WCD934X_HPH_L_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_2: + snd_soc_update_bits(codec, WCD934X_HPH_R_EN, 0x20, + (value ? 0x00:0x20)); + break; + case COMPANDER_3: + case COMPANDER_4: + case COMPANDER_7: + case COMPANDER_8: + break; + default: + /* + * if compander is not enabled for any interpolator, + * it does not cause any audio failure, so do not + * return error in this case, but just print a log + */ + dev_warn(codec->dev, "%s: unknown compander: %d\n", + __func__, comp); + }; + return 0; +} + +static int tavil_hph_asrc_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int index = -EINVAL; + + if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode")) + index = ASRC0; + if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode")) + index = ASRC1; + + if (tavil && (index >= 0) && (index < ASRC_MAX)) + tavil->asrc_output_mode[index] = + ucontrol->value.integer.value[0]; + + return 0; +} + +static int tavil_hph_asrc_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int val = 0; + int index = -EINVAL; + + if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode")) + index = ASRC0; + if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode")) + index = ASRC1; + + if (tavil && (index >= 0) && (index < ASRC_MAX)) + val = tavil->asrc_output_mode[index]; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tavil_hph_idle_detect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int val = 0; + + if (tavil) + val = tavil->idle_det_cfg.hph_idle_detect_en; + + ucontrol->value.integer.value[0] = val; + + return 0; +} + +static int tavil_hph_idle_detect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + if (tavil) + tavil->idle_det_cfg.hph_idle_detect_en = + ucontrol->value.integer.value[0]; + + return 0; +} + +static int tavil_dmic_pin_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 dmic_pin; + u8 reg_val, pinctl_position; + + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + dmic_pin = pinctl_position & 0x07; + reg_val = snd_soc_read(codec, + WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1); + + ucontrol->value.integer.value[0] = !!reg_val; + + return 0; +} + +static int tavil_dmic_pin_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u16 ctl_reg, cfg_reg, dmic_pin; + u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask; + + /* 0- high or low; 1- high Z */ + pinctl_mode = ucontrol->value.integer.value[0]; + pinctl_position = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + + switch (pinctl_position >> 3) { + case 0: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_0; + break; + case 1: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_1; + break; + case 2: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_2; + break; + case 3: + ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_3; + break; + default: + dev_err(codec->dev, "%s: Invalid pinctl position = %d\n", + __func__, pinctl_position); + return -EINVAL; + } + + ctl_val = ~(pinctl_mode << (pinctl_position & 0x07)); + mask = 1 << (pinctl_position & 0x07); + snd_soc_update_bits(codec, ctl_reg, mask, ctl_val); + + dmic_pin = pinctl_position & 0x07; + cfg_reg = WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1; + if (pinctl_mode) { + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + cfg_val = 0x6; + else + cfg_val = 0xD; + } else + cfg_val = 0; + snd_soc_update_bits(codec, cfg_reg, 0x1F, cfg_val); + + dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n", + __func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val); + + return 0; +} + +static int tavil_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u16 amic_reg = 0; + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC3; + + if (amic_reg) + ucontrol->value.integer.value[0] = + (snd_soc_read(codec, amic_reg) & + WCD934X_AMIC_PWR_LVL_MASK) >> + WCD934X_AMIC_PWR_LVL_SHIFT; + return 0; +} + +static int tavil_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u32 mode_val; + u16 amic_reg = 0; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val); + + if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC1; + if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE")) + amic_reg = WCD934X_ANA_AMIC3; + + if (amic_reg) + snd_soc_update_bits(codec, amic_reg, WCD934X_AMIC_PWR_LVL_MASK, + mode_val << WCD934X_AMIC_PWR_LVL_SHIFT); + return 0; +} + +static const char *const tavil_conn_mad_text[] = { + "NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "NOTUSED5", + "NOTUSED6", "NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3", + "DMIC4", "DMIC5", "NOTUSED3", "NOTUSED4" +}; + +static const struct soc_enum tavil_conn_mad_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tavil_conn_mad_text), + tavil_conn_mad_text); + +static int tavil_mad_input_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 tavil_mad_input; + + tavil_mad_input = snd_soc_read(codec, WCD934X_SOC_MAD_INP_SEL) & 0x0F; + ucontrol->value.integer.value[0] = tavil_mad_input; + + dev_dbg(codec->dev, "%s: tavil_mad_input = %s\n", __func__, + tavil_conn_mad_text[tavil_mad_input]); + + return 0; +} + +static int tavil_mad_input_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + u8 tavil_mad_input; + char mad_amic_input_widget[6]; + const char *mad_input_widget; + const char *source_widget = NULL; + u32 adc, i, mic_bias_found = 0; + int ret = 0; + char *mad_input; + bool is_adc_input = false; + + tavil_mad_input = ucontrol->value.integer.value[0]; + + if (tavil_mad_input >= sizeof(tavil_conn_mad_text)/ + sizeof(tavil_conn_mad_text[0])) { + dev_err(codec->dev, + "%s: tavil_mad_input = %d out of bounds\n", + __func__, tavil_mad_input); + return -EINVAL; + } + + if (strnstr(tavil_conn_mad_text[tavil_mad_input], "NOTUSED", + sizeof("NOTUSED"))) { + dev_dbg(codec->dev, + "%s: Unsupported tavil_mad_input = %s\n", + __func__, tavil_conn_mad_text[tavil_mad_input]); + /* Make sure the MAD register is updated */ + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x88, 0x00); + return -EINVAL; + } + + if (strnstr(tavil_conn_mad_text[tavil_mad_input], + "ADC", sizeof("ADC"))) { + mad_input = strpbrk(tavil_conn_mad_text[tavil_mad_input], + "1234"); + if (!mad_input) { + dev_err(codec->dev, "%s: Invalid MAD input %s\n", + __func__, tavil_conn_mad_text[tavil_mad_input]); + return -EINVAL; + } + + ret = kstrtouint(mad_input, 10, &adc); + if ((ret < 0) || (adc > 4)) { + dev_err(codec->dev, "%s: Invalid ADC = %s\n", __func__, + tavil_conn_mad_text[tavil_mad_input]); + return -EINVAL; + } + + /*AMIC4 and AMIC5 share ADC4*/ + if ((adc == 4) && + (snd_soc_read(codec, WCD934X_TX_NEW_AMIC_4_5_SEL) & 0x10)) + adc = 5; + + snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc); + + mad_input_widget = mad_amic_input_widget; + is_adc_input = true; + } else { + /* DMIC type input widget*/ + mad_input_widget = tavil_conn_mad_text[tavil_mad_input]; + } + + dev_dbg(codec->dev, + "%s: tavil input widget = %s, adc_input = %s\n", __func__, + mad_input_widget, is_adc_input ? "true" : "false"); + + for (i = 0; i < card->num_of_dapm_routes; i++) { + if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) { + source_widget = card->of_dapm_routes[i].source; + if (!source_widget) { + dev_err(codec->dev, + "%s: invalid source widget\n", + __func__); + return -EINVAL; + } + + if (strnstr(source_widget, + "MIC BIAS1", sizeof("MIC BIAS1"))) { + mic_bias_found = 1; + break; + } else if (strnstr(source_widget, + "MIC BIAS2", sizeof("MIC BIAS2"))) { + mic_bias_found = 2; + break; + } else if (strnstr(source_widget, + "MIC BIAS3", sizeof("MIC BIAS3"))) { + mic_bias_found = 3; + break; + } else if (strnstr(source_widget, + "MIC BIAS4", sizeof("MIC BIAS4"))) { + mic_bias_found = 4; + break; + } + } + } + + if (!mic_bias_found) { + dev_err(codec->dev, "%s: mic bias not found for input %s\n", + __func__, mad_input_widget); + return -EINVAL; + } + + dev_dbg(codec->dev, "%s: mic_bias found = %d\n", __func__, + mic_bias_found); + + snd_soc_update_bits(codec, WCD934X_SOC_MAD_INP_SEL, + 0x0F, tavil_mad_input); + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x07, mic_bias_found); + /* for all adc inputs, mad should be in micbias mode with BG enabled */ + if (is_adc_input) + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x88, 0x88); + else + snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP, + 0x88, 0x00); + return 0; +} + +static int tavil_ear_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + ear_pa_gain = snd_soc_read(codec, WCD934X_ANA_EAR); + + ear_pa_gain = (ear_pa_gain & 0x70) >> 4; + + ucontrol->value.integer.value[0] = ear_pa_gain; + + dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__, + ear_pa_gain); + + return 0; +} + +static int tavil_ear_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 ear_pa_gain; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + ear_pa_gain = ucontrol->value.integer.value[0] << 4; + + snd_soc_update_bits(codec, WCD934X_ANA_EAR, 0x70, ear_pa_gain); + return 0; +} + +static int tavil_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->ear_spkr_gain; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tavil_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + tavil->ear_spkr_gain = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: gain = %d\n", __func__, tavil->ear_spkr_gain); + + return 0; +} + +static int tavil_rx_hph_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil->hph_mode; + return 0; +} + +static int tavil_rx_hph_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u32 mode_val; + + mode_val = ucontrol->value.enumerated.item[0]; + + dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val); + + if (mode_val == 0) { + dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H LOHiFi\n", + __func__); + mode_val = CLS_H_LOHIFI; + } + tavil->hph_mode = mode_val; + return 0; +} + +static const char * const rx_hph_mode_mux_text[] = { + "CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI", + "CLS_H_ULP", "CLS_AB_HIFI", +}; + +static const struct soc_enum rx_hph_mode_mux_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text), + rx_hph_mode_mux_text); + +static const char *const tavil_anc_func_text[] = {"OFF", "ON"}; +static const struct soc_enum tavil_anc_func_enum = + SOC_ENUM_SINGLE_EXT(2, tavil_anc_func_text); + +/* Cutoff frequency for high pass filter */ +static const char * const cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" +}; + +static const char * const rx_cf_text[] = { + "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ", + "CF_NEG_3DB_0P48HZ" +}; + +static const char * const amic_pwr_lvl_text[] = { + "LOW_PWR", "DEFAULT", "HIGH_PERF" +}; + +static const char * const hph_idle_detect_text[] = { + "OFF", "ON" +}; + +static const char * const asrc_mode_text[] = { + "INT", "FRAC" +}; + +static const char * const tavil_ear_pa_gain_text[] = { + "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", + "G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB" +}; + +static const char * const tavil_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", + "G_4_DB", "G_5_DB", "G_6_DB" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_pa_gain_enum, tavil_ear_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_spkr_pa_gain_enum, + tavil_ear_spkr_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(amic_pwr_lvl_enum, amic_pwr_lvl_text); +static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text); +static SOC_ENUM_SINGLE_EXT_DECL(asrc_mode_enum, asrc_mode_text); +static SOC_ENUM_SINGLE_DECL(cf_dec0_enum, WCD934X_CDC_TX0_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec1_enum, WCD934X_CDC_TX1_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec2_enum, WCD934X_CDC_TX2_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec3_enum, WCD934X_CDC_TX3_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec4_enum, WCD934X_CDC_TX4_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec5_enum, WCD934X_CDC_TX5_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec6_enum, WCD934X_CDC_TX6_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec7_enum, WCD934X_CDC_TX7_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_dec8_enum, WCD934X_CDC_TX8_TX_PATH_CFG0, 5, + cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int0_1_enum, WCD934X_CDC_RX0_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int1_1_enum, WCD934X_CDC_RX1_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int2_1_enum, WCD934X_CDC_RX2_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int3_1_enum, WCD934X_CDC_RX3_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int4_1_enum, WCD934X_CDC_RX4_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int7_1_enum, WCD934X_CDC_RX7_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 2, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int8_1_enum, WCD934X_CDC_RX8_RX_PATH_CFG2, 0, + rx_cf_text); +static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 2, + rx_cf_text); + +static const struct snd_kcontrol_new tavil_snd_controls[] = { + SOC_ENUM_EXT("EAR PA Gain", tavil_ear_pa_gain_enum, + tavil_ear_pa_gain_get, tavil_ear_pa_gain_put), + SOC_ENUM_EXT("EAR SPKR PA Gain", tavil_ear_spkr_pa_gain_enum, + tavil_ear_spkr_pa_gain_get, tavil_ear_spkr_pa_gain_put), + SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("LINEOUT2 Volume", WCD934X_DIFF_LO_LO2_COMPANDER, + 3, 16, 1, line_gain), + SOC_SINGLE_TLV("ADC1 Volume", WCD934X_ANA_AMIC1, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC2 Volume", WCD934X_ANA_AMIC2, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC3 Volume", WCD934X_ANA_AMIC3, 0, 20, 0, analog_gain), + SOC_SINGLE_TLV("ADC4 Volume", WCD934X_ANA_AMIC4, 0, 20, 0, analog_gain), + + SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD934X_CDC_RX0_RX_VOL_CTL, + 0, -84, 40, digital_gain), /* -84dB min - 40dB max */ + SOC_SINGLE_SX_TLV("RX1 Digital Volume", WCD934X_CDC_RX1_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Digital Volume", WCD934X_CDC_RX2_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Digital Volume", WCD934X_CDC_RX3_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX4 Digital Volume", WCD934X_CDC_RX4_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Digital Volume", WCD934X_CDC_RX7_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Digital Volume", WCD934X_CDC_RX8_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX0 Mix Digital Volume", + WCD934X_CDC_RX0_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX1 Mix Digital Volume", + WCD934X_CDC_RX1_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX2 Mix Digital Volume", + WCD934X_CDC_RX2_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX3 Mix Digital Volume", + WCD934X_CDC_RX3_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX4 Mix Digital Volume", + WCD934X_CDC_RX4_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume", + WCD934X_CDC_RX7_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume", + WCD934X_CDC_RX8_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("DEC0 Volume", WCD934X_CDC_TX0_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC1 Volume", WCD934X_CDC_TX1_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC2 Volume", WCD934X_CDC_TX2_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC3 Volume", WCD934X_CDC_TX3_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC4 Volume", WCD934X_CDC_TX4_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC5 Volume", WCD934X_CDC_TX5_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC6 Volume", WCD934X_CDC_TX6_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC7 Volume", WCD934X_CDC_TX7_TX_VOL_CTL, 0, + -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("DEC8 Volume", WCD934X_CDC_TX8_TX_VOL_CTL, 0, + -84, 40, digital_gain), + + SOC_SINGLE_SX_TLV("IIR0 INP0 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP1 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP2 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR0 INP3 Volume", + WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP0 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84, 40, + digital_gain), + SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", + WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, 40, + digital_gain), + + SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tavil_get_anc_slot, + tavil_put_anc_slot), + SOC_ENUM_EXT("ANC Function", tavil_anc_func_enum, tavil_get_anc_func, + tavil_put_anc_func), + + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), + SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), + SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), + SOC_ENUM("TX3 HPF cut off", cf_dec3_enum), + SOC_ENUM("TX4 HPF cut off", cf_dec4_enum), + SOC_ENUM("TX5 HPF cut off", cf_dec5_enum), + SOC_ENUM("TX6 HPF cut off", cf_dec6_enum), + SOC_ENUM("TX7 HPF cut off", cf_dec7_enum), + SOC_ENUM("TX8 HPF cut off", cf_dec8_enum), + + SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum), + SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum), + SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum), + SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum), + SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum), + SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum), + SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum), + SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum), + SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum), + SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum), + SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum), + SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum), + SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum), + SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum), + + SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum, + tavil_rx_hph_mode_get, tavil_rx_hph_mode_put), + + SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0, + tavil_iir_enable_audio_mixer_get, + tavil_iir_enable_audio_mixer_put), + + SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5, + tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put), + + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0, + tavil_compander_get, tavil_compander_put), + SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0, + tavil_compander_get, tavil_compander_put), + + SOC_ENUM_EXT("ASRC0 Output Mode", asrc_mode_enum, + tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put), + SOC_ENUM_EXT("ASRC1 Output Mode", asrc_mode_enum, + tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put), + + SOC_ENUM_EXT("HPH Idle Detect", hph_idle_detect_enum, + tavil_hph_idle_detect_get, tavil_hph_idle_detect_put), + + SOC_ENUM_EXT("MAD Input", tavil_conn_mad_enum, + tavil_mad_input_get, tavil_mad_input_put), + + SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC1_DATA_PIN_MODE", SND_SOC_NOPM, 18, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC2_CLK_PIN_MODE", SND_SOC_NOPM, 19, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC2_DATA_PIN_MODE", SND_SOC_NOPM, 20, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC3_CLK_PIN_MODE", SND_SOC_NOPM, 21, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + + SOC_SINGLE_EXT("DMIC3_DATA_PIN_MODE", SND_SOC_NOPM, 22, 1, 0, + tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put), + SOC_ENUM_EXT("AMIC_1_2 PWR MODE", amic_pwr_lvl_enum, + tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_3_4 PWR MODE", amic_pwr_lvl_enum, + tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put), + SOC_ENUM_EXT("AMIC_5_6 PWR MODE", amic_pwr_lvl_enum, + tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put), +}; + +static int tavil_dec_enum_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 snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + u16 mic_sel_reg = 0; + u8 mic_sel; + + val = ucontrol->value.enumerated.item[0]; + if (val > e->items - 1) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + switch (e->reg) { + case WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX0_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX4_TX_PATH_CFG0; + else if (e->shift_l == 4) + mic_sel_reg = WCD934X_CDC_TX8_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX1_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX5_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX2_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX6_TX_PATH_CFG0; + break; + case WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1: + if (e->shift_l == 0) + mic_sel_reg = WCD934X_CDC_TX3_TX_PATH_CFG0; + else if (e->shift_l == 2) + mic_sel_reg = WCD934X_CDC_TX7_TX_PATH_CFG0; + break; + default: + dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n", + __func__, e->reg); + return -EINVAL; + } + + /* ADC: 0, DMIC: 1 */ + mic_sel = val ? 0x0 : 0x1; + if (mic_sel_reg) + snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, mic_sel << 7); + + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static int tavil_int_dem_inp_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 snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int val; + unsigned short look_ahead_dly_reg = WCD934X_CDC_RX0_RX_PATH_CFG0; + + val = ucontrol->value.enumerated.item[0]; + if (val >= e->items) + return -EINVAL; + + dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__, + widget->name, val); + + if (e->reg == WCD934X_CDC_RX0_RX_PATH_SEC0) + look_ahead_dly_reg = WCD934X_CDC_RX0_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX1_RX_PATH_SEC0) + look_ahead_dly_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; + else if (e->reg == WCD934X_CDC_RX2_RX_PATH_SEC0) + look_ahead_dly_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; + + /* Set Look Ahead Delay */ + snd_soc_update_bits(codec, look_ahead_dly_reg, + 0x08, (val ? 0x08 : 0x00)); + /* Set DEM INP Select */ + return snd_soc_dapm_put_enum_double(kcontrol, ucontrol); +} + +static const char * const rx_int0_7_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7", "PROXIMITY" +}; + +static const char * const rx_int_mix_mux_text[] = { + "ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", + "RX6", "RX7" +}; + +static const char * const rx_prim_mix_text[] = { + "ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2", + "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_sidetone_mix_text[] = { + "ZERO", "SRC0", "SRC1", "SRC_SUM" +}; + +static const char * const cdc_if_tx0_mux_text[] = { + "ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192" +}; +static const char * const cdc_if_tx1_mux_text[] = { + "ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192" +}; +static const char * const cdc_if_tx2_mux_text[] = { + "ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192" +}; +static const char * const cdc_if_tx3_mux_text[] = { + "ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192" +}; +static const char * const cdc_if_tx4_mux_text[] = { + "ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192" +}; +static const char * const cdc_if_tx5_mux_text[] = { + "ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192" +}; +static const char * const cdc_if_tx6_mux_text[] = { + "ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192" +}; +static const char * const cdc_if_tx7_mux_text[] = { + "ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192" +}; +static const char * const cdc_if_tx8_mux_text[] = { + "ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192" +}; +static const char * const cdc_if_tx9_mux_text[] = { + "ZERO", "DEC7", "DEC7_192" +}; +static const char * const cdc_if_tx10_mux_text[] = { + "ZERO", "DEC6", "DEC6_192" +}; +static const char * const cdc_if_tx11_mux_text[] = { + "DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST" +}; +static const char * const cdc_if_tx11_inp1_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", + "DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12" +}; +static const char * const cdc_if_tx13_mux_text[] = { + "CDC_DEC_5", "MAD_BRDCST" +}; +static const char * const cdc_if_tx13_inp1_mux_text[] = { + "ZERO", "DEC5", "DEC5_192" +}; + +static const char * const iir_inp_mux_text[] = { + "ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", + "DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7" +}; + +static const char * const rx_int_dem_inp_mux_text[] = { + "NORMAL_DSM_OUT", "CLSH_DSM_OUT", +}; + +static const char * const rx_int0_1_interp_mux_text[] = { + "ZERO", "RX INT0_1 MIX1", +}; + +static const char * const rx_int1_1_interp_mux_text[] = { + "ZERO", "RX INT1_1 MIX1", +}; + +static const char * const rx_int2_1_interp_mux_text[] = { + "ZERO", "RX INT2_1 MIX1", +}; + +static const char * const rx_int3_1_interp_mux_text[] = { + "ZERO", "RX INT3_1 MIX1", +}; + +static const char * const rx_int4_1_interp_mux_text[] = { + "ZERO", "RX INT4_1 MIX1", +}; + +static const char * const rx_int7_1_interp_mux_text[] = { + "ZERO", "RX INT7_1 MIX1", +}; + +static const char * const rx_int8_1_interp_mux_text[] = { + "ZERO", "RX INT8_1 MIX1", +}; + +static const char * const rx_int0_2_interp_mux_text[] = { + "ZERO", "RX INT0_2 MUX", +}; + +static const char * const rx_int1_2_interp_mux_text[] = { + "ZERO", "RX INT1_2 MUX", +}; + +static const char * const rx_int2_2_interp_mux_text[] = { + "ZERO", "RX INT2_2 MUX", +}; + +static const char * const rx_int3_2_interp_mux_text[] = { + "ZERO", "RX INT3_2 MUX", +}; + +static const char * const rx_int4_2_interp_mux_text[] = { + "ZERO", "RX INT4_2 MUX", +}; + +static const char * const rx_int7_2_interp_mux_text[] = { + "ZERO", "RX INT7_2 MUX", +}; + +static const char * const rx_int8_2_interp_mux_text[] = { + "ZERO", "RX INT8_2 MUX", +}; + +static const char * const mad_sel_txt[] = { + "SPE", "MSM" +}; + +static const char * const mad_inp_mux_txt[] = { + "MAD", "DEC1" +}; + +static const char * const adc_mux_text[] = { + "DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2" +}; + +static const char * const dmic_mux_text[] = { + "ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5" +}; + +static const char * const amic_mux_text[] = { + "ZERO", "ADC1", "ADC2", "ADC3", "ADC4" +}; + +static const char * const amic4_5_sel_text[] = { + "AMIC4", "AMIC5" +}; + +static const char * const anc0_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR", + "ANC_IN_LO1" +}; + +static const char * const anc1_fb_mux_text[] = { + "ZERO", "ANC_IN_HPHR", "ANC_IN_LO2" +}; + +static const char * const rx_echo_mux_text[] = { + "ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4", + "RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8" +}; + +static const char *const slim_rx_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB" +}; + +static const char *const cdc_if_rx0_mux_text[] = { + "SLIM RX0", "I2S_0 RX0" +}; +static const char *const cdc_if_rx1_mux_text[] = { + "SLIM RX1", "I2S_0 RX1" +}; +static const char *const cdc_if_rx2_mux_text[] = { + "SLIM RX2", "I2S_0 RX2" +}; +static const char *const cdc_if_rx3_mux_text[] = { + "SLIM RX3", "I2S_0 RX3" +}; +static const char *const cdc_if_rx4_mux_text[] = { + "SLIM RX4", "I2S_0 RX4" +}; +static const char *const cdc_if_rx5_mux_text[] = { + "SLIM RX5", "I2S_0 RX5" +}; +static const char *const cdc_if_rx6_mux_text[] = { + "SLIM RX6", "I2S_0 RX6" +}; +static const char *const cdc_if_rx7_mux_text[] = { + "SLIM RX7", "I2S_0 RX7" +}; + +static const char * const asrc0_mux_text[] = { + "ZERO", "ASRC_IN_HPHL", "ASRC_IN_LO1", +}; + +static const char * const asrc1_mux_text[] = { + "ZERO", "ASRC_IN_HPHR", "ASRC_IN_LO2", +}; + +static const char * const asrc2_mux_text[] = { + "ZERO", "ASRC_IN_SPKR1", +}; + +static const char * const asrc3_mux_text[] = { + "ZERO", "ASRC_IN_SPKR2", +}; + +static const char * const native_mux_text[] = { + "OFF", "ON", +}; + +static const struct snd_kcontrol_new aif4_vi_mixer[] = { + SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, WCD934X_TX14, 1, 0, + tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put), + SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, WCD934X_TX15, 1, 0, + tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put), +}; + +static const struct snd_kcontrol_new aif1_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_cap_mixer[] = { + SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif4_mad_mixer[] = { + SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, + slim_tx_mixer_get, slim_tx_mixer_put), +}; + +WCD_DAPM_ENUM_EXT(slim_rx0, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx1, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx2, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx3, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx4, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx5, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx6, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); +WCD_DAPM_ENUM_EXT(slim_rx7, SND_SOC_NOPM, 0, slim_rx_mux_text, + slim_rx_mux_get, slim_rx_mux_put); + +WCD_DAPM_ENUM(cdc_if_rx0, SND_SOC_NOPM, 0, cdc_if_rx0_mux_text); +WCD_DAPM_ENUM(cdc_if_rx1, SND_SOC_NOPM, 0, cdc_if_rx1_mux_text); +WCD_DAPM_ENUM(cdc_if_rx2, SND_SOC_NOPM, 0, cdc_if_rx2_mux_text); +WCD_DAPM_ENUM(cdc_if_rx3, SND_SOC_NOPM, 0, cdc_if_rx3_mux_text); +WCD_DAPM_ENUM(cdc_if_rx4, SND_SOC_NOPM, 0, cdc_if_rx4_mux_text); +WCD_DAPM_ENUM(cdc_if_rx5, SND_SOC_NOPM, 0, cdc_if_rx5_mux_text); +WCD_DAPM_ENUM(cdc_if_rx6, SND_SOC_NOPM, 0, cdc_if_rx6_mux_text); +WCD_DAPM_ENUM(cdc_if_rx7, SND_SOC_NOPM, 0, cdc_if_rx7_mux_text); + +WCD_DAPM_ENUM(rx_int0_2, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, + rx_int0_7_mix_mux_text); +WCD_DAPM_ENUM(rx_int1_2, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int2_2, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int3_2, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int4_2, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, + rx_int_mix_mux_text); +WCD_DAPM_ENUM(rx_int7_2, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, + rx_int0_7_mix_mux_text); +WCD_DAPM_ENUM(rx_int8_2, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, + rx_int_mix_mux_text); + +WCD_DAPM_ENUM(rx_int0_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int0_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int0_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int1_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int1_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int1_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int2_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int2_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int2_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int3_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int3_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int3_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int4_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int4_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int4_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int7_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, + rx_prim_mix_text); +WCD_DAPM_ENUM(rx_int8_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, + rx_prim_mix_text); + +WCD_DAPM_ENUM(rx_int0_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int1_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int2_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int3_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int4_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, + rx_sidetone_mix_text); +WCD_DAPM_ENUM(rx_int7_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, + rx_sidetone_mix_text); + +WCD_DAPM_ENUM(tx_adc_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 4, + adc_mux_text); +WCD_DAPM_ENUM(tx_adc_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 4, + adc_mux_text); +WCD_DAPM_ENUM(tx_adc_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 4, + adc_mux_text); +WCD_DAPM_ENUM(tx_adc_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 6, + adc_mux_text); + + +WCD_DAPM_ENUM(tx_dmic_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 3, + dmic_mux_text); +WCD_DAPM_ENUM(tx_dmic_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 3, + dmic_mux_text); + + +WCD_DAPM_ENUM(tx_amic_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0, + amic_mux_text); +WCD_DAPM_ENUM(tx_amic_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0, + amic_mux_text); + +WCD_DAPM_ENUM(tx_amic4_5, WCD934X_TX_NEW_AMIC_4_5_SEL, 7, amic4_5_sel_text); + +WCD_DAPM_ENUM(cdc_if_tx0, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0, + cdc_if_tx0_mux_text); +WCD_DAPM_ENUM(cdc_if_tx1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 2, + cdc_if_tx1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx2, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 4, + cdc_if_tx2_mux_text); +WCD_DAPM_ENUM(cdc_if_tx3, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 6, + cdc_if_tx3_mux_text); +WCD_DAPM_ENUM(cdc_if_tx4, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0, + cdc_if_tx4_mux_text); +WCD_DAPM_ENUM(cdc_if_tx5, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 2, + cdc_if_tx5_mux_text); +WCD_DAPM_ENUM(cdc_if_tx6, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 4, + cdc_if_tx6_mux_text); +WCD_DAPM_ENUM(cdc_if_tx7, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 6, + cdc_if_tx7_mux_text); +WCD_DAPM_ENUM(cdc_if_tx8, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0, + cdc_if_tx8_mux_text); +WCD_DAPM_ENUM(cdc_if_tx9, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 2, + cdc_if_tx9_mux_text); +WCD_DAPM_ENUM(cdc_if_tx10, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 4, + cdc_if_tx10_mux_text); +WCD_DAPM_ENUM(cdc_if_tx11_inp1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0, + cdc_if_tx11_inp1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx11, WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0, + cdc_if_tx11_mux_text); +WCD_DAPM_ENUM(cdc_if_tx13_inp1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 4, + cdc_if_tx13_inp1_mux_text); +WCD_DAPM_ENUM(cdc_if_tx13, WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0, + cdc_if_tx13_mux_text); + +WCD_DAPM_ENUM(rx_mix_tx0, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx1, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx2, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx3, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx4, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx5, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx6, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 0, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx7, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 4, + rx_echo_mux_text); +WCD_DAPM_ENUM(rx_mix_tx8, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, + rx_echo_mux_text); + +WCD_DAPM_ENUM(iir0_inp0, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp1, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp2, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir0_inp3, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp0, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp1, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp2, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0, + iir_inp_mux_text); +WCD_DAPM_ENUM(iir1_inp3, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0, + iir_inp_mux_text); + +WCD_DAPM_ENUM(rx_int0_1_interp, SND_SOC_NOPM, 0, rx_int0_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int1_1_interp, SND_SOC_NOPM, 0, rx_int1_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int2_1_interp, SND_SOC_NOPM, 0, rx_int2_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int3_1_interp, SND_SOC_NOPM, 0, rx_int3_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int4_1_interp, SND_SOC_NOPM, 0, rx_int4_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int7_1_interp, SND_SOC_NOPM, 0, rx_int7_1_interp_mux_text); +WCD_DAPM_ENUM(rx_int8_1_interp, SND_SOC_NOPM, 0, rx_int8_1_interp_mux_text); + +WCD_DAPM_ENUM(rx_int0_2_interp, SND_SOC_NOPM, 0, rx_int0_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int1_2_interp, SND_SOC_NOPM, 0, rx_int1_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int2_2_interp, SND_SOC_NOPM, 0, rx_int2_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int3_2_interp, SND_SOC_NOPM, 0, rx_int3_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int4_2_interp, SND_SOC_NOPM, 0, rx_int4_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int7_2_interp, SND_SOC_NOPM, 0, rx_int7_2_interp_mux_text); +WCD_DAPM_ENUM(rx_int8_2_interp, SND_SOC_NOPM, 0, rx_int8_2_interp_mux_text); + +WCD_DAPM_ENUM(mad_sel, WCD934X_CPE_SS_SVA_CFG, 0, + mad_sel_txt); + +WCD_DAPM_ENUM(mad_inp_mux, WCD934X_CPE_SS_SVA_CFG, 2, + mad_inp_mux_txt); + +WCD_DAPM_ENUM_EXT(rx_int0_dem_inp, WCD934X_CDC_RX0_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + tavil_int_dem_inp_mux_put); +WCD_DAPM_ENUM_EXT(rx_int1_dem_inp, WCD934X_CDC_RX1_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + tavil_int_dem_inp_mux_put); +WCD_DAPM_ENUM_EXT(rx_int2_dem_inp, WCD934X_CDC_RX2_RX_PATH_SEC0, 0, + rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double, + tavil_int_dem_inp_mux_put); + +WCD_DAPM_ENUM_EXT(tx_adc_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); +WCD_DAPM_ENUM_EXT(tx_adc_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 4, + adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put); + +WCD_DAPM_ENUM(asrc0, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0, + asrc0_mux_text); +WCD_DAPM_ENUM(asrc1, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 2, + asrc1_mux_text); +WCD_DAPM_ENUM(asrc2, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 4, + asrc2_mux_text); +WCD_DAPM_ENUM(asrc3, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 6, + asrc3_mux_text); + +WCD_DAPM_ENUM(int1_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int2_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int3_1_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int4_1_native, SND_SOC_NOPM, 0, native_mux_text); + +WCD_DAPM_ENUM(int1_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int2_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int3_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int4_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int7_2_native, SND_SOC_NOPM, 0, native_mux_text); +WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text); + +WCD_DAPM_ENUM(anc0_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text); +WCD_DAPM_ENUM(anc1_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 3, anc1_fb_mux_text); + +static const struct snd_kcontrol_new anc_ear_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_ear_spkr_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_spkr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_hphl_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new anc_hphr_pa_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_cpe1_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_cpe2_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new mad_brdcst_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux0_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux1_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux2_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux3_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux4_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux5_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux6_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux7_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new adc_us_mux8_switch = + SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0); + +static const struct snd_kcontrol_new rx_int1_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int2_asrc_switch[] = { + SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int3_asrc_switch[] = { + SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static const struct snd_kcontrol_new rx_int4_asrc_switch[] = { + SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0), +}; + +static int tavil_dsd_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config; + int val; + + val = tavil_dsd_get_current_mixer_value(dsd_conf, mc->shift); + + ucontrol->value.integer.value[0] = ((val < 0) ? 0 : val); + + return 0; +} + +static int tavil_dsd_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + unsigned int wval = ucontrol->value.integer.value[0]; + struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config; + + if (!dsd_conf) + return 0; + + mutex_lock(&tavil_p->codec_mutex); + + tavil_dsd_set_out_select(dsd_conf, mc->shift); + tavil_dsd_set_mixer_value(dsd_conf, mc->shift, wval); + + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(dapm, kcontrol, wval, NULL); + + return 0; +} + +static const struct snd_kcontrol_new hphl_mixer[] = { + SOC_SINGLE_EXT("DSD HPHL Switch", SND_SOC_NOPM, INTERP_HPHL, 1, 0, + tavil_dsd_mixer_get, tavil_dsd_mixer_put), +}; + +static const struct snd_kcontrol_new hphr_mixer[] = { + SOC_SINGLE_EXT("DSD HPHR Switch", SND_SOC_NOPM, INTERP_HPHR, 1, 0, + tavil_dsd_mixer_get, tavil_dsd_mixer_put), +}; + +static const struct snd_kcontrol_new lo1_mixer[] = { + SOC_SINGLE_EXT("DSD LO1 Switch", SND_SOC_NOPM, INTERP_LO1, 1, 0, + tavil_dsd_mixer_get, tavil_dsd_mixer_put), +}; + +static const struct snd_kcontrol_new lo2_mixer[] = { + SOC_SINGLE_EXT("DSD LO2 Switch", SND_SOC_NOPM, INTERP_LO2, 1, 0, + tavil_dsd_mixer_get, tavil_dsd_mixer_put), +}; + +static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, tavil_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, tavil_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, tavil_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, + AIF4_PB, 0, tavil_codec_enable_slimrx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("SLIM RX0 MUX", WCD934X_RX0, slim_rx0), + WCD_DAPM_MUX("SLIM RX1 MUX", WCD934X_RX1, slim_rx1), + WCD_DAPM_MUX("SLIM RX2 MUX", WCD934X_RX2, slim_rx2), + WCD_DAPM_MUX("SLIM RX3 MUX", WCD934X_RX3, slim_rx3), + WCD_DAPM_MUX("SLIM RX4 MUX", WCD934X_RX4, slim_rx4), + WCD_DAPM_MUX("SLIM RX5 MUX", WCD934X_RX5, slim_rx5), + WCD_DAPM_MUX("SLIM RX6 MUX", WCD934X_RX6, slim_rx6), + WCD_DAPM_MUX("SLIM RX7 MUX", WCD934X_RX7, slim_rx7), + + SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + + WCD_DAPM_MUX("CDC_IF RX0 MUX", WCD934X_RX0, cdc_if_rx0), + WCD_DAPM_MUX("CDC_IF RX1 MUX", WCD934X_RX1, cdc_if_rx1), + WCD_DAPM_MUX("CDC_IF RX2 MUX", WCD934X_RX2, cdc_if_rx2), + WCD_DAPM_MUX("CDC_IF RX3 MUX", WCD934X_RX3, cdc_if_rx3), + WCD_DAPM_MUX("CDC_IF RX4 MUX", WCD934X_RX4, cdc_if_rx4), + WCD_DAPM_MUX("CDC_IF RX5 MUX", WCD934X_RX5, cdc_if_rx5), + WCD_DAPM_MUX("CDC_IF RX6 MUX", WCD934X_RX6, cdc_if_rx6), + WCD_DAPM_MUX("CDC_IF RX7 MUX", WCD934X_RX7, cdc_if_rx7), + + SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_2_mux, tavil_codec_enable_mix_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("RX INT0_1 MIX1 INP0", 0, rx_int0_1_mix_inp0), + WCD_DAPM_MUX("RX INT0_1 MIX1 INP1", 0, rx_int0_1_mix_inp1), + WCD_DAPM_MUX("RX INT0_1 MIX1 INP2", 0, rx_int0_1_mix_inp2), + WCD_DAPM_MUX("RX INT1_1 MIX1 INP0", 0, rx_int1_1_mix_inp0), + WCD_DAPM_MUX("RX INT1_1 MIX1 INP1", 0, rx_int1_1_mix_inp1), + WCD_DAPM_MUX("RX INT1_1 MIX1 INP2", 0, rx_int1_1_mix_inp2), + WCD_DAPM_MUX("RX INT2_1 MIX1 INP0", 0, rx_int2_1_mix_inp0), + WCD_DAPM_MUX("RX INT2_1 MIX1 INP1", 0, rx_int2_1_mix_inp1), + WCD_DAPM_MUX("RX INT2_1 MIX1 INP2", 0, rx_int2_1_mix_inp2), + WCD_DAPM_MUX("RX INT3_1 MIX1 INP0", 0, rx_int3_1_mix_inp0), + WCD_DAPM_MUX("RX INT3_1 MIX1 INP1", 0, rx_int3_1_mix_inp1), + WCD_DAPM_MUX("RX INT3_1 MIX1 INP2", 0, rx_int3_1_mix_inp2), + WCD_DAPM_MUX("RX INT4_1 MIX1 INP0", 0, rx_int4_1_mix_inp0), + WCD_DAPM_MUX("RX INT4_1 MIX1 INP1", 0, rx_int4_1_mix_inp1), + WCD_DAPM_MUX("RX INT4_1 MIX1 INP2", 0, rx_int4_1_mix_inp2), + + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp0_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp1_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int7_1_mix_inp2_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp0_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp1_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx_int8_1_mix_inp2_mux, tavil_codec_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int1_asrc_switch, ARRAY_SIZE(rx_int1_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int2_asrc_switch, ARRAY_SIZE(rx_int2_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int3_asrc_switch, ARRAY_SIZE(rx_int3_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, + rx_int4_asrc_switch, ARRAY_SIZE(rx_int4_asrc_switch)), + SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT1 MIX3", SND_SOC_NOPM, 0, 0, hphl_mixer, + ARRAY_SIZE(hphl_mixer)), + SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT2 MIX3", SND_SOC_NOPM, 0, 0, hphr_mixer, + ARRAY_SIZE(hphr_mixer)), + SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT3 MIX3", SND_SOC_NOPM, 0, 0, lo1_mixer, + ARRAY_SIZE(lo1_mixer)), + SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX INT4 MIX3", SND_SOC_NOPM, 0, 0, lo2_mixer, + ARRAY_SIZE(lo2_mixer)), + SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, tavil_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, tavil_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", SND_SOC_NOPM, INTERP_EAR, + 0, &rx_int0_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", SND_SOC_NOPM, INTERP_HPHL, + 0, &rx_int1_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", SND_SOC_NOPM, INTERP_HPHR, + 0, &rx_int2_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3 MIX2 INP", SND_SOC_NOPM, INTERP_LO1, + 0, &rx_int3_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4 MIX2 INP", SND_SOC_NOPM, INTERP_LO2, + 0, &rx_int4_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7 MIX2 INP", SND_SOC_NOPM, INTERP_SPKR1, + 0, &rx_int7_mix2_inp_mux, tavil_codec_enable_rx_path_clk, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("CDC_IF TX0 MUX", WCD934X_TX0, cdc_if_tx0), + WCD_DAPM_MUX("CDC_IF TX1 MUX", WCD934X_TX1, cdc_if_tx1), + WCD_DAPM_MUX("CDC_IF TX2 MUX", WCD934X_TX2, cdc_if_tx2), + WCD_DAPM_MUX("CDC_IF TX3 MUX", WCD934X_TX3, cdc_if_tx3), + WCD_DAPM_MUX("CDC_IF TX4 MUX", WCD934X_TX4, cdc_if_tx4), + WCD_DAPM_MUX("CDC_IF TX5 MUX", WCD934X_TX5, cdc_if_tx5), + WCD_DAPM_MUX("CDC_IF TX6 MUX", WCD934X_TX6, cdc_if_tx6), + WCD_DAPM_MUX("CDC_IF TX7 MUX", WCD934X_TX7, cdc_if_tx7), + WCD_DAPM_MUX("CDC_IF TX8 MUX", WCD934X_TX8, cdc_if_tx8), + WCD_DAPM_MUX("CDC_IF TX9 MUX", WCD934X_TX9, cdc_if_tx9), + WCD_DAPM_MUX("CDC_IF TX10 MUX", WCD934X_TX10, cdc_if_tx10), + WCD_DAPM_MUX("CDC_IF TX11 MUX", WCD934X_TX11, cdc_if_tx11), + WCD_DAPM_MUX("CDC_IF TX11 INP1 MUX", WCD934X_TX11, cdc_if_tx11_inp1), + WCD_DAPM_MUX("CDC_IF TX13 MUX", WCD934X_TX13, cdc_if_tx13), + WCD_DAPM_MUX("CDC_IF TX13 INP1 MUX", WCD934X_TX13, cdc_if_tx13_inp1), + + SND_SOC_DAPM_MUX_E("ADC MUX0", WCD934X_CDC_TX0_TX_PATH_CTL, 5, 0, + &tx_adc_mux0_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX1", WCD934X_CDC_TX1_TX_PATH_CTL, 5, 0, + &tx_adc_mux1_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX2", WCD934X_CDC_TX2_TX_PATH_CTL, 5, 0, + &tx_adc_mux2_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX3", WCD934X_CDC_TX3_TX_PATH_CTL, 5, 0, + &tx_adc_mux3_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX4", WCD934X_CDC_TX4_TX_PATH_CTL, 5, 0, + &tx_adc_mux4_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX5", WCD934X_CDC_TX5_TX_PATH_CTL, 5, 0, + &tx_adc_mux5_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX6", WCD934X_CDC_TX6_TX_PATH_CTL, 5, 0, + &tx_adc_mux6_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX7", WCD934X_CDC_TX7_TX_PATH_CTL, 5, 0, + &tx_adc_mux7_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX8", WCD934X_CDC_TX8_TX_PATH_CTL, 5, 0, + &tx_adc_mux8_mux, tavil_codec_enable_dec, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX_E("ADC MUX10", SND_SOC_NOPM, 10, 0, &tx_adc_mux10_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX11", SND_SOC_NOPM, 11, 0, &tx_adc_mux11_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX12", SND_SOC_NOPM, 12, 0, &tx_adc_mux12_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("ADC MUX13", SND_SOC_NOPM, 13, 0, &tx_adc_mux13_mux, + tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU), + + WCD_DAPM_MUX("DMIC MUX0", 0, tx_dmic_mux0), + WCD_DAPM_MUX("DMIC MUX1", 0, tx_dmic_mux1), + WCD_DAPM_MUX("DMIC MUX2", 0, tx_dmic_mux2), + WCD_DAPM_MUX("DMIC MUX3", 0, tx_dmic_mux3), + WCD_DAPM_MUX("DMIC MUX4", 0, tx_dmic_mux4), + WCD_DAPM_MUX("DMIC MUX5", 0, tx_dmic_mux5), + WCD_DAPM_MUX("DMIC MUX6", 0, tx_dmic_mux6), + WCD_DAPM_MUX("DMIC MUX7", 0, tx_dmic_mux7), + WCD_DAPM_MUX("DMIC MUX8", 0, tx_dmic_mux8), + WCD_DAPM_MUX("DMIC MUX10", 0, tx_dmic_mux10), + WCD_DAPM_MUX("DMIC MUX11", 0, tx_dmic_mux11), + WCD_DAPM_MUX("DMIC MUX12", 0, tx_dmic_mux12), + WCD_DAPM_MUX("DMIC MUX13", 0, tx_dmic_mux13), + + WCD_DAPM_MUX("AMIC MUX0", 0, tx_amic_mux0), + WCD_DAPM_MUX("AMIC MUX1", 0, tx_amic_mux1), + WCD_DAPM_MUX("AMIC MUX2", 0, tx_amic_mux2), + WCD_DAPM_MUX("AMIC MUX3", 0, tx_amic_mux3), + WCD_DAPM_MUX("AMIC MUX4", 0, tx_amic_mux4), + WCD_DAPM_MUX("AMIC MUX5", 0, tx_amic_mux5), + WCD_DAPM_MUX("AMIC MUX6", 0, tx_amic_mux6), + WCD_DAPM_MUX("AMIC MUX7", 0, tx_amic_mux7), + WCD_DAPM_MUX("AMIC MUX8", 0, tx_amic_mux8), + WCD_DAPM_MUX("AMIC MUX10", 0, tx_amic_mux10), + WCD_DAPM_MUX("AMIC MUX11", 0, tx_amic_mux11), + WCD_DAPM_MUX("AMIC MUX12", 0, tx_amic_mux12), + WCD_DAPM_MUX("AMIC MUX13", 0, tx_amic_mux13), + + SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD934X_ANA_AMIC1, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD934X_ANA_AMIC2, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD934X_ANA_AMIC3, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD934X_ANA_AMIC4, 7, 0, + tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU), + + WCD_DAPM_MUX("AMIC4_5 SEL", 0, tx_amic4_5), + + WCD_DAPM_MUX("ANC0 FB MUX", 0, anc0_fb), + WCD_DAPM_MUX("ANC1 FB MUX", 0, anc1_fb), + + SND_SOC_DAPM_INPUT("AMIC1"), + SND_SOC_DAPM_INPUT("AMIC2"), + SND_SOC_DAPM_INPUT("AMIC3"), + SND_SOC_DAPM_INPUT("AMIC4"), + SND_SOC_DAPM_INPUT("AMIC5"), + + SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS2", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS3", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E("MIC BIAS4", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + /* + * Not supply widget, this is used to recover HPH registers. + * It is not connected to any other widgets + */ + SND_SOC_DAPM_SUPPLY("RESET_HPH_REGISTERS", SND_SOC_NOPM, + 0, 0, tavil_codec_reset_hph_registers, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS4_STANDALONE, SND_SOC_NOPM, 0, 0, + tavil_codec_force_enable_micbias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, + AIF1_CAP, 0, tavil_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, + AIF2_CAP, 0, tavil_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, + AIF3_CAP, 0, tavil_codec_enable_slimtx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, + aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), + + SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, + AIF4_VIFEED, 0, tavil_codec_enable_slimvi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0, + aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)), + SND_SOC_DAPM_INPUT("VIINPUT"), + + SND_SOC_DAPM_MIXER("SLIM TX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX8", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX9", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX10", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX11", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SLIM TX13", SND_SOC_NOPM, 0, 0, NULL, 0), + + /* Digital Mic Inputs */ + SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0, + tavil_codec_enable_dmic, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("IIR0 INP0 MUX", 0, iir0_inp0), + WCD_DAPM_MUX("IIR0 INP1 MUX", 0, iir0_inp1), + WCD_DAPM_MUX("IIR0 INP2 MUX", 0, iir0_inp2), + WCD_DAPM_MUX("IIR0 INP3 MUX", 0, iir0_inp3), + WCD_DAPM_MUX("IIR1 INP0 MUX", 0, iir1_inp0), + WCD_DAPM_MUX("IIR1 INP1 MUX", 0, iir1_inp1), + WCD_DAPM_MUX("IIR1 INP2 MUX", 0, iir1_inp2), + WCD_DAPM_MUX("IIR1 INP3 MUX", 0, iir1_inp3), + + SND_SOC_DAPM_MIXER_E("IIR0", WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL, + 4, 0, NULL, 0, tavil_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER_E("IIR1", WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL, + 4, 0, NULL, 0, tavil_codec_set_iir_gain, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MIXER("SRC0", WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + SND_SOC_DAPM_MIXER("SRC1", WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL, + 4, 0, NULL, 0), + + WCD_DAPM_MUX("RX MIX TX0 MUX", 0, rx_mix_tx0), + WCD_DAPM_MUX("RX MIX TX1 MUX", 0, rx_mix_tx1), + WCD_DAPM_MUX("RX MIX TX2 MUX", 0, rx_mix_tx2), + WCD_DAPM_MUX("RX MIX TX3 MUX", 0, rx_mix_tx3), + WCD_DAPM_MUX("RX MIX TX4 MUX", 0, rx_mix_tx4), + WCD_DAPM_MUX("RX MIX TX5 MUX", 0, rx_mix_tx5), + WCD_DAPM_MUX("RX MIX TX6 MUX", 0, rx_mix_tx6), + WCD_DAPM_MUX("RX MIX TX7 MUX", 0, rx_mix_tx7), + WCD_DAPM_MUX("RX MIX TX8 MUX", 0, rx_mix_tx8), + WCD_DAPM_MUX("RX INT0 DEM MUX", 0, rx_int0_dem_inp), + WCD_DAPM_MUX("RX INT1 DEM MUX", 0, rx_int1_dem_inp), + WCD_DAPM_MUX("RX INT2 DEM MUX", 0, rx_int2_dem_inp), + + SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_EAR, 0, + &rx_int0_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0, + &rx_int1_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0, + &rx_int2_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT3_1 INTERP", SND_SOC_NOPM, INTERP_LO1, 0, + &rx_int3_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT4_1 INTERP", SND_SOC_NOPM, INTERP_LO2, 0, + &rx_int4_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT7_1 INTERP", SND_SOC_NOPM, INTERP_SPKR1, 0, + &rx_int7_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX INT8_1 INTERP", SND_SOC_NOPM, INTERP_SPKR2, 0, + &rx_int8_1_interp_mux, tavil_codec_enable_main_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + WCD_DAPM_MUX("RX INT0_2 INTERP", 0, rx_int0_2_interp), + WCD_DAPM_MUX("RX INT1_2 INTERP", 0, rx_int1_2_interp), + WCD_DAPM_MUX("RX INT2_2 INTERP", 0, rx_int2_2_interp), + WCD_DAPM_MUX("RX INT3_2 INTERP", 0, rx_int3_2_interp), + WCD_DAPM_MUX("RX INT4_2 INTERP", 0, rx_int4_2_interp), + WCD_DAPM_MUX("RX INT7_2 INTERP", 0, rx_int7_2_interp), + WCD_DAPM_MUX("RX INT8_2 INTERP", 0, rx_int8_2_interp), + + SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD934X_CDC_TX0_TX_PATH_192_CTL, 0, + 0, &adc_us_mux0_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD934X_CDC_TX1_TX_PATH_192_CTL, 0, + 0, &adc_us_mux1_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX2", WCD934X_CDC_TX2_TX_PATH_192_CTL, 0, + 0, &adc_us_mux2_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX3", WCD934X_CDC_TX3_TX_PATH_192_CTL, 0, + 0, &adc_us_mux3_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX4", WCD934X_CDC_TX4_TX_PATH_192_CTL, 0, + 0, &adc_us_mux4_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX5", WCD934X_CDC_TX5_TX_PATH_192_CTL, 0, + 0, &adc_us_mux5_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX6", WCD934X_CDC_TX6_TX_PATH_192_CTL, 0, + 0, &adc_us_mux6_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX7", WCD934X_CDC_TX7_TX_PATH_192_CTL, 0, + 0, &adc_us_mux7_switch), + SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD934X_CDC_TX8_TX_PATH_192_CTL, 0, + 0, &adc_us_mux8_switch), + + /* MAD related widgets */ + SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"), + SND_SOC_DAPM_INPUT("MADINPUT"), + + WCD_DAPM_MUX("MAD_SEL MUX", 0, mad_sel), + WCD_DAPM_MUX("MAD_INP MUX", 0, mad_inp_mux), + + SND_SOC_DAPM_SWITCH_E("MAD_BROADCAST", SND_SOC_NOPM, 0, 0, + &mad_brdcst_switch, tavil_codec_ape_enable_mad, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SWITCH_E("MAD_CPE1", SND_SOC_NOPM, 0, 0, + &mad_cpe1_switch, tavil_codec_cpe_mad_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH_E("MAD_CPE2", SND_SOC_NOPM, 0, 0, + &mad_cpe2_switch, tavil_codec_cpe_mad_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT1"), + SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT2"), + + SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM, + 0, 0, tavil_codec_ear_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, WCD934X_ANA_HPH, + 5, 0, tavil_codec_hphl_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, WCD934X_ANA_HPH, + 4, 0, tavil_codec_hphr_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM, + 0, 0, tavil_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM, + 0, 0, tavil_codec_lineout_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0, + tavil_codec_enable_ear_pa, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHL PA", WCD934X_ANA_HPH, 7, 0, NULL, 0, + tavil_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("HPHR PA", WCD934X_ANA_HPH, 6, 0, NULL, 0, + tavil_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD934X_ANA_LO_1_2, 7, 0, NULL, 0, + tavil_codec_enable_lineout_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD934X_ANA_LO_1_2, 6, 0, NULL, 0, + tavil_codec_enable_lineout_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0, + tavil_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tavil_codec_enable_spkr_anc, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tavil_codec_enable_hphl_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_PGA_E("ANC HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0, + tavil_codec_enable_hphr_pa, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("EAR"), + SND_SOC_DAPM_OUTPUT("HPHL"), + SND_SOC_DAPM_OUTPUT("HPHR"), + SND_SOC_DAPM_OUTPUT("LINEOUT1"), + SND_SOC_DAPM_OUTPUT("LINEOUT2"), + SND_SOC_DAPM_OUTPUT("SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + SND_SOC_DAPM_OUTPUT("ANC EAR"), + SND_SOC_DAPM_OUTPUT("ANC HPHL"), + SND_SOC_DAPM_OUTPUT("ANC HPHR"), + + SND_SOC_DAPM_SWITCH("ANC OUT EAR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_switch), + SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0, + &anc_ear_spkr_switch), + SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0, + &anc_spkr_pa_switch), + + SND_SOC_DAPM_SWITCH_E("ANC OUT HPHL Enable", SND_SOC_NOPM, INTERP_HPHL, + 0, &anc_hphl_pa_switch, tavil_anc_out_switch_cb, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SWITCH_E("ANC OUT HPHR Enable", SND_SOC_NOPM, INTERP_HPHR, + 0, &anc_hphr_pa_switch, tavil_anc_out_switch_cb, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0, + tavil_codec_enable_rx_bias, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY("RX INT1 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHL, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT2 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_HPHR, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT3 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO1, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT4 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_LO2, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT7 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_SPKR1, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("RX INT8 NATIVE SUPPLY", SND_SOC_NOPM, + INTERP_SPKR2, 0, tavil_enable_native_supply, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + WCD_DAPM_MUX("RX INT1_1 NATIVE MUX", 0, int1_1_native), + WCD_DAPM_MUX("RX INT2_1 NATIVE MUX", 0, int2_1_native), + WCD_DAPM_MUX("RX INT3_1 NATIVE MUX", 0, int3_1_native), + WCD_DAPM_MUX("RX INT4_1 NATIVE MUX", 0, int4_1_native), + + WCD_DAPM_MUX("RX INT1_2 NATIVE MUX", 0, int1_2_native), + WCD_DAPM_MUX("RX INT2_2 NATIVE MUX", 0, int2_2_native), + WCD_DAPM_MUX("RX INT3_2 NATIVE MUX", 0, int3_2_native), + WCD_DAPM_MUX("RX INT4_2 NATIVE MUX", 0, int4_2_native), + WCD_DAPM_MUX("RX INT7_2 NATIVE MUX", 0, int7_2_native), + WCD_DAPM_MUX("RX INT8_2 NATIVE MUX", 0, int8_2_native), + + SND_SOC_DAPM_MUX_E("ASRC0 MUX", SND_SOC_NOPM, ASRC0, 0, + &asrc0_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC1 MUX", SND_SOC_NOPM, ASRC1, 0, + &asrc1_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC2 MUX", SND_SOC_NOPM, ASRC2, 0, + &asrc2_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("ASRC3 MUX", SND_SOC_NOPM, ASRC3, 0, + &asrc3_mux, tavil_codec_enable_asrc_resampler, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static int tavil_get_channel_map(struct snd_soc_dai *dai, + unsigned int *tx_num, unsigned int *tx_slot, + unsigned int *rx_num, unsigned int *rx_slot) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); + u32 i = 0; + struct wcd9xxx_ch *ch; + int ret = 0; + + switch (dai->id) { + case AIF1_PB: + case AIF2_PB: + case AIF3_PB: + case AIF4_PB: + if (!rx_slot || !rx_num) { + dev_err(tavil->dev, "%s: Invalid rx_slot 0x%pK or rx_num 0x%pK\n", + __func__, rx_slot, rx_num); + ret = -EINVAL; + break; + } + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, + list) { + dev_dbg(tavil->dev, "%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + rx_slot[i++] = ch->ch_num; + } + *rx_num = i; + dev_dbg(tavil->dev, "%s: dai_name = %s dai_id = %x rx_num = %d\n", + __func__, dai->name, dai->id, i); + if (*rx_num == 0) { + dev_err(tavil->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n", + __func__, dai->name, dai->id); + ret = -EINVAL; + } + break; + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + case AIF4_MAD_TX: + case AIF4_VIFEED: + if (!tx_slot || !tx_num) { + dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK or tx_num 0x%pK\n", + __func__, tx_slot, tx_num); + ret = -EINVAL; + break; + } + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, + list) { + dev_dbg(tavil->dev, "%s: slot_num %u ch->ch_num %d\n", + __func__, i, ch->ch_num); + tx_slot[i++] = ch->ch_num; + } + *tx_num = i; + dev_dbg(tavil->dev, "%s: dai_name = %s dai_id = %x tx_num = %d\n", + __func__, dai->name, dai->id, i); + if (*tx_num == 0) { + dev_err(tavil->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n", + __func__, dai->name, dai->id); + ret = -EINVAL; + } + break; + default: + dev_err(tavil->dev, "%s: Invalid DAI ID %x\n", + __func__, dai->id); + ret = -EINVAL; + break; + } + + return ret; +} + +static int tavil_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 tavil_priv *tavil; + struct wcd9xxx *core; + struct wcd9xxx_codec_dai_data *dai_data = NULL; + + tavil = snd_soc_codec_get_drvdata(dai->codec); + core = dev_get_drvdata(dai->codec->dev->parent); + + if (!tx_slot || !rx_slot) { + dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK, rx_slot 0x%pK\n", + __func__, tx_slot, rx_slot); + return -EINVAL; + } + dev_dbg(tavil->dev, "%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n", + __func__, dai->name, dai->id, tx_num, rx_num); + + wcd9xxx_init_slimslave(core, core->slim->laddr, + tx_num, tx_slot, rx_num, rx_slot); + /* Reserve TX13 for MAD data channel */ + dai_data = &tavil->dai[AIF4_MAD_TX]; + if (dai_data) + list_add_tail(&core->tx_chs[WCD934X_TX13].list, + &dai_data->wcd9xxx_ch_list); + + return 0; +} + +static int tavil_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + + return 0; +} + +static void tavil_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); +} + +static int tavil_set_decimator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + u32 tx_port = 0, tx_fs_rate = 0; + u8 shift = 0, shift_val = 0, tx_mux_sel = 0; + int decimator = -1; + u16 tx_port_reg = 0, tx_fs_reg = 0; + + switch (sample_rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 3; + break; + case 48000: + tx_fs_rate = 4; + break; + case 96000: + tx_fs_rate = 5; + break; + case 192000: + tx_fs_rate = 6; + break; + default: + dev_err(tavil->dev, "%s: Invalid TX sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + + }; + + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) { + tx_port = ch->port; + dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d", + __func__, dai->id, tx_port); + + if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) { + dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n", + __func__, tx_port, dai->id); + return -EINVAL; + } + /* Find the SB TX MUX input - which decimator is connected */ + if (tx_port < 4) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0; + shift = (tx_port << 1); + shift_val = 0x03; + } else if ((tx_port >= 4) && (tx_port < 8)) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1; + shift = ((tx_port - 4) << 1); + shift_val = 0x03; + } else if ((tx_port >= 8) && (tx_port < 11)) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2; + shift = ((tx_port - 8) << 1); + shift_val = 0x03; + } else if (tx_port == 11) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 0; + shift_val = 0x0F; + } else if (tx_port == 13) { + tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3; + shift = 4; + shift_val = 0x03; + } + tx_mux_sel = snd_soc_read(codec, tx_port_reg) & + (shift_val << shift); + tx_mux_sel = tx_mux_sel >> shift; + + if (tx_port <= 8) { + if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3)) + decimator = tx_port; + } else if (tx_port <= 10) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = ((tx_port == 9) ? 7 : 6); + } else if (tx_port == 11) { + if ((tx_mux_sel >= 1) && (tx_mux_sel < 7)) + decimator = tx_mux_sel - 1; + } else if (tx_port == 13) { + if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2)) + decimator = 5; + } + + if (decimator >= 0) { + tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + + 16 * decimator; + dev_dbg(codec->dev, "%s: set DEC%u (-> SLIM_TX%u) rate to %u\n", + __func__, decimator, tx_port, sample_rate); + snd_soc_update_bits(codec, tx_fs_reg, 0x0F, tx_fs_rate); + } else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) { + /* Check if the TX Mux input is RX MIX TXn */ + dev_dbg(codec->dev, "%s: RX_MIX_TX%u going to CDC_IF TX%u\n", + __func__, tx_port, tx_port); + } else { + dev_err(codec->dev, "%s: ERROR: Invalid decimator: %d\n", + __func__, decimator); + return -EINVAL; + } + } + return 0; +} + +static int tavil_set_mix_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_2_inp; + u32 j; + u16 int_mux_cfg1, int_fs_reg; + u8 int_mux_cfg1_val; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) { + int_2_inp = INTn_2_INP_SEL_RX0 + ch->port - + WCD934X_RX_PORT_START_NUMBER; + if ((int_2_inp < INTn_2_INP_SEL_RX0) || + (int_2_inp > INTn_2_INP_SEL_RX7)) { + dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - WCD934X_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg1 = WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1; + for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) { + /* Interpolators 5 and 6 are not aviliable in Tavil */ + if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) { + int_mux_cfg1 += 2; + continue; + } + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) & + 0x0F; + if (int_mux_cfg1_val == int_2_inp) { + /* + * Ear mix path supports only 48, 96, 192, + * 384KHz only + */ + if ((j == INTERP_EAR) && + (rate_reg_val < 0x4 || + rate_reg_val > 0x7)) { + dev_err_ratelimited(codec->dev, + "%s: Invalid rate for AIF_PB DAI(%d)\n", + __func__, dai->id); + return -EINVAL; + } + + int_fs_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL + + 20 * j; + dev_dbg(codec->dev, "%s: AIF_PB DAI(%d) connected to INT%u_2\n", + __func__, dai->id, j); + dev_dbg(codec->dev, "%s: set INT%u_2 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, 0x0F, + rate_reg_val); + } + int_mux_cfg1 += 2; + } + } + return 0; +} + +static int tavil_set_prim_interpolator_rate(struct snd_soc_dai *dai, + u8 rate_reg_val, + u32 sample_rate) +{ + u8 int_1_mix1_inp; + u32 j; + u16 int_mux_cfg0, int_mux_cfg1; + u16 int_fs_reg; + u8 int_mux_cfg0_val, int_mux_cfg1_val; + u8 inp0_sel, inp1_sel, inp2_sel; + struct snd_soc_codec *codec = dai->codec; + struct wcd9xxx_ch *ch; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + struct tavil_dsd_config *dsd_conf = tavil->dsd_config; + + list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) { + int_1_mix1_inp = INTn_1_INP_SEL_RX0 + ch->port - + WCD934X_RX_PORT_START_NUMBER; + if ((int_1_mix1_inp < INTn_1_INP_SEL_RX0) || + (int_1_mix1_inp > INTn_1_INP_SEL_RX7)) { + dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n", + __func__, + (ch->port - WCD934X_RX_PORT_START_NUMBER), + dai->id); + return -EINVAL; + } + + int_mux_cfg0 = WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0; + + /* + * Loop through all interpolator MUX inputs and find out + * to which interpolator input, the slim rx port + * is connected + */ + for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) { + /* Interpolators 5 and 6 are not aviliable in Tavil */ + if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) { + int_mux_cfg0 += 2; + continue; + } + int_mux_cfg1 = int_mux_cfg0 + 1; + + int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0); + int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1); + inp0_sel = int_mux_cfg0_val & 0x0F; + inp1_sel = (int_mux_cfg0_val >> 4) & 0x0F; + inp2_sel = (int_mux_cfg1_val >> 4) & 0x0F; + if ((inp0_sel == int_1_mix1_inp) || + (inp1_sel == int_1_mix1_inp) || + (inp2_sel == int_1_mix1_inp)) { + /* + * Ear and speaker primary path does not support + * native sample rates + */ + if ((j == INTERP_EAR || j == INTERP_SPKR1 || + j == INTERP_SPKR2) && + (rate_reg_val > 0x7)) { + dev_err_ratelimited(codec->dev, + "%s: Invalid rate for AIF_PB DAI(%d)\n", + __func__, dai->id); + return -EINVAL; + } + + int_fs_reg = WCD934X_CDC_RX0_RX_PATH_CTL + + 20 * j; + dev_dbg(codec->dev, + "%s: AIF_PB DAI(%d) connected to INT%u_1\n", + __func__, dai->id, j); + dev_dbg(codec->dev, + "%s: set INT%u_1 sample rate to %u\n", + __func__, j, sample_rate); + snd_soc_update_bits(codec, int_fs_reg, 0x0F, + rate_reg_val); + } + int_mux_cfg0 += 2; + } + if (dsd_conf) + tavil_dsd_set_interp_rate(dsd_conf, ch->port, + sample_rate, rate_reg_val); + } + + return 0; +} + + +static int tavil_set_interpolator_rate(struct snd_soc_dai *dai, + u32 sample_rate) +{ + struct snd_soc_codec *codec = dai->codec; + int rate_val = 0; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) { + if (sample_rate == sr_val_tbl[i].sample_rate) { + rate_val = sr_val_tbl[i].rate_val; + break; + } + } + if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) { + dev_err(codec->dev, "%s: Unsupported sample rate: %d\n", + __func__, sample_rate); + return -EINVAL; + } + + ret = tavil_set_prim_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + ret = tavil_set_mix_interpolator_rate(dai, (u8)rate_val, sample_rate); + if (ret) + return ret; + + return ret; +} + +static int tavil_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + pr_debug("%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); + return 0; +} + +static int tavil_vi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); + + dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params)); + + tavil->dai[dai->id].rate = params_rate(params); + tavil->dai[dai->id].bit_width = 32; + + return 0; +} + +static int tavil_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); + int ret = 0; + + dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params)); + + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ret = tavil_set_interpolator_rate(dai, params_rate(params)); + if (ret) { + dev_err(tavil->dev, "%s: cannot set sample rate: %u\n", + __func__, params_rate(params)); + return ret; + } + switch (params_width(params)) { + case 16: + tavil->dai[dai->id].bit_width = 16; + break; + case 24: + tavil->dai[dai->id].bit_width = 24; + break; + case 32: + tavil->dai[dai->id].bit_width = 32; + break; + default: + return -EINVAL; + } + tavil->dai[dai->id].rate = params_rate(params); + break; + case SNDRV_PCM_STREAM_CAPTURE: + if (dai->id != AIF4_MAD_TX) + ret = tavil_set_decimator_rate(dai, + params_rate(params)); + if (ret) { + dev_err(tavil->dev, "%s: cannot set TX Decimator rate: %d\n", + __func__, ret); + return ret; + } + switch (params_width(params)) { + case 16: + tavil->dai[dai->id].bit_width = 16; + break; + case 24: + tavil->dai[dai->id].bit_width = 24; + break; + default: + dev_err(tavil->dev, "%s: Invalid format 0x%x\n", + __func__, params_width(params)); + return -EINVAL; + }; + tavil->dai[dai->id].rate = params_rate(params); + break; + default: + dev_err(tavil->dev, "%s: Invalid stream type %d\n", __func__, + substream->stream); + return -EINVAL; + }; + + return 0; +} + +static struct snd_soc_dai_ops tavil_dai_ops = { + .startup = tavil_startup, + .shutdown = tavil_shutdown, + .hw_params = tavil_hw_params, + .prepare = tavil_prepare, + .set_channel_map = tavil_set_channel_map, + .get_channel_map = tavil_get_channel_map, +}; + +static struct snd_soc_dai_ops tavil_vi_dai_ops = { + .hw_params = tavil_vi_hw_params, + .set_channel_map = tavil_set_channel_map, + .get_channel_map = tavil_get_channel_map, +}; + +static struct snd_soc_dai_driver tavil_dai[] = { + { + .name = "tavil_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_rx4", + .id = AIF4_PB, + .playback = { + .stream_name = "AIF4 Playback", + .rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_dai_ops, + }, + { + .name = "tavil_vifeedback", + .id = AIF4_VIFEED, + .capture = { + .stream_name = "VIfeed", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &tavil_vi_dai_ops, + }, + { + .name = "tavil_mad1", + .id = AIF4_MAD_TX, + .capture = { + .stream_name = "AIF4 MAD TX", + .rates = SNDRV_PCM_RATE_16000, + .formats = WCD934X_FORMATS_S16_LE, + .rate_min = 16000, + .rate_max = 16000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &tavil_dai_ops, + }, +}; + +static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil) +{ + mutex_lock(&tavil->power_lock); + dev_dbg(tavil->dev, "%s: Entering power gating function, %d\n", + __func__, tavil->power_active_ref); + + if (tavil->power_active_ref > 0) + goto exit; + + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_BEGIN, + WCD9XXX_DIG_CORE_REGION_1); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x04, 0x04); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x01, 0x00); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x02, 0x00); + wcd9xxx_set_power_state(tavil->wcd9xxx, WCD_REGION_POWER_DOWN, + WCD9XXX_DIG_CORE_REGION_1); +exit: + dev_dbg(tavil->dev, "%s: Exiting power gating function, %d\n", + __func__, tavil->power_active_ref); + mutex_unlock(&tavil->power_lock); +} + +static void tavil_codec_power_gate_work(struct work_struct *work) +{ + struct tavil_priv *tavil; + struct delayed_work *dwork; + + dwork = to_delayed_work(work); + tavil = container_of(dwork, struct tavil_priv, power_gate_work); + + tavil_codec_power_gate_digital_core(tavil); +} + +/* called under power_lock acquisition */ +static int tavil_dig_core_remove_power_collapse(struct tavil_priv *tavil) +{ + regmap_write(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x05); + regmap_write(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x07); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x00); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x02); + regmap_write(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x03); + + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + regcache_mark_dirty(tavil->wcd9xxx->regmap); + regcache_sync_region(tavil->wcd9xxx->regmap, + WCD934X_DIG_CORE_REG_MIN, + WCD934X_DIG_CORE_REG_MAX); + + tavil_restore_iir_coeff(tavil, IIR0); + tavil_restore_iir_coeff(tavil, IIR1); + return 0; +} + +static int tavil_dig_core_power_collapse(struct tavil_priv *tavil, + int req_state) +{ + int cur_state; + + /* Exit if feature is disabled */ + if (!dig_core_collapse_enable) + return 0; + + mutex_lock(&tavil->power_lock); + if (req_state == POWER_COLLAPSE) + tavil->power_active_ref--; + else if (req_state == POWER_RESUME) + tavil->power_active_ref++; + else + goto unlock_mutex; + + if (tavil->power_active_ref < 0) { + dev_dbg(tavil->dev, "%s: power_active_ref is negative\n", + __func__); + goto unlock_mutex; + } + + if (req_state == POWER_COLLAPSE) { + if (tavil->power_active_ref == 0) { + schedule_delayed_work(&tavil->power_gate_work, + msecs_to_jiffies(dig_core_collapse_timer * 1000)); + } + } else if (req_state == POWER_RESUME) { + if (tavil->power_active_ref == 1) { + /* + * At this point, there can be two cases: + * 1. Core already in power collapse state + * 2. Timer kicked in and still did not expire or + * waiting for the power_lock + */ + cur_state = wcd9xxx_get_current_power_state( + tavil->wcd9xxx, + WCD9XXX_DIG_CORE_REGION_1); + if (cur_state == WCD_REGION_POWER_DOWN) { + tavil_dig_core_remove_power_collapse(tavil); + } else { + mutex_unlock(&tavil->power_lock); + cancel_delayed_work_sync( + &tavil->power_gate_work); + mutex_lock(&tavil->power_lock); + } + } + } + +unlock_mutex: + mutex_unlock(&tavil->power_lock); + + return 0; +} + +static int tavil_cdc_req_mclk_enable(struct tavil_priv *tavil, + bool enable) +{ + int ret = 0; + + if (enable) { + ret = clk_prepare_enable(tavil->wcd_ext_clk); + if (ret) { + dev_err(tavil->dev, "%s: ext clk enable failed\n", + __func__); + goto done; + } + /* get BG */ + wcd_resmgr_enable_master_bias(tavil->resmgr); + /* get MCLK */ + wcd_resmgr_enable_clk_block(tavil->resmgr, WCD_CLK_MCLK); + } else { + /* put MCLK */ + wcd_resmgr_disable_clk_block(tavil->resmgr, WCD_CLK_MCLK); + /* put BG */ + wcd_resmgr_disable_master_bias(tavil->resmgr); + clk_disable_unprepare(tavil->wcd_ext_clk); + } + +done: + return ret; +} + +static int __tavil_cdc_mclk_enable_locked(struct tavil_priv *tavil, + bool enable) +{ + int ret = 0; + + if (!tavil->wcd_ext_clk) { + dev_err(tavil->dev, "%s: wcd ext clock is NULL\n", __func__); + return -EINVAL; + } + + dev_dbg(tavil->dev, "%s: mclk_enable = %u\n", __func__, enable); + + if (enable) { + tavil_dig_core_power_collapse(tavil, POWER_RESUME); + tavil_vote_svs(tavil, true); + ret = tavil_cdc_req_mclk_enable(tavil, true); + if (ret) + goto done; + } else { + tavil_cdc_req_mclk_enable(tavil, false); + tavil_vote_svs(tavil, false); + tavil_dig_core_power_collapse(tavil, POWER_COLLAPSE); + } + +done: + return ret; +} + +static int __tavil_cdc_mclk_enable(struct tavil_priv *tavil, + bool enable) +{ + int ret; + + WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr); + ret = __tavil_cdc_mclk_enable_locked(tavil, enable); + if (enable) + wcd_resmgr_set_sido_input_src(tavil->resmgr, + SIDO_SOURCE_RCO_BG); + WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr); + + return ret; +} + +static ssize_t tavil_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + char buffer[TAVIL_VERSION_ENTRY_SIZE]; + int len = 0; + + tavil = (struct tavil_priv *) entry->private_data; + if (!tavil) { + pr_err("%s: tavil priv is null\n", __func__); + return -EINVAL; + } + + wcd9xxx = tavil->wcd9xxx; + + switch (wcd9xxx->version) { + case TAVIL_VERSION_WCD9340_1_0: + len = snprintf(buffer, sizeof(buffer), "WCD9340_1_0\n"); + break; + case TAVIL_VERSION_WCD9341_1_0: + len = snprintf(buffer, sizeof(buffer), "WCD9341_1_0\n"); + break; + case TAVIL_VERSION_WCD9340_1_1: + len = snprintf(buffer, sizeof(buffer), "WCD9340_1_1\n"); + break; + case TAVIL_VERSION_WCD9341_1_1: + len = snprintf(buffer, sizeof(buffer), "WCD9341_1_1\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops tavil_codec_info_ops = { + .read = tavil_codec_version_read, +}; + +/* + * tavil_codec_info_create_codec_entry - creates wcd934x module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wcd934x module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int tavil_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct tavil_priv *tavil; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + tavil = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + tavil->entry = snd_info_create_subdir(codec_root->module, + "tavil", codec_root); + if (!tavil->entry) { + dev_dbg(codec->dev, "%s: failed to create wcd934x entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + tavil->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wcd934x version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = tavil; + version_entry->size = TAVIL_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &tavil_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + tavil->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(tavil_codec_info_create_codec_entry); + +/** + * tavil_cdc_mclk_enable - Enable/disable codec mclk + * + * @codec: codec instance + * @enable: Indicates clk enable or disable + * + * Returns 0 on Success and error on failure + */ +int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + return __tavil_cdc_mclk_enable(tavil, enable); +} +EXPORT_SYMBOL(tavil_cdc_mclk_enable); + +static int __tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (enable) { + if (wcd_resmgr_get_clk_type(tavil->resmgr) == + WCD_CLK_RCO) { + ret = wcd_resmgr_enable_clk_block(tavil->resmgr, + WCD_CLK_RCO); + } else { + ret = tavil_cdc_req_mclk_enable(tavil, true); + if (ret) { + dev_err(codec->dev, + "%s: mclk_enable failed, err = %d\n", + __func__, ret); + goto done; + } + wcd_resmgr_set_sido_input_src(tavil->resmgr, + SIDO_SOURCE_RCO_BG); + ret = wcd_resmgr_enable_clk_block(tavil->resmgr, + WCD_CLK_RCO); + ret |= tavil_cdc_req_mclk_enable(tavil, false); + } + + } else { + ret = wcd_resmgr_disable_clk_block(tavil->resmgr, + WCD_CLK_RCO); + } + + if (ret) { + dev_err(codec->dev, "%s: Error in %s RCO\n", + __func__, (enable ? "enabling" : "disabling")); + ret = -EINVAL; + } + +done: + return ret; +} + +/* + * tavil_codec_internal_rco_ctrl: Enable/Disable codec's RCO clock + * @codec: Handle to the codec + * @enable: Indicates whether clock should be enabled or disabled + */ +static int tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec, + bool enable) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr); + ret = __tavil_codec_internal_rco_ctrl(codec, enable); + WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr); + return ret; +} + +static const struct wcd_resmgr_cb tavil_resmgr_cb = { + .cdc_rco_ctrl = __tavil_codec_internal_rco_ctrl, +}; + +static const struct tavil_reg_mask_val tavil_codec_mclk2_1_1_defaults[] = { + {WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20}, +}; + +static const struct tavil_reg_mask_val tavil_codec_mclk2_1_0_defaults[] = { + /* + * PLL Settings: + * Clock Root: MCLK2, + * Clock Source: EXT_CLK, + * Clock Destination: MCLK2 + * Clock Freq In: 19.2MHz, + * Clock Freq Out: 11.2896MHz + */ + {WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20}, + {WCD934X_CLK_SYS_INT_POST_DIV_REG0, 0xFF, 0x5E}, + {WCD934X_CLK_SYS_INT_POST_DIV_REG1, 0x1F, 0x1F}, + {WCD934X_CLK_SYS_INT_REF_DIV_REG0, 0xFF, 0x54}, + {WCD934X_CLK_SYS_INT_REF_DIV_REG1, 0xFF, 0x01}, + {WCD934X_CLK_SYS_INT_FILTER_REG1, 0x07, 0x04}, + {WCD934X_CLK_SYS_INT_PLL_L_VAL, 0xFF, 0x93}, + {WCD934X_CLK_SYS_INT_PLL_N_VAL, 0xFF, 0xFA}, + {WCD934X_CLK_SYS_INT_TEST_REG0, 0xFF, 0x90}, + {WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG, 0xFF, 0x7E}, + {WCD934X_CLK_SYS_INT_VCO_PROG, 0xFF, 0xF8}, + {WCD934X_CLK_SYS_INT_TEST_REG1, 0xFF, 0x68}, + {WCD934X_CLK_SYS_INT_LDO_LOCK_CFG, 0xFF, 0x40}, + {WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0xFF, 0x32}, +}; + +static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = { + {WCD934X_BIAS_VBG_FINE_ADJ, 0xFF, 0x75}, + {WCD934X_CODEC_CPR_SVS_CX_VDD, 0xFF, 0x7C}, /* value in svs mode */ + {WCD934X_CODEC_CPR_SVS2_CX_VDD, 0xFF, 0x58}, /* value in svs2 mode */ + {WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x1E, 0x18}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x1E, 0x18}, + {WCD934X_CDC_RX0_RX_PATH_SEC0, 0x08, 0x0}, + {WCD934X_CDC_CLSH_DECAY_CTRL, 0x03, 0x0}, + {WCD934X_MICB1_TEST_CTL_2, 0x07, 0x01}, + {WCD934X_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {WCD934X_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {WCD934X_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {WCD934X_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x1F, 0x09}, + {WCD934X_CDC_TX0_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX1_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX2_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX3_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX4_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX5_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX6_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX7_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_CDC_TX8_TX_PATH_CFG1, 0x01, 0x00}, + {WCD934X_RX_OCP_CTL, 0x0F, 0x02}, /* OCP number of attempts is 2 */ + {WCD934X_HPH_OCP_CTL, 0xFF, 0x3A}, /* OCP current limit */ + {WCD934X_HPH_L_TEST, 0x01, 0x01}, + {WCD934X_HPH_R_TEST, 0x01, 0x01}, + {WCD934X_CPE_FLL_CONFIG_CTL_2, 0xFF, 0x20}, + {WCD934X_MBHC_NEW_CTL_2, 0x0C, 0x00}, +}; + +static const struct tavil_reg_mask_val tavil_codec_reg_init_1_1_val[] = { + {WCD934X_CDC_COMPANDER1_CTL7, 0x1E, 0x06}, + {WCD934X_CDC_COMPANDER2_CTL7, 0x1E, 0x06}, + {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xFF, 0x84}, + {WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xFF, 0x84}, + {WCD934X_CDC_RX3_RX_PATH_SEC0, 0xFC, 0xF4}, + {WCD934X_CDC_RX4_RX_PATH_SEC0, 0xFC, 0xF4}, +}; + +static const struct tavil_cpr_reg_defaults cpr_defaults[] = { + { 0x00000820, 0x00000094 }, + { 0x00000fC0, 0x00000048 }, + { 0x0000f000, 0x00000044 }, + { 0x0000bb80, 0xC0000178 }, + { 0x00000000, 0x00000160 }, + { 0x10854522, 0x00000060 }, + { 0x10854509, 0x00000064 }, + { 0x108544dd, 0x00000068 }, + { 0x108544ad, 0x0000006C }, + { 0x0000077E, 0x00000070 }, + { 0x000007da, 0x00000074 }, + { 0x00000000, 0x00000078 }, + { 0x00000000, 0x0000007C }, + { 0x00042029, 0x00000080 }, + { 0x4002002A, 0x00000090 }, + { 0x4002002B, 0x00000090 }, +}; + +static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { + {WCD934X_CDC_CLSH_K2_MSB, 0x0F, 0x00}, + {WCD934X_CDC_CLSH_K2_LSB, 0xFF, 0x60}, + {WCD934X_CPE_SS_DMIC_CFG, 0x80, 0x00}, + {WCD934X_CDC_BOOST0_BOOST_CTL, 0x70, 0x50}, + {WCD934X_CDC_BOOST1_BOOST_CTL, 0x70, 0x50}, + {WCD934X_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08}, + {WCD934X_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08}, + {WCD934X_CDC_TOP_TOP_CFG1, 0x02, 0x02}, + {WCD934X_CDC_TOP_TOP_CFG1, 0x01, 0x01}, + {WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0x01, 0x01}, + {WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80}, + {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01}, + {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01}, + {WCD934X_CODEC_RPM_CLK_GATE, 0x08, 0x00}, + {WCD934X_TLMM_DMIC3_CLK_PINCFG, 0xFF, 0x0a}, + {WCD934X_TLMM_DMIC3_DATA_PINCFG, 0xFF, 0x0a}, + {WCD934X_CPE_SS_SVA_CFG, 0x60, 0x00}, + {WCD934X_CPE_SS_CPAR_CFG, 0x10, 0x10}, +}; + +static void tavil_codec_init_reg(struct tavil_priv *priv) +{ + struct snd_soc_codec *codec = priv->codec; + u32 i; + + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_common_val); i++) + snd_soc_update_bits(codec, + tavil_codec_reg_init_common_val[i].reg, + tavil_codec_reg_init_common_val[i].mask, + tavil_codec_reg_init_common_val[i].val); + + if (TAVIL_IS_1_1(priv->wcd9xxx)) { + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_1_1_val); i++) + snd_soc_update_bits(codec, + tavil_codec_reg_init_1_1_val[i].reg, + tavil_codec_reg_init_1_1_val[i].mask, + tavil_codec_reg_init_1_1_val[i].val); + } +} + +static void tavil_update_reg_defaults(struct tavil_priv *tavil) +{ + u32 i; + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tavil->wcd9xxx; + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_defaults); i++) + regmap_update_bits(wcd9xxx->regmap, + tavil_codec_reg_defaults[i].reg, + tavil_codec_reg_defaults[i].mask, + tavil_codec_reg_defaults[i].val); +} + +static void tavil_update_cpr_defaults(struct tavil_priv *tavil) +{ + int i; + struct wcd9xxx *wcd9xxx; + + wcd9xxx = tavil->wcd9xxx; + if (!TAVIL_IS_1_1(wcd9xxx)) + return; + + __tavil_cdc_mclk_enable(tavil, true); + + regmap_write(wcd9xxx->regmap, WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD, 0x2C); + regmap_update_bits(wcd9xxx->regmap, WCD934X_CODEC_RPM_CLK_GATE, + 0x10, 0x00); + + for (i = 0; i < ARRAY_SIZE(cpr_defaults); i++) { + regmap_bulk_write(wcd9xxx->regmap, + WCD934X_CODEC_CPR_WR_DATA_0, + (u8 *)&cpr_defaults[i].wr_data, 4); + regmap_bulk_write(wcd9xxx->regmap, + WCD934X_CODEC_CPR_WR_ADDR_0, + (u8 *)&cpr_defaults[i].wr_addr, 4); + } + + __tavil_cdc_mclk_enable(tavil, false); +} + +static void tavil_slim_interface_init_reg(struct snd_soc_codec *codec) +{ + int i; + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + + for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++) + wcd9xxx_interface_reg_write(priv->wcd9xxx, + WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + i, + 0xFF); +} + +static irqreturn_t tavil_misc_irq(int irq, void *data) +{ + struct tavil_priv *tavil = data; + int misc_val; + + /* Find source of interrupt */ + regmap_read(tavil->wcd9xxx->regmap, WCD934X_INTR_CODEC_MISC_STATUS, + &misc_val); + + if (misc_val & 0x08) { + dev_info(tavil->dev, "%s: irq: %d, DSD DC detected!\n", + __func__, irq); + /* DSD DC interrupt, reset DSD path */ + tavil_dsd_reset(tavil->dsd_config); + } else { + dev_err(tavil->dev, "%s: Codec misc irq: %d, val: 0x%x\n", + __func__, irq, misc_val); + } + + /* Clear interrupt status */ + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_INTR_CODEC_MISC_CLEAR, misc_val, 0x00); + + return IRQ_HANDLED; +} + +static irqreturn_t tavil_slimbus_irq(int irq, void *data) +{ + struct tavil_priv *tavil = data; + unsigned long status = 0; + int i, j, port_id, k; + u32 bit; + u8 val, int_val = 0; + bool tx, cleared; + unsigned short reg = 0; + + for (i = WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0; + i <= WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) { + val = wcd9xxx_interface_reg_read(tavil->wcd9xxx, i); + status |= ((u32)val << (8 * j)); + } + + for_each_set_bit(j, &status, 32) { + tx = (j >= 16 ? true : false); + port_id = (tx ? j - 16 : j); + val = wcd9xxx_interface_reg_read(tavil->wcd9xxx, + WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 + j); + if (val) { + if (!tx) + reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + + (port_id / 8); + else + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + tavil->wcd9xxx, reg); + /* + * Ignore interrupts for ports for which the + * interrupts are not specifically enabled. + */ + if (!(int_val & (1 << (port_id % 8)))) + continue; + } + if (val & WCD934X_SLIM_IRQ_OVERFLOW) + dev_err_ratelimited(tavil->dev, "%s: overflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if (val & WCD934X_SLIM_IRQ_UNDERFLOW) + dev_err_ratelimited(tavil->dev, "%s: underflow error on %s port %d, value %x\n", + __func__, (tx ? "TX" : "RX"), port_id, val); + if ((val & WCD934X_SLIM_IRQ_OVERFLOW) || + (val & WCD934X_SLIM_IRQ_UNDERFLOW)) { + if (!tx) + reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + + (port_id / 8); + else + reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + + (port_id / 8); + int_val = wcd9xxx_interface_reg_read( + tavil->wcd9xxx, reg); + if (int_val & (1 << (port_id % 8))) { + int_val = int_val ^ (1 << (port_id % 8)); + wcd9xxx_interface_reg_write(tavil->wcd9xxx, + reg, int_val); + } + } + if (val & WCD934X_SLIM_IRQ_PORT_CLOSED) { + /* + * INT SOURCE register starts from RX to TX + * but port number in the ch_mask is in opposite way + */ + bit = (tx ? j - 16 : j + 16); + dev_dbg(tavil->dev, "%s: %s port %d closed value %x, bit %u\n", + __func__, (tx ? "TX" : "RX"), port_id, val, + bit); + for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) { + dev_dbg(tavil->dev, "%s: tavil->dai[%d].ch_mask = 0x%lx\n", + __func__, k, tavil->dai[k].ch_mask); + if (test_and_clear_bit(bit, + &tavil->dai[k].ch_mask)) { + cleared = true; + if (!tavil->dai[k].ch_mask) + wake_up( + &tavil->dai[k].dai_wait); + /* + * There are cases when multiple DAIs + * might be using the same slimbus + * channel. Hence don't break here. + */ + } + } + WARN(!cleared, + "Couldn't find slimbus %s port %d for closing\n", + (tx ? "TX" : "RX"), port_id); + } + wcd9xxx_interface_reg_write(tavil->wcd9xxx, + WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 + + (j / 8), + 1 << (j % 8)); + } + + return IRQ_HANDLED; +} + +static int tavil_setup_irqs(struct tavil_priv *tavil) +{ + int ret = 0; + struct snd_soc_codec *codec = tavil->codec; + struct wcd9xxx *wcd9xxx = tavil->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS, + tavil_slimbus_irq, "SLIMBUS Slave", tavil); + if (ret) + dev_err(codec->dev, "%s: Failed to request irq %d\n", __func__, + WCD9XXX_IRQ_SLIMBUS); + else + tavil_slim_interface_init_reg(codec); + + /* Register for misc interrupts as well */ + ret = wcd9xxx_request_irq(core_res, WCD934X_IRQ_MISC, + tavil_misc_irq, "CDC MISC Irq", tavil); + if (ret) + dev_err(codec->dev, "%s: Failed to request cdc misc irq\n", + __func__); + + return ret; +} + +static void tavil_init_slim_slave_cfg(struct snd_soc_codec *codec) +{ + struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec); + struct afe_param_cdc_slimbus_slave_cfg *cfg; + struct wcd9xxx *wcd9xxx = priv->wcd9xxx; + uint64_t eaddr = 0; + + cfg = &priv->slimbus_slave_cfg; + cfg->minor_version = 1; + cfg->tx_slave_port_offset = 0; + cfg->rx_slave_port_offset = 16; + + memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr)); + WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6); + cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF; + cfg->device_enum_addr_msw = eaddr >> 32; + + dev_dbg(codec->dev, "%s: slimbus logical address 0x%llx\n", + __func__, eaddr); +} + +static void tavil_cleanup_irqs(struct tavil_priv *tavil) +{ + struct wcd9xxx *wcd9xxx = tavil->wcd9xxx; + struct wcd9xxx_core_resource *core_res = + &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tavil); + wcd9xxx_free_irq(core_res, WCD934X_IRQ_MISC, tavil); +} + +/* + * wcd934x_get_micb_vout_ctl_val: converts micbias from volts to register value + * @micb_mv: micbias in mv + * + * return register value converted + */ +int wcd934x_get_micb_vout_ctl_val(u32 micb_mv) +{ + /* min micbias voltage is 1V and maximum is 2.85V */ + if (micb_mv < 1000 || micb_mv > 2850) { + pr_err("%s: unsupported micbias voltage\n", __func__); + return -EINVAL; + } + + return (micb_mv - 1000) / 50; +} +EXPORT_SYMBOL(wcd934x_get_micb_vout_ctl_val); + +static int tavil_handle_pdata(struct tavil_priv *tavil, + struct wcd9xxx_pdata *pdata) +{ + struct snd_soc_codec *codec = tavil->codec; + u8 mad_dmic_ctl_val; + u8 anc_ctl_value; + u32 def_dmic_rate, dmic_clk_drv; + int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4; + int rc = 0; + + if (!pdata) { + dev_err(codec->dev, "%s: NULL pdata\n", __func__); + return -ENODEV; + } + + /* set micbias voltage */ + vout_ctl_1 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb1_mv); + vout_ctl_2 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb2_mv); + vout_ctl_3 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb3_mv); + vout_ctl_4 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb4_mv); + if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || + vout_ctl_3 < 0 || vout_ctl_4 < 0) { + rc = -EINVAL; + goto done; + } + snd_soc_update_bits(codec, WCD934X_ANA_MICB1, 0x3F, vout_ctl_1); + snd_soc_update_bits(codec, WCD934X_ANA_MICB2, 0x3F, vout_ctl_2); + snd_soc_update_bits(codec, WCD934X_ANA_MICB3, 0x3F, vout_ctl_3); + snd_soc_update_bits(codec, WCD934X_ANA_MICB4, 0x3F, vout_ctl_4); + + /* Set the DMIC sample rate */ + switch (pdata->mclk_rate) { + case WCD934X_MCLK_CLK_9P6MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ; + break; + case WCD934X_MCLK_CLK_12P288MHZ: + def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ; + break; + default: + /* should never happen */ + dev_err(codec->dev, "%s: Invalid mclk_rate %d\n", + __func__, pdata->mclk_rate); + rc = -EINVAL; + goto done; + }; + + if (pdata->dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + pdata->dmic_sample_rate = def_dmic_rate; + } + if (pdata->mad_dmic_sample_rate == + WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) { + dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n", + __func__, def_dmic_rate); + /* + * use dmic_sample_rate as the default for MAD + * if mad dmic sample rate is undefined + */ + pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate; + } + + if (pdata->dmic_clk_drv == + WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) { + pdata->dmic_clk_drv = WCD934X_DMIC_CLK_DRIVE_DEFAULT; + dev_dbg(codec->dev, + "%s: dmic_clk_strength invalid, default = %d\n", + __func__, pdata->dmic_clk_drv); + } + + switch (pdata->dmic_clk_drv) { + case 2: + dmic_clk_drv = 0; + break; + case 4: + dmic_clk_drv = 1; + break; + case 8: + dmic_clk_drv = 2; + break; + case 16: + dmic_clk_drv = 3; + break; + default: + dev_err(codec->dev, + "%s: invalid dmic_clk_drv %d, using default\n", + __func__, pdata->dmic_clk_drv); + dmic_clk_drv = 0; + break; + } + + snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_PAD_DRVCTL_0, + 0x0C, dmic_clk_drv << 2); + + /* + * Default the DMIC clk rates to mad_dmic_sample_rate, + * whereas, the anc/txfe dmic rates to dmic_sample_rate + * since the anc/txfe are independent of mad block. + */ + mad_dmic_ctl_val = tavil_get_dmic_clk_val(tavil->codec, + pdata->mclk_rate, + pdata->mad_dmic_sample_rate); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC0_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC1_CTL, + 0x0E, mad_dmic_ctl_val << 1); + snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC2_CTL, + 0x0E, mad_dmic_ctl_val << 1); + + if (dmic_clk_drv == WCD934X_DMIC_CLK_DIV_2) + anc_ctl_value = WCD934X_ANC_DMIC_X2_FULL_RATE; + else + anc_ctl_value = WCD934X_ANC_DMIC_X2_HALF_RATE; + + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL, + 0x40, anc_ctl_value << 6); + snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL, + 0x20, anc_ctl_value << 5); + +done: + return rc; +} + +static void tavil_cdc_vote_svs(struct snd_soc_codec *codec, bool vote) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + return tavil_vote_svs(tavil, vote); +} + +struct wcd_dsp_cdc_cb cdc_cb = { + .cdc_clk_en = tavil_codec_internal_rco_ctrl, + .cdc_vote_svs = tavil_cdc_vote_svs, +}; + +static int tavil_wdsp_initialize(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tavil_priv *tavil; + struct wcd_dsp_params params; + int ret = 0; + + control = dev_get_drvdata(codec->dev->parent); + tavil = snd_soc_codec_get_drvdata(codec); + + params.cb = &cdc_cb; + params.irqs.cpe_ipc1_irq = WCD934X_IRQ_CPE1_INTR; + params.irqs.cpe_err_irq = WCD934X_IRQ_CPE_ERROR; + params.irqs.fatal_irqs = CPE_FATAL_IRQS; + params.clk_rate = control->mclk_rate; + params.dsp_instance = 0; + + wcd_dsp_cntl_init(codec, ¶ms, &tavil->wdsp_cntl); + if (!tavil->wdsp_cntl) { + dev_err(tavil->dev, "%s: wcd-dsp-control init failed\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + +/* + * tavil_soc_get_mbhc: get wcd934x_mbhc handle of corresponding codec + * @codec: handle to snd_soc_codec * + * + * return wcd934x_mbhc handle or error code in case of failure + */ +struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec) +{ + struct tavil_priv *tavil; + + if (!codec) { + pr_err("%s: Invalid params, NULL codec\n", __func__); + return NULL; + } + tavil = snd_soc_codec_get_drvdata(codec); + + if (!tavil) { + pr_err("%s: Invalid params, NULL tavil\n", __func__); + return NULL; + } + + return tavil->mbhc; +} +EXPORT_SYMBOL(tavil_soc_get_mbhc); + +static void tavil_mclk2_reg_defaults(struct tavil_priv *tavil) +{ + int i; + struct snd_soc_codec *codec = tavil->codec; + + if (TAVIL_IS_1_0(tavil->wcd9xxx)) { + /* MCLK2 configuration */ + for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_0_defaults); i++) + snd_soc_update_bits(codec, + tavil_codec_mclk2_1_0_defaults[i].reg, + tavil_codec_mclk2_1_0_defaults[i].mask, + tavil_codec_mclk2_1_0_defaults[i].val); + } + if (TAVIL_IS_1_1(tavil->wcd9xxx)) { + /* MCLK2 configuration */ + for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_1_defaults); i++) + snd_soc_update_bits(codec, + tavil_codec_mclk2_1_1_defaults[i].reg, + tavil_codec_mclk2_1_1_defaults[i].mask, + tavil_codec_mclk2_1_1_defaults[i].val); + } +} + +static int tavil_device_down(struct wcd9xxx *wcd9xxx) +{ + struct snd_soc_codec *codec; + struct tavil_priv *priv; + int count; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + priv = snd_soc_codec_get_drvdata(codec); + if (priv->swr.ctrl_data) + swrm_wcd_notify(priv->swr.ctrl_data[0].swr_pdev, + SWR_DEVICE_DOWN, NULL); + tavil_dsd_reset(priv->dsd_config); + snd_soc_card_change_online_state(codec->component.card, 0); + for (count = 0; count < NUM_CODEC_DAIS; count++) + priv->dai[count].bus_down_in_recovery = true; + wcd_dsp_ssr_event(priv->wdsp_cntl, WCD_CDC_DOWN_EVENT); + wcd_resmgr_set_sido_input_src_locked(priv->resmgr, + SIDO_SOURCE_INTERNAL); + + return 0; +} + +static int tavil_post_reset_cb(struct wcd9xxx *wcd9xxx) +{ + int i, ret = 0; + struct wcd9xxx *control; + struct snd_soc_codec *codec; + struct tavil_priv *tavil; + struct wcd9xxx_pdata *pdata; + struct wcd_mbhc *mbhc; + + codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); + tavil = snd_soc_codec_get_drvdata(codec); + control = dev_get_drvdata(codec->dev->parent); + + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + + mutex_lock(&tavil->codec_mutex); + + tavil_vote_svs(tavil, true); + tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tavil_init_slim_slave_cfg(codec); + snd_soc_card_change_online_state(codec->component.card, 1); + + for (i = 0; i < TAVIL_MAX_MICBIAS; i++) + tavil->micb_ref[i] = 0; + + dev_dbg(codec->dev, "%s: MCLK Rate = %x\n", + __func__, control->mclk_rate); + + if (control->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ) + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (control->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + wcd_resmgr_post_ssr_v2(tavil->resmgr); + tavil_update_reg_defaults(tavil); + tavil_codec_init_reg(tavil); + __tavil_enable_efuse_sensing(tavil); + tavil_mclk2_reg_defaults(tavil); + + __tavil_cdc_mclk_enable(tavil, true); + regcache_mark_dirty(codec->component.regmap); + regcache_sync(codec->component.regmap); + __tavil_cdc_mclk_enable(tavil, false); + + tavil_update_cpr_defaults(tavil); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tavil_handle_pdata(tavil, pdata); + if (ret < 0) + dev_err(codec->dev, "%s: invalid pdata\n", __func__); + + /* Initialize MBHC module */ + mbhc = &tavil->mbhc->wcd_mbhc; + ret = tavil_mbhc_post_ssr_init(tavil->mbhc, codec); + if (ret) { + dev_err(codec->dev, "%s: mbhc initialization failed\n", + __func__); + goto done; + } else { + tavil_mbhc_hs_detect(codec, mbhc->mbhc_cfg); + } + + /* DSD initialization */ + ret = tavil_dsd_post_ssr_init(tavil->dsd_config); + if (ret) + dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__); + + tavil_cleanup_irqs(tavil); + ret = tavil_setup_irqs(tavil); + if (ret) { + dev_err(codec->dev, "%s: tavil irq setup failed %d\n", + __func__, ret); + goto done; + } + + tavil_set_spkr_mode(codec, tavil->swr.spkr_mode); + /* + * Once the codec initialization is completed, the svs vote + * can be released allowing the codec to go to SVS2. + */ + tavil_vote_svs(tavil, false); + wcd_dsp_ssr_event(tavil->wdsp_cntl, WCD_CDC_UP_EVENT); + +done: + mutex_unlock(&tavil->codec_mutex); + return ret; +} + +static int tavil_soc_codec_probe(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tavil_priv *tavil; + struct wcd9xxx_pdata *pdata; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + int i, ret; + void *ptr = NULL; + + control = dev_get_drvdata(codec->dev->parent); + + dev_info(codec->dev, "%s()\n", __func__); + tavil = snd_soc_codec_get_drvdata(codec); + tavil->intf_type = wcd9xxx_get_intf_type(); + + control->dev_down = tavil_device_down; + control->post_reset = tavil_post_reset_cb; + control->ssr_priv = (void *)codec; + + /* Resource Manager post Init */ + ret = wcd_resmgr_post_init(tavil->resmgr, &tavil_resmgr_cb, codec); + if (ret) { + dev_err(codec->dev, "%s: wcd resmgr post init failed\n", + __func__); + goto err; + } + /* Class-H Init */ + wcd_clsh_init(&tavil->clsh_d); + /* Default HPH Mode to Class-H Low HiFi */ + tavil->hph_mode = CLS_H_LOHIFI; + + tavil->fw_data = devm_kzalloc(codec->dev, sizeof(*(tavil->fw_data)), + GFP_KERNEL); + if (!tavil->fw_data) + goto err; + + set_bit(WCD9XXX_ANC_CAL, tavil->fw_data->cal_bit); + set_bit(WCD9XXX_MBHC_CAL, tavil->fw_data->cal_bit); + set_bit(WCD9XXX_MAD_CAL, tavil->fw_data->cal_bit); + set_bit(WCD9XXX_VBAT_CAL, tavil->fw_data->cal_bit); + + ret = wcd_cal_create_hwdep(tavil->fw_data, + WCD9XXX_CODEC_HWDEP_NODE, codec); + if (ret < 0) { + dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret); + goto err_hwdep; + } + + /* Initialize MBHC module */ + ret = tavil_mbhc_init(&tavil->mbhc, codec, tavil->fw_data); + if (ret) { + pr_err("%s: mbhc initialization failed\n", __func__); + goto err_hwdep; + } + + tavil->codec = codec; + for (i = 0; i < COMPANDER_MAX; i++) + tavil->comp_enabled[i] = 0; + + tavil_codec_init_reg(tavil); + + pdata = dev_get_platdata(codec->dev->parent); + ret = tavil_handle_pdata(tavil, pdata); + if (ret < 0) { + dev_err(codec->dev, "%s: bad pdata\n", __func__); + goto err_hwdep; + } + + ptr = devm_kzalloc(codec->dev, (sizeof(tavil_rx_chs) + + sizeof(tavil_tx_chs)), GFP_KERNEL); + if (!ptr) { + ret = -ENOMEM; + goto err_hwdep; + } + + snd_soc_dapm_add_routes(dapm, tavil_slim_audio_map, + ARRAY_SIZE(tavil_slim_audio_map)); + for (i = 0; i < NUM_CODEC_DAIS; i++) { + INIT_LIST_HEAD(&tavil->dai[i].wcd9xxx_ch_list); + init_waitqueue_head(&tavil->dai[i].dai_wait); + } + tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tavil_slimbus_slave_port_cfg.slave_port_mapping[0] = + WCD934X_TX13; + tavil_init_slim_slave_cfg(codec); + + control->num_rx_port = WCD934X_RX_MAX; + control->rx_chs = ptr; + memcpy(control->rx_chs, tavil_rx_chs, sizeof(tavil_rx_chs)); + control->num_tx_port = WCD934X_TX_MAX; + control->tx_chs = ptr + sizeof(tavil_rx_chs); + memcpy(control->tx_chs, tavil_tx_chs, sizeof(tavil_tx_chs)); + + ret = tavil_setup_irqs(tavil); + if (ret) { + dev_err(tavil->dev, "%s: tavil irq setup failed %d\n", + __func__, ret); + goto err_pdata; + } + + for (i = 0; i < WCD934X_NUM_DECIMATORS; i++) { + tavil->tx_hpf_work[i].tavil = tavil; + tavil->tx_hpf_work[i].decimator = i; + INIT_DELAYED_WORK(&tavil->tx_hpf_work[i].dwork, + tavil_tx_hpf_corner_freq_callback); + + tavil->tx_mute_dwork[i].tavil = tavil; + tavil->tx_mute_dwork[i].decimator = i; + INIT_DELAYED_WORK(&tavil->tx_mute_dwork[i].dwork, + tavil_tx_mute_update_callback); + } + + tavil->spk_anc_dwork.tavil = tavil; + INIT_DELAYED_WORK(&tavil->spk_anc_dwork.dwork, + tavil_spk_anc_update_callback); + + tavil_mclk2_reg_defaults(tavil); + + /* DSD initialization */ + tavil->dsd_config = tavil_dsd_init(codec); + if (IS_ERR_OR_NULL(tavil->dsd_config)) + dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__); + + mutex_lock(&tavil->codec_mutex); + snd_soc_dapm_disable_pin(dapm, "ANC EAR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC EAR"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHL"); + snd_soc_dapm_disable_pin(dapm, "ANC HPHR"); + snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA"); + mutex_unlock(&tavil->codec_mutex); + + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + + snd_soc_dapm_sync(dapm); + + tavil_wdsp_initialize(codec); + + /* + * Once the codec initialization is completed, the svs vote + * can be released allowing the codec to go to SVS2. + */ + tavil_vote_svs(tavil, false); + + return ret; + +err_pdata: + devm_kfree(codec->dev, ptr); + control->rx_chs = NULL; + control->tx_chs = NULL; +err_hwdep: + devm_kfree(codec->dev, tavil->fw_data); + tavil->fw_data = NULL; +err: + return ret; +} + +static int tavil_soc_codec_remove(struct snd_soc_codec *codec) +{ + struct wcd9xxx *control; + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); + + control = dev_get_drvdata(codec->dev->parent); + devm_kfree(codec->dev, control->rx_chs); + control->rx_chs = NULL; + control->tx_chs = NULL; + tavil_cleanup_irqs(tavil); + + if (tavil->wdsp_cntl) + wcd_dsp_cntl_deinit(&tavil->wdsp_cntl); + + /* Deinitialize MBHC module */ + tavil_mbhc_deinit(codec); + tavil->mbhc = NULL; + + return 0; +} + +static struct regmap *tavil_get_regmap(struct device *dev) +{ + struct wcd9xxx *control = dev_get_drvdata(dev->parent); + + return control->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_tavil = { + .probe = tavil_soc_codec_probe, + .remove = tavil_soc_codec_remove, + .get_regmap = tavil_get_regmap, + .component_driver = { + .controls = tavil_snd_controls, + .num_controls = ARRAY_SIZE(tavil_snd_controls), + .dapm_widgets = tavil_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tavil_dapm_widgets), + .dapm_routes = tavil_audio_map, + .num_dapm_routes = ARRAY_SIZE(tavil_audio_map), + }, +}; + +#ifdef CONFIG_PM +static int tavil_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tavil_priv *tavil = platform_get_drvdata(pdev); + + if (!tavil) { + dev_err(dev, "%s: tavil private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system suspend\n", __func__); + if (delayed_work_pending(&tavil->power_gate_work) && + cancel_delayed_work_sync(&tavil->power_gate_work)) + tavil_codec_power_gate_digital_core(tavil); + return 0; +} + +static int tavil_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tavil_priv *tavil = platform_get_drvdata(pdev); + + if (!tavil) { + dev_err(dev, "%s: tavil private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system resume\n", __func__); + return 0; +} + +static const struct dev_pm_ops tavil_pm_ops = { + .suspend = tavil_suspend, + .resume = tavil_resume, +}; +#endif + +static int tavil_swrm_read(void *handle, int reg) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + unsigned short swr_rd_addr_base; + unsigned short swr_rd_data_base; + int val, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + wcd9xxx = tavil->wcd9xxx; + + dev_dbg(tavil->dev, "%s: Reading soundwire register, 0x%x\n", + __func__, reg); + swr_rd_addr_base = WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0; + swr_rd_data_base = WCD934X_SWR_AHB_BRIDGE_RD_DATA_0; + + mutex_lock(&tavil->swr.read_mutex); + ret = regmap_bulk_write(wcd9xxx->regmap, swr_rd_addr_base, + (u8 *)®, 4); + if (ret < 0) { + dev_err(tavil->dev, "%s: RD Addr Failure\n", __func__); + goto done; + } + ret = regmap_bulk_read(wcd9xxx->regmap, swr_rd_data_base, + (u8 *)&val, 4); + if (ret < 0) { + dev_err(tavil->dev, "%s: RD Data Failure\n", __func__); + goto done; + } + ret = val; +done: + mutex_unlock(&tavil->swr.read_mutex); + + return ret; +} + +static int tavil_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + struct wcd9xxx_reg_val *bulk_reg; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + int i, j, ret; + + if (!handle || !reg || !val) { + pr_err("%s: NULL parameter\n", __func__); + return -EINVAL; + } + if (len <= 0) { + pr_err("%s: Invalid size: %zu\n", __func__, len); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + wcd9xxx = tavil->wcd9xxx; + + swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0; + + bulk_reg = kzalloc((2 * len * sizeof(struct wcd9xxx_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + for (i = 0, j = 0; i < (len * 2); i += 2, j++) { + bulk_reg[i].reg = swr_wr_data_base; + bulk_reg[i].buf = (u8 *)(&val[j]); + bulk_reg[i].bytes = 4; + bulk_reg[i+1].reg = swr_wr_addr_base; + bulk_reg[i+1].buf = (u8 *)(®[j]); + bulk_reg[i+1].bytes = 4; + } + + mutex_lock(&tavil->swr.write_mutex); + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + (len * 2), false); + if (ret) { + dev_err(tavil->dev, "%s: swrm bulk write failed, ret: %d\n", + __func__, ret); + } + mutex_unlock(&tavil->swr.write_mutex); + + kfree(bulk_reg); + return ret; +} + +static int tavil_swrm_write(void *handle, int reg, int val) +{ + struct tavil_priv *tavil; + struct wcd9xxx *wcd9xxx; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + struct wcd9xxx_reg_val bulk_reg[2]; + int ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + wcd9xxx = tavil->wcd9xxx; + + swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0; + + /* First Write the Data to register */ + bulk_reg[0].reg = swr_wr_data_base; + bulk_reg[0].buf = (u8 *)(&val); + bulk_reg[0].bytes = 4; + bulk_reg[1].reg = swr_wr_addr_base; + bulk_reg[1].buf = (u8 *)(®); + bulk_reg[1].bytes = 4; + + mutex_lock(&tavil->swr.write_mutex); + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false); + if (ret < 0) + dev_err(tavil->dev, "%s: WR Data Failure\n", __func__); + mutex_unlock(&tavil->swr.write_mutex); + + return ret; +} + +static int tavil_swrm_clock(void *handle, bool enable) +{ + struct tavil_priv *tavil; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *)handle; + + mutex_lock(&tavil->swr.clk_mutex); + dev_dbg(tavil->dev, "%s: swrm clock %s\n", + __func__, (enable?"enable" : "disable")); + if (enable) { + tavil->swr.clk_users++; + if (tavil->swr.clk_users == 1) { + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x00); + __tavil_cdc_mclk_enable(tavil, true); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x01); + } + } else { + tavil->swr.clk_users--; + if (tavil->swr.clk_users == 0) { + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + __tavil_cdc_mclk_enable(tavil, false); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_TEST_DEBUG_NPL_DLY_TEST_1, + 0x10, 0x10); + } + } + dev_dbg(tavil->dev, "%s: swrm clock users %d\n", + __func__, tavil->swr.clk_users); + mutex_unlock(&tavil->swr.clk_mutex); + + return 0; +} + +static int tavil_swrm_handle_irq(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action) +{ + struct tavil_priv *tavil; + int ret = 0; + struct wcd9xxx *wcd9xxx; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + tavil = (struct tavil_priv *) handle; + wcd9xxx = tavil->wcd9xxx; + + if (action) { + ret = wcd9xxx_request_irq(&wcd9xxx->core_res, + WCD934X_IRQ_SOUNDWIRE, + swrm_irq_handler, + "Tavil SWR Master", swrm_handle); + if (ret) + dev_err(tavil->dev, "%s: Failed to request irq %d\n", + __func__, WCD934X_IRQ_SOUNDWIRE); + } else + wcd9xxx_free_irq(&wcd9xxx->core_res, WCD934X_IRQ_SOUNDWIRE, + swrm_handle); + + return ret; +} + +static void tavil_codec_add_spi_device(struct tavil_priv *tavil, + struct device_node *node) +{ + struct spi_master *master; + struct spi_device *spi; + u32 prop_value; + int rc; + + /* Read the master bus num from DT node */ + rc = of_property_read_u32(node, "qcom,master-bus-num", + &prop_value); + if (rc < 0) { + dev_err(tavil->dev, "%s: prop %s not found in node %s", + __func__, "qcom,master-bus-num", node->full_name); + goto done; + } + + /* Get the reference to SPI master */ + master = spi_busnum_to_master(prop_value); + if (!master) { + dev_err(tavil->dev, "%s: Invalid spi_master for bus_num %u\n", + __func__, prop_value); + goto done; + } + + /* Allocate the spi device */ + spi = spi_alloc_device(master); + if (!spi) { + dev_err(tavil->dev, "%s: spi_alloc_device failed\n", + __func__); + goto err_spi_alloc_dev; + } + + /* Initialize device properties */ + if (of_modalias_node(node, spi->modalias, + sizeof(spi->modalias)) < 0) { + dev_err(tavil->dev, "%s: cannot find modalias for %s\n", + __func__, node->full_name); + goto err_dt_parse; + } + + rc = of_property_read_u32(node, "qcom,chip-select", + &prop_value); + if (rc < 0) { + dev_err(tavil->dev, "%s: prop %s not found in node %s", + __func__, "qcom,chip-select", node->full_name); + goto err_dt_parse; + } + spi->chip_select = prop_value; + + rc = of_property_read_u32(node, "qcom,max-frequency", + &prop_value); + if (rc < 0) { + dev_err(tavil->dev, "%s: prop %s not found in node %s", + __func__, "qcom,max-frequency", node->full_name); + goto err_dt_parse; + } + spi->max_speed_hz = prop_value; + + spi->dev.of_node = node; + + rc = spi_add_device(spi); + if (rc < 0) { + dev_err(tavil->dev, "%s: spi_add_device failed\n", __func__); + goto err_dt_parse; + } + + /* Put the reference to SPI master */ + put_device(&master->dev); + + return; + +err_dt_parse: + spi_dev_put(spi); + +err_spi_alloc_dev: + /* Put the reference to SPI master */ + put_device(&master->dev); +done: + return; +} + +static void tavil_add_child_devices(struct work_struct *work) +{ + struct tavil_priv *tavil; + struct platform_device *pdev; + struct device_node *node; + struct wcd9xxx *wcd9xxx; + struct tavil_swr_ctrl_data *swr_ctrl_data = NULL, *temp; + int ret, ctrl_num = 0; + struct wcd_swr_ctrl_platform_data *platdata; + char plat_dev_name[WCD934X_STRING_LEN]; + + tavil = container_of(work, struct tavil_priv, + tavil_add_child_devices_work); + if (!tavil) { + pr_err("%s: Memory for WCD934X does not exist\n", + __func__); + return; + } + wcd9xxx = tavil->wcd9xxx; + if (!wcd9xxx) { + pr_err("%s: Memory for WCD9XXX does not exist\n", + __func__); + return; + } + if (!wcd9xxx->dev->of_node) { + dev_err(wcd9xxx->dev, "%s: DT node for wcd9xxx does not exist\n", + __func__); + return; + } + + platdata = &tavil->swr.plat_data; + + for_each_child_of_node(wcd9xxx->dev->of_node, node) { + + /* Parse and add the SPI device node */ + if (!strcmp(node->name, "wcd_spi")) { + tavil_codec_add_spi_device(tavil, node); + continue; + } + + /* Parse other child device nodes and add platform device */ + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "tavil_swr_ctrl", + (WCD934X_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (WCD934X_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err_mem; + } + pdev->dev.parent = tavil->dev; + pdev->dev.of_node = node; + + if (strcmp(node->name, "swr_master") == 0) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto err_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto err_pdev_add; + } + + if (strcmp(node->name, "swr_master") == 0) { + temp = krealloc(swr_ctrl_data, + (ctrl_num + 1) * sizeof( + struct tavil_swr_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(wcd9xxx->dev, "out of memory\n"); + ret = -ENOMEM; + goto err_pdev_add; + } + swr_ctrl_data = temp; + swr_ctrl_data[ctrl_num].swr_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + tavil->swr.ctrl_data = swr_ctrl_data; + } + } + + return; + +err_pdev_add: + platform_device_put(pdev); +err_mem: + return; +} + +static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil) +{ + int val, rc; + + WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr); + __tavil_cdc_mclk_enable_locked(tavil, true); + + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x10); + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x01, 0x01); + /* + * 5ms sleep required after enabling efuse control + * before checking the status. + */ + usleep_range(5000, 5500); + wcd_resmgr_set_sido_input_src(tavil->resmgr, + SIDO_SOURCE_RCO_BG); + + WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr); + + rc = regmap_read(tavil->wcd9xxx->regmap, + WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, &val); + if (rc || (!(val & 0x01))) + WARN(1, "%s: Efuse sense is not complete val=%x, ret=%d\n", + __func__, val, rc); + + __tavil_cdc_mclk_enable(tavil, false); + + return rc; +} + +static void ___tavil_get_codec_fine_version(struct tavil_priv *tavil) +{ + int val1, val2, version; + struct regmap *regmap; + u16 id_minor; + u32 version_mask = 0; + + regmap = tavil->wcd9xxx->regmap; + version = tavil->wcd9xxx->version; + id_minor = tavil->wcd9xxx->codec_type->id_minor; + + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val1); + regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val2); + + dev_dbg(tavil->dev, "%s: chip version :0x%x 0x:%x\n", + __func__, val1, val2); + + version_mask |= (!!((u8)val1 & 0x80)) << DSD_DISABLED_MASK; + version_mask |= (!!((u8)val2 & 0x01)) << SLNQ_DISABLED_MASK; + + switch (version_mask) { + case DSD_DISABLED | SLNQ_DISABLED: + if (id_minor == cpu_to_le16(0)) + version = TAVIL_VERSION_WCD9340_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_WCD9340_1_1; + break; + case SLNQ_DISABLED: + if (id_minor == cpu_to_le16(0)) + version = TAVIL_VERSION_WCD9341_1_0; + else if (id_minor == cpu_to_le16(0x01)) + version = TAVIL_VERSION_WCD9341_1_1; + break; + } + + tavil->wcd9xxx->version = version; + tavil->wcd9xxx->codec_type->version = version; +} + +/* + * tavil_get_wcd_dsp_cntl: Get the reference to wcd_dsp_cntl + * @dev: Device pointer for codec device + * + * This API gets the reference to codec's struct wcd_dsp_cntl + */ +struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev) +{ + struct platform_device *pdev; + struct tavil_priv *tavil; + + if (!dev) { + pr_err("%s: Invalid device\n", __func__); + return NULL; + } + + pdev = to_platform_device(dev); + tavil = platform_get_drvdata(pdev); + + return tavil->wdsp_cntl; +} +EXPORT_SYMBOL(tavil_get_wcd_dsp_cntl); + +static int tavil_probe(struct platform_device *pdev) +{ + int ret = 0; + struct tavil_priv *tavil; + struct clk *wcd_ext_clk; + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9xxx_power_region *cdc_pwr; + + tavil = devm_kzalloc(&pdev->dev, sizeof(struct tavil_priv), + GFP_KERNEL); + if (!tavil) + return -ENOMEM; + + platform_set_drvdata(pdev, tavil); + + tavil->wcd9xxx = dev_get_drvdata(pdev->dev.parent); + tavil->dev = &pdev->dev; + INIT_DELAYED_WORK(&tavil->power_gate_work, tavil_codec_power_gate_work); + mutex_init(&tavil->power_lock); + INIT_WORK(&tavil->tavil_add_child_devices_work, + tavil_add_child_devices); + mutex_init(&tavil->micb_lock); + mutex_init(&tavil->swr.read_mutex); + mutex_init(&tavil->swr.write_mutex); + mutex_init(&tavil->swr.clk_mutex); + mutex_init(&tavil->codec_mutex); + mutex_init(&tavil->svs_mutex); + + /* + * Codec hardware by default comes up in SVS mode. + * Initialize the svs_ref_cnt to 1 to reflect the hardware + * state in the driver. + */ + tavil->svs_ref_cnt = 1; + + cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region), + GFP_KERNEL); + if (!cdc_pwr) { + ret = -ENOMEM; + goto err_resmgr; + } + tavil->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr; + cdc_pwr->pwr_collapse_reg_min = WCD934X_DIG_CORE_REG_MIN; + cdc_pwr->pwr_collapse_reg_max = WCD934X_DIG_CORE_REG_MAX; + wcd9xxx_set_power_state(tavil->wcd9xxx, + WCD_REGION_POWER_COLLAPSE_REMOVE, + WCD9XXX_DIG_CORE_REGION_1); + /* + * Init resource manager so that if child nodes such as SoundWire + * requests for clock, resource manager can honor the request + */ + resmgr = wcd_resmgr_init(&tavil->wcd9xxx->core_res, NULL); + if (IS_ERR(resmgr)) { + ret = PTR_ERR(resmgr); + dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n", + __func__); + goto err_resmgr; + } + tavil->resmgr = resmgr; + tavil->swr.plat_data.handle = (void *) tavil; + tavil->swr.plat_data.read = tavil_swrm_read; + tavil->swr.plat_data.write = tavil_swrm_write; + tavil->swr.plat_data.bulk_write = tavil_swrm_bulk_write; + tavil->swr.plat_data.clk = tavil_swrm_clock; + tavil->swr.plat_data.handle_irq = tavil_swrm_handle_irq; + tavil->swr.spkr_gain_offset = WCD934X_RX_GAIN_OFFSET_0_DB; + + /* Register for Clock */ + wcd_ext_clk = clk_get(tavil->wcd9xxx->dev, "wcd_clk"); + if (IS_ERR(wcd_ext_clk)) { + dev_err(tavil->wcd9xxx->dev, "%s: clk get %s failed\n", + __func__, "wcd_ext_clk"); + goto err_clk; + } + tavil->wcd_ext_clk = wcd_ext_clk; + set_bit(AUDIO_NOMINAL, &tavil->status_mask); + /* Update codec register default values */ + dev_dbg(&pdev->dev, "%s: MCLK Rate = %x\n", __func__, + tavil->wcd9xxx->mclk_rate); + if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ) + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x00); + else if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) + regmap_update_bits(tavil->wcd9xxx->regmap, + WCD934X_CODEC_RPM_CLK_MCLK_CFG, + 0x03, 0x01); + tavil_update_reg_defaults(tavil); + __tavil_enable_efuse_sensing(tavil); + ___tavil_get_codec_fine_version(tavil); + tavil_update_cpr_defaults(tavil); + + /* Register with soc framework */ + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil, + tavil_dai, ARRAY_SIZE(tavil_dai)); + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed\n", + __func__); + goto err_cdc_reg; + } + schedule_work(&tavil->tavil_add_child_devices_work); + + return ret; + +err_cdc_reg: + clk_put(tavil->wcd_ext_clk); +err_clk: + wcd_resmgr_remove(tavil->resmgr); +err_resmgr: + mutex_destroy(&tavil->micb_lock); + mutex_destroy(&tavil->svs_mutex); + mutex_destroy(&tavil->codec_mutex); + mutex_destroy(&tavil->swr.read_mutex); + mutex_destroy(&tavil->swr.write_mutex); + mutex_destroy(&tavil->swr.clk_mutex); + devm_kfree(&pdev->dev, tavil); + + return ret; +} + +static int tavil_remove(struct platform_device *pdev) +{ + struct tavil_priv *tavil; + + tavil = platform_get_drvdata(pdev); + if (!tavil) + return -EINVAL; + + mutex_destroy(&tavil->micb_lock); + mutex_destroy(&tavil->svs_mutex); + mutex_destroy(&tavil->codec_mutex); + mutex_destroy(&tavil->swr.read_mutex); + mutex_destroy(&tavil->swr.write_mutex); + mutex_destroy(&tavil->swr.clk_mutex); + + snd_soc_unregister_codec(&pdev->dev); + clk_put(tavil->wcd_ext_clk); + wcd_resmgr_remove(tavil->resmgr); + if (tavil->dsd_config) { + tavil_dsd_deinit(tavil->dsd_config); + tavil->dsd_config = NULL; + } + devm_kfree(&pdev->dev, tavil); + return 0; +} + +static struct platform_driver tavil_codec_driver = { + .probe = tavil_probe, + .remove = tavil_remove, + .driver = { + .name = "tavil_codec", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &tavil_pm_ops, +#endif + }, +}; + +module_platform_driver(tavil_codec_driver); + +MODULE_DESCRIPTION("Tavil Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd934x/wcd934x.h b/sound/soc/codecs/wcd934x/wcd934x.h new file mode 100644 index 000000000000..c3bf50a4ffdb --- /dev/null +++ b/sound/soc/codecs/wcd934x/wcd934x.h @@ -0,0 +1,158 @@ +/* + * 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 WCD934X_H +#define WCD934X_H + +#include +#include +#include "wcd934x-dsp-cntl.h" +#include "../wcd9xxx-common-v2.h" +#include "../wcd-mbhc-v2.h" + +#define WCD934X_REGISTER_START_OFFSET 0x800 +#define WCD934X_SB_PGD_PORT_RX_BASE 0x40 +#define WCD934X_SB_PGD_PORT_TX_BASE 0x50 +#define WCD934X_RX_PORT_START_NUMBER 16 + +#define WCD934X_DMIC_CLK_DIV_2 0x0 +#define WCD934X_DMIC_CLK_DIV_3 0x1 +#define WCD934X_DMIC_CLK_DIV_4 0x2 +#define WCD934X_DMIC_CLK_DIV_6 0x3 +#define WCD934X_DMIC_CLK_DIV_8 0x4 +#define WCD934X_DMIC_CLK_DIV_16 0x5 +#define WCD934X_DMIC_CLK_DRIVE_DEFAULT 0x02 + +#define WCD934X_ANC_DMIC_X2_FULL_RATE 1 +#define WCD934X_ANC_DMIC_X2_HALF_RATE 0 + +#define TAVIL_MAX_MICBIAS 4 +#define TAVIL_NUM_INTERPOLATORS 9 +#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH 64 + +/* Convert from vout ctl to micbias voltage in mV */ +#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) + +/* Feature masks to distinguish codec version */ +#define DSD_DISABLED_MASK 0 +#define SLNQ_DISABLED_MASK 1 + +#define DSD_DISABLED (1 << DSD_DISABLED_MASK) +#define SLNQ_DISABLED (1 << SLNQ_DISABLED_MASK) + +/* Number of input and output Slimbus port */ +enum { + WCD934X_RX0 = 0, + WCD934X_RX1, + WCD934X_RX2, + WCD934X_RX3, + WCD934X_RX4, + WCD934X_RX5, + WCD934X_RX6, + WCD934X_RX7, + WCD934X_RX_MAX, +}; + +enum { + WCD934X_TX0 = 0, + WCD934X_TX1, + WCD934X_TX2, + WCD934X_TX3, + WCD934X_TX4, + WCD934X_TX5, + WCD934X_TX6, + WCD934X_TX7, + WCD934X_TX8, + WCD934X_TX9, + WCD934X_TX10, + WCD934X_TX11, + WCD934X_TX12, + WCD934X_TX13, + WCD934X_TX14, + WCD934X_TX15, + WCD934X_TX_MAX, +}; + +enum { + INTERP_EAR = 0, + INTERP_HPHL, + INTERP_HPHR, + INTERP_LO1, + INTERP_LO2, + INTERP_LO3_NA, /* LO3 not avalible in Tavil*/ + INTERP_LO4_NA, + INTERP_SPKR1, + INTERP_SPKR2, + INTERP_MAX, +}; + +/* + * Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + WCD934X_SPKR_MODE_DEFAULT, + WCD934X_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +/* + * Rx path gain offsets + */ +enum { + WCD934X_RX_GAIN_OFFSET_M1P5_DB, + WCD934X_RX_GAIN_OFFSET_0_DB, +}; + +/* + * Dai data structure holds the + * dai specific info like rate, + * channel number etc. + */ +struct tavil_codec_dai_data { + u32 rate; + u32 *ch_num; + u32 ch_act; + u32 ch_tot; +}; + +/* + * Structure used to update codec + * register defaults after reset + */ +struct tavil_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +extern void *tavil_get_afe_config(struct snd_soc_codec *codec, + enum afe_config_type config_type); +extern int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable); +extern int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode); +extern int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset); +extern struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev); +extern int wcd934x_get_micb_vout_ctl_val(u32 micb_mv); +extern int tavil_micbias_control(struct snd_soc_codec *codec, + int micb_num, + int req, bool is_dapm); +extern int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec, + int req_volt, + int micb_num); +extern struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec); +extern int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec, + int event, int intp_idx); +extern struct tavil_dsd_config *tavil_get_dsd_config( + struct snd_soc_codec *codec); +extern int tavil_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +#endif diff --git a/sound/soc/codecs/wcd9xxx-common-v2.c b/sound/soc/codecs/wcd9xxx-common-v2.c new file mode 100644 index 000000000000..62166579342a --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-common-v2.c @@ -0,0 +1,1367 @@ +/* + * 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 "wcd9xxx-common-v2.h" + +#define WCD_USLEEP_RANGE 50 +#define MAX_IMPED_PARAMS 6 + +enum { + DAC_GAIN_0DB = 0, + DAC_GAIN_0P2DB, + DAC_GAIN_0P4DB, + DAC_GAIN_0P6DB, + DAC_GAIN_0P8DB, + DAC_GAIN_M0P2DB, + DAC_GAIN_M0P4DB, + DAC_GAIN_M0P6DB, +}; + +enum { + VREF_FILT_R_0OHM = 0, + VREF_FILT_R_25KOHM, + VREF_FILT_R_50KOHM, + VREF_FILT_R_100KOHM, +}; + +enum { + DELTA_I_0MA, + DELTA_I_10MA, + DELTA_I_20MA, + DELTA_I_30MA, + DELTA_I_40MA, + DELTA_I_50MA, +}; + +struct wcd_imped_val { + u32 imped_val; + u8 index; +}; + +static const struct wcd_reg_mask_val imped_table[][MAX_IMPED_PARAMS] = { + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf5}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x0}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x0}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfe}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xff}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, +}; + +static const struct wcd_reg_mask_val imped_table_tavil[][MAX_IMPED_PARAMS] = { + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf2}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf2}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf2}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf2}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf4}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf4}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf4}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf4}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf7}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf9}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfa}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfb}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfc}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00}, + }, + { + {WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01}, + {WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd}, + {WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01}, + }, +}; + +static const struct wcd_imped_val imped_index[] = { + {4, 0}, + {5, 1}, + {6, 2}, + {7, 3}, + {8, 4}, + {9, 5}, + {10, 6}, + {11, 7}, + {12, 8}, + {13, 9}, +}; + +static void (*clsh_state_fp[NUM_CLSH_STATES_V2])(struct snd_soc_codec *, + struct wcd_clsh_cdc_data *, + u8 req_state, bool en, int mode); + +static int get_impedance_index(int imped) +{ + int i = 0; + + if (imped < imped_index[i].imped_val) { + pr_debug("%s, detected impedance is less than 4 Ohm\n", + __func__); + i = 0; + goto ret; + } + if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) { + pr_debug("%s, detected impedance is greater than 12 Ohm\n", + __func__); + i = ARRAY_SIZE(imped_index) - 1; + goto ret; + } + for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) { + if (imped >= imped_index[i].imped_val && + imped < imped_index[i + 1].imped_val) + break; + } +ret: + pr_debug("%s: selected impedance index = %d\n", + __func__, imped_index[i].index); + return imped_index[i].index; +} + +/* + * Function: wcd_clsh_imped_config + * Params: codec, imped, reset + * Description: + * This function updates HPHL and HPHR gain settings + * according to the impedance value. + */ +void wcd_clsh_imped_config(struct snd_soc_codec *codec, int imped, bool reset) +{ + int i; + int index = 0; + int table_size; + + static const struct wcd_reg_mask_val + (*imped_table_ptr)[MAX_IMPED_PARAMS]; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (IS_CODEC_TYPE(wcd9xxx, WCD934X)) { + table_size = ARRAY_SIZE(imped_table_tavil); + imped_table_ptr = imped_table_tavil; + } else { + table_size = ARRAY_SIZE(imped_table); + imped_table_ptr = imped_table; + } + + /* reset = 1, which means request is to reset the register values */ + if (reset) { + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, + imped_table_ptr[index][i].reg, + imped_table_ptr[index][i].mask, 0); + return; + } + index = get_impedance_index(imped); + if (index >= (ARRAY_SIZE(imped_index) - 1)) { + pr_debug("%s, impedance not in range = %d\n", __func__, imped); + return; + } + if (index >= table_size) { + pr_debug("%s, impedance index not in range = %d\n", __func__, + index); + return; + } + for (i = 0; i < MAX_IMPED_PARAMS; i++) + snd_soc_update_bits(codec, + imped_table_ptr[index][i].reg, + imped_table_ptr[index][i].mask, + imped_table_ptr[index][i].val); +} +EXPORT_SYMBOL(wcd_clsh_imped_config); + +static bool is_native_44_1_active(struct snd_soc_codec *codec) +{ + bool native_active = false; + u8 native_clk, rx1_rate, rx2_rate; + + native_clk = snd_soc_read(codec, + WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL); + rx1_rate = snd_soc_read(codec, WCD9XXX_CDC_RX1_RX_PATH_CTL); + rx2_rate = snd_soc_read(codec, WCD9XXX_CDC_RX2_RX_PATH_CTL); + + dev_dbg(codec->dev, "%s: native_clk %x rx1_rate= %x rx2_rate= %x", + __func__, native_clk, rx1_rate, rx2_rate); + + if ((native_clk & 0x2) && + ((rx1_rate & 0x0F) == 0x9 || (rx2_rate & 0x0F) == 0x9)) + native_active = true; + + return native_active; +} + +static const char *mode_to_str(int mode) +{ + switch (mode) { + case CLS_H_NORMAL: + return "CLS_H_NORMAL"; + case CLS_H_HIFI: + return "CLS_H_HIFI"; + case CLS_H_LOHIFI: + return "CLS_H_LOHIFI"; + case CLS_H_LP: + return "CLS_H_LP"; + case CLS_H_ULP: + return "CLS_H_ULP"; + case CLS_AB: + return "CLS_AB"; + case CLS_AB_HIFI: + return "CLS_AB_HIFI"; + default: + return "CLS_H_INVALID"; + }; +} + +static const char *state_to_str(u8 state, char *buf, size_t buflen) +{ + int i; + int cnt = 0; + /* + * This array of strings should match with enum wcd_clsh_state_bit. + */ + static const char *const states[] = { + "STATE_EAR", + "STATE_HPH_L", + "STATE_HPH_R", + "STATE_LO", + }; + + if (state == WCD_CLSH_STATE_IDLE) { + snprintf(buf, buflen, "[STATE_IDLE]"); + goto done; + } + + buf[0] = '\0'; + for (i = 0; i < ARRAY_SIZE(states); i++) { + if (!(state & (1 << i))) + continue; + cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf, + buf[0] == '\0' ? "[" : "|", + states[i]); + } + if (cnt > 0) + strlcat(buf + cnt, "]", buflen); + +done: + if (buf[0] == '\0') + snprintf(buf, buflen, "[STATE_UNKNOWN]"); + return buf; +} + +static inline void +wcd_enable_clsh_block(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, bool enable) +{ + if ((enable && ++clsh_d->clsh_users == 1) || + (!enable && --clsh_d->clsh_users == 0)) + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_CRC, 0x01, + (u8) enable); + if (clsh_d->clsh_users < 0) + clsh_d->clsh_users = 0; + dev_dbg(codec->dev, "%s: clsh_users %d, enable %d", __func__, + clsh_d->clsh_users, enable); +} + +static inline bool wcd_clsh_enable_status(struct snd_soc_codec *codec) +{ + return snd_soc_read(codec, WCD9XXX_A_CDC_CLSH_CRC) & 0x01; +} + +static inline int wcd_clsh_get_int_mode(struct wcd_clsh_cdc_data *clsh_d, + int clsh_state) +{ + int mode; + + if ((clsh_state != WCD_CLSH_STATE_EAR) && + (clsh_state != WCD_CLSH_STATE_HPHL) && + (clsh_state != WCD_CLSH_STATE_HPHR) && + (clsh_state != WCD_CLSH_STATE_LO)) + mode = CLS_NONE; + else + mode = clsh_d->interpolator_modes[ffs(clsh_state)]; + + return mode; +} + +static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_data *clsh_d, + int clsh_state, int mode) +{ + if ((clsh_state != WCD_CLSH_STATE_EAR) && + (clsh_state != WCD_CLSH_STATE_HPHL) && + (clsh_state != WCD_CLSH_STATE_HPHR) && + (clsh_state != WCD_CLSH_STATE_LO)) + return; + + clsh_d->interpolator_modes[ffs(clsh_state)] = mode; +} + +static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x08, 0x08); /* set to HIFI */ + else + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x08, 0x00); /* set to default */ +} + +static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec, + int mode) +{ + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x04, 0x04); /* set to HIFI */ + else + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x04, 0x00); /* set to Default */ +} + +static inline void wcd_clsh_gm3_boost_disable(struct snd_soc_codec *codec, + int mode) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!IS_CODEC_TYPE(wcd9xxx, WCD934X)) + return; + + if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI || + mode == CLS_AB_HIFI || mode == CLS_AB) { + if (TAVIL_IS_1_0(wcd9xxx)) + snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL, + 0x80, 0x0); /* disable GM3 Boost */ + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x80); + } else { + snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL, + 0x80, 0x80); /* set to Default */ + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4, + 0xF0, 0x70); + } +} + + +static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec, + int mode) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!IS_CODEC_TYPE(wcd9xxx, WCD934X)) + return; + + if (mode == CLS_H_LOHIFI || mode == CLS_AB) { + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x20); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0xC0); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x02); + } else { + + snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2, + 0x20, 0x0); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER, + 0xF0, 0x80); + snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1, + 0x0E, 0x06); + } +} + +static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + int mode, + bool enable) +{ + /* enable/disable buck */ + if ((enable && (++clsh_d->buck_users == 1)) || + (!enable && (--clsh_d->buck_users == 0))) + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + (1 << 7), (enable << 7)); + dev_dbg(codec->dev, "%s: buck_users %d, enable %d, mode: %s", + __func__, clsh_d->buck_users, enable, mode_to_str(mode)); + /* + * 500us sleep is required after buck enable/disable + * as per HW requirement + */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); +} + +static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + int mode, + bool enable) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_reg_val bulk_reg[2]; + u8 vneg[] = {0x00, 0x40}; + + /* enable/disable flyback */ + if ((enable && (++clsh_d->flyback_users == 1)) || + (!enable && (--clsh_d->flyback_users == 0))) { + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + (1 << 6), (enable << 6)); + /* 100usec delay is needed as per HW requirement */ + usleep_range(100, 110); + if (enable && (TASHA_IS_1_1(wcd9xxx))) { + wcd_clsh_set_flyback_mode(codec, CLS_H_HIFI); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN, + 0x60, 0x40); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN, + 0x10, 0x10); + vneg[0] = snd_soc_read(codec, + WCD9XXX_A_ANA_RX_SUPPLIES); + vneg[0] &= ~(0x40); + vneg[1] = vneg[0] | 0x40; + bulk_reg[0].reg = WCD9XXX_A_ANA_RX_SUPPLIES; + bulk_reg[0].buf = &vneg[0]; + bulk_reg[0].bytes = 1; + bulk_reg[1].reg = WCD9XXX_A_ANA_RX_SUPPLIES; + bulk_reg[1].buf = &vneg[1]; + bulk_reg[1].bytes = 1; + /* 500usec delay is needed as per HW requirement */ + usleep_range(500, 510); + wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, + false); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN, + 0x10, 0x00); + wcd_clsh_set_flyback_mode(codec, mode); + } + + } + dev_dbg(codec->dev, "%s: flyback_users %d, enable %d, mode: %s", + __func__, clsh_d->flyback_users, enable, mode_to_str(mode)); + /* + * 500us sleep is required after flyback enable/disable + * as per HW requirement + */ + usleep_range(500, 500 + WCD_USLEEP_RANGE); +} + +static void wcd_clsh_set_gain_path(struct snd_soc_codec *codec, + int mode) +{ + u8 val = 0; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!TASHA_IS_2_0(wcd9xxx)) + return; + + switch (mode) { + case CLS_H_NORMAL: + case CLS_AB: + val = 0x00; + break; + case CLS_H_HIFI: + val = 0x02; + break; + case CLS_H_LP: + val = 0x01; + break; + default: + return; + }; + snd_soc_update_bits(codec, WCD9XXX_HPH_L_EN, 0xC0, (val << 6)); + snd_soc_update_bits(codec, WCD9XXX_HPH_R_EN, 0xC0, (val << 6)); +} + +static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec, + int mode) +{ + u8 val = 0; + u8 gain = 0; + u8 res_val = VREF_FILT_R_0OHM; + u8 ipeak = DELTA_I_50MA; + + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + switch (mode) { + case CLS_H_NORMAL: + res_val = VREF_FILT_R_50KOHM; + val = 0x00; + gain = DAC_GAIN_0DB; + ipeak = DELTA_I_50MA; + break; + case CLS_AB: + val = 0x00; + gain = DAC_GAIN_0DB; + ipeak = DELTA_I_50MA; + break; + case CLS_AB_HIFI: + val = 0x08; + break; + case CLS_H_HIFI: + val = 0x08; + gain = DAC_GAIN_M0P2DB; + ipeak = DELTA_I_50MA; + break; + case CLS_H_LOHIFI: + val = 0x00; + if ((IS_CODEC_TYPE(wcd9xxx, WCD9335)) || + (IS_CODEC_TYPE(wcd9xxx, WCD9326))) { + val = 0x08; + gain = DAC_GAIN_M0P2DB; + ipeak = DELTA_I_50MA; + } + break; + case CLS_H_ULP: + val = 0x0C; + break; + case CLS_H_LP: + val = 0x04; + ipeak = DELTA_I_30MA; + break; + default: + return; + }; + + /* + * For tavil set mode to Lower_power for + * CLS_H_LOHIFI and CLS_AB + */ + if ((IS_CODEC_TYPE(wcd9xxx, WCD934X)) && + (mode == CLS_H_LOHIFI || mode == CLS_AB)) + val = 0x04; + + snd_soc_update_bits(codec, WCD9XXX_A_ANA_HPH, 0x0C, val); + if (TASHA_IS_2_0(wcd9xxx)) { + snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_VCL_2, + 0x30, (res_val << 4)); + if (mode != CLS_H_LP) + snd_soc_update_bits(codec, WCD9XXX_HPH_REFBUFF_UHQA_CTL, + 0x07, gain); + snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_CCL_1, + 0xF0, (ipeak << 4)); + } +} + +static void wcd_clsh_set_flyback_vneg_ctl(struct snd_soc_codec *codec, + bool enable) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!TASHA_IS_2_0(wcd9xxx)) + return; + + if (enable) { + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_1, 0xE0, + 0x00); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, (0x07 << 5)); + } else { + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_1, 0xE0, + (0x07 << 5)); + snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2, + 0xE0, (0x02 << 5)); + } +} + +static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode) +{ + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + + if (!TASHA_IS_2_0(wcd9xxx)) + return; + + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A); + snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0xF0, 0xA0); + /* Sleep needed to avoid click and pop as per HW requirement */ + usleep_range(100, 110); +} + +static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_codec *codec, + int mode) +{ + snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES, + 0x02, 0x00); +} + +static void wcd_clsh_state_lo(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + dev_err(codec->dev, "%s: LO cannot be in this mode: %d\n", + __func__, mode); + return; + } + + if (is_enable) { + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_vneg_ctl(codec, true); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + } else { + wcd_clsh_buck_ctrl(codec, clsh_d, mode, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_flyback_vneg_ctl(codec, false); + wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + int hph_mode = 0; + + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + if (req_state == WCD_CLSH_STATE_EAR) { + /* If HPH is running in CLS-AB when + * EAR comes, let it continue to run + * in Class-AB, no need to enable Class-H + * for EAR. + */ + if (clsh_d->state & WCD_CLSH_STATE_HPHL) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHL); + else if (clsh_d->state & WCD_CLSH_STATE_HPHR) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHR); + else + return; + if (hph_mode != CLS_AB && hph_mode != CLS_AB_HIFI + && !is_native_44_1_active(codec)) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + } + + if (is_native_44_1_active(codec)) { + snd_soc_write(codec, WCD9XXX_CDC_CLSH_HPH_V_PA, 0x39); + snd_soc_update_bits(codec, + WCD9XXX_CDC_RX0_RX_PATH_SEC0, + 0x03, 0x00); + if ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR)) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x00); + } + + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + if ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR)) { + wcd_clsh_set_gain_path(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + } + } else { + if (req_state == WCD_CLSH_STATE_EAR) { + /* + * If EAR goes away, disable EAR Channel Enable + * if HPH running in Class-H otherwise + * and if HPH requested mode is CLS_AB then + * no need to disable EAR channel enable bit. + */ + if (wcd_clsh_enable_status(codec)) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x00); + } + + if (is_native_44_1_active(codec)) { + snd_soc_write(codec, WCD9XXX_CDC_CLSH_HPH_V_PA, 0x1C); + snd_soc_update_bits(codec, + WCD9XXX_CDC_RX0_RX_PATH_SEC0, + 0x03, 0x01); + if (((clsh_d->state & WCD_CLSH_STATE_HPH_ST) + != WCD_CLSH_STATE_HPH_ST) && + ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR))) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + } + + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + if ((req_state & WCD_CLSH_STATE_HPH_ST) && + !wcd_clsh_enable_status(codec)) { + /* If Class-H is not enabled when HPH is turned + * off, enable it as EAR is in progress + */ + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } + } +} + +static void wcd_clsh_state_ear_lo(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + /* LO powerup is taken care in PA sequence. + * No need to change to class AB here. + */ + if (req_state == WCD_CLSH_STATE_EAR) { + /* EAR powerup.*/ + if (!wcd_clsh_enable_status(codec)) { + wcd_enable_clsh_block(codec, clsh_d, true); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + } + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + } + } else { + if (req_state == WCD_CLSH_STATE_EAR) { + /* EAR powerdown.*/ + wcd_enable_clsh_block(codec, clsh_d, false); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x00); + } + /* LO powerdown is taken care in PA sequence. + * No need to change to class H here. + */ + } +} + +static void wcd_clsh_state_hph_lo(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + int hph_mode = 0; + + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (is_enable) { + /* + * If requested state is LO, put regulator + * in class-AB or if requested state is HPH, + * which means LO is already enabled, keep + * the regulator config the same at class-AB + * and just set the power modes for flyback + * and buck. + */ + if (req_state == WCD_CLSH_STATE_LO) + wcd_clsh_set_buck_regulator_mode(codec, CLS_AB); + else { + if (!wcd_clsh_enable_status(codec)) { + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_set_flyback_vneg_ctl(codec, false); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_hph_mode(codec, mode); + wcd_clsh_set_gain_path(codec, mode); + } else { + dev_dbg(codec->dev, "%s:clsh is already enabled\n", + __func__); + } + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + } + } else { + if ((req_state == WCD_CLSH_STATE_HPHL) || + (req_state == WCD_CLSH_STATE_HPHR)) { + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + /* + * If HPH is powering down first, then disable clsh, + * set the buck/flyback mode to default and keep the + * regulator at Class-AB + */ + if ((clsh_d->state & WCD_CLSH_STATE_HPH_ST) + != WCD_CLSH_STATE_HPH_ST) { + wcd_enable_clsh_block(codec, clsh_d, false); + wcd_clsh_set_flyback_vneg_ctl(codec, true); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } + } else { + /* LO powerdown. + * If HPH mode also is CLS-AB, no need + * to turn-on class-H, otherwise enable + * Class-H configuration. + */ + if (clsh_d->state & WCD_CLSH_STATE_HPHL) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHL); + else if (clsh_d->state & WCD_CLSH_STATE_HPHR) + hph_mode = wcd_clsh_get_int_mode(clsh_d, + WCD_CLSH_STATE_HPHR); + else + return; + dev_dbg(codec->dev, "%s: hph_mode = %d\n", __func__, + hph_mode); + + if ((hph_mode == CLS_AB) || + (hph_mode == CLS_AB_HIFI) || + (hph_mode == CLS_NONE)) + goto end; + + /* + * If Class-H is already enabled (HPH ON and then + * LO ON), no need to turn on again, just set the + * regulator mode. + */ + if (wcd_clsh_enable_status(codec)) { + wcd_clsh_set_buck_regulator_mode(codec, + hph_mode); + goto end; + } else { + dev_dbg(codec->dev, "%s: clsh is not enabled\n", + __func__); + } + + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + wcd_clsh_set_buck_regulator_mode(codec, + hph_mode); + if (clsh_d->state & WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (clsh_d->state & WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + wcd_clsh_set_hph_mode(codec, hph_mode); + } + } +end: + return; +} + +static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_AB || mode == CLS_AB_HIFI) + return; + + if (is_enable) { + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + } else { + if (req_state == WCD_CLSH_STATE_HPHL) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + if (req_state == WCD_CLSH_STATE_HPHR) + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + } +} + +static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_err(codec->dev, "%s: Normal mode not applicable for hph_r\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + wcd_enable_clsh_block(codec, clsh_d, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x40); + } + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_gm3_boost_disable(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + wcd_clsh_set_gain_path(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX2_RX_PATH_CFG0, + 0x40, 0x00); + wcd_enable_clsh_block(codec, clsh_d, false); + } + /* buck and flyback set to default mode and disable */ + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL); + wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode == CLS_H_NORMAL) { + dev_err(codec->dev, "%s: Normal mode not applicable for hph_l\n", + __func__); + return; + } + + if (is_enable) { + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + wcd_enable_clsh_block(codec, clsh_d, true); + /* + * These K1 values depend on the Headphone Impedance + * For now it is assumed to be 16 ohm + */ + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_MSB, + 0x0F, 0x00); + snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_LSB, + 0xFF, 0xC0); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x40); + } + wcd_clsh_set_buck_regulator_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_gm3_boost_disable(codec, mode); + wcd_clsh_force_iq_ctl(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_hph_mode(codec, mode); + wcd_clsh_set_gain_path(codec, mode); + } else { + wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL); + + if (mode != CLS_AB && mode != CLS_AB_HIFI) { + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX1_RX_PATH_CFG0, + 0x40, 0x00); + wcd_enable_clsh_block(codec, clsh_d, false); + } + /* set buck and flyback to Default Mode */ + wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false); + wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL); + wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_ear(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode), + is_enable ? "enable" : "disable"); + + if (mode != CLS_H_NORMAL) { + dev_err(codec->dev, "%s: mode: %s cannot be used for EAR\n", + __func__, mode_to_str(mode)); + return; + } + + if (is_enable) { + wcd_enable_clsh_block(codec, clsh_d, true); + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x40); + wcd_clsh_set_buck_mode(codec, mode); + wcd_clsh_set_flyback_mode(codec, mode); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true); + wcd_clsh_set_flyback_current(codec, mode); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, true); + } else { + snd_soc_update_bits(codec, + WCD9XXX_A_CDC_RX0_RX_PATH_CFG0, + 0x40, 0x00); + wcd_enable_clsh_block(codec, clsh_d, false); + wcd_clsh_buck_ctrl(codec, clsh_d, mode, false); + wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false); + wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL); + wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL); + } +} + +static void wcd_clsh_state_err(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *clsh_d, + u8 req_state, bool is_enable, int mode) +{ + char msg[128]; + + dev_err(codec->dev, + "%s Wrong request for class H state machine requested to %s %s", + __func__, is_enable ? "enable" : "disable", + state_to_str(req_state, msg, sizeof(msg))); + WARN_ON(1); +} + +/* + * Function: wcd_clsh_is_state_valid + * Params: state + * Description: + * Provides information on valid states of Class H configuration + */ +static bool wcd_clsh_is_state_valid(u8 state) +{ + switch (state) { + case WCD_CLSH_STATE_IDLE: + case WCD_CLSH_STATE_EAR: + case WCD_CLSH_STATE_HPHL: + case WCD_CLSH_STATE_HPHR: + case WCD_CLSH_STATE_HPH_ST: + case WCD_CLSH_STATE_LO: + case WCD_CLSH_STATE_HPHL_EAR: + case WCD_CLSH_STATE_HPHR_EAR: + case WCD_CLSH_STATE_HPH_ST_EAR: + case WCD_CLSH_STATE_HPHL_LO: + case WCD_CLSH_STATE_HPHR_LO: + case WCD_CLSH_STATE_HPH_ST_LO: + case WCD_CLSH_STATE_EAR_LO: + return true; + default: + return false; + }; +} + +/* + * Function: wcd_clsh_fsm + * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event + * Description: + * This function handles PRE DAC and POST DAC conditions of different devices + * and updates class H configuration of different combination of devices + * based on validity of their states. cdc_clsh_d will contain current + * class h state information + */ +void wcd_clsh_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode) +{ + u8 old_state, new_state; + char msg0[128], msg1[128]; + + switch (clsh_event) { + case WCD_CLSH_EVENT_PRE_DAC: + old_state = cdc_clsh_d->state; + new_state = old_state | req_state; + + if (!wcd_clsh_is_state_valid(new_state)) { + dev_err(codec->dev, + "%s: Class-H not a valid new state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__, + state_to_str(new_state, msg0, sizeof(msg0))); + return; + } + cdc_clsh_d->state = new_state; + wcd_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode); + (*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state, + CLSH_REQ_ENABLE, int_mode); + dev_dbg(codec->dev, + "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1))); + break; + case WCD_CLSH_EVENT_POST_PA: + old_state = cdc_clsh_d->state; + new_state = old_state & (~req_state); + if (new_state < NUM_CLSH_STATES_V2) { + if (!wcd_clsh_is_state_valid(old_state)) { + dev_err(codec->dev, + "%s:Invalid old state:%s\n", + __func__, + state_to_str(old_state, msg0, + sizeof(msg0))); + return; + } + if (new_state == old_state) { + dev_err(codec->dev, + "%s: Class-H already in requested state: %s\n", + __func__, + state_to_str(new_state, msg0, + sizeof(msg0))); + return; + } + (*clsh_state_fp[old_state]) (codec, cdc_clsh_d, + req_state, CLSH_REQ_DISABLE, + int_mode); + cdc_clsh_d->state = new_state; + wcd_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE); + dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n", + __func__, state_to_str(old_state, msg0, + sizeof(msg0)), + state_to_str(cdc_clsh_d->state, msg1, + sizeof(msg1))); + } + break; + }; +} +EXPORT_SYMBOL(wcd_clsh_fsm); + +int wcd_clsh_get_clsh_state(struct wcd_clsh_cdc_data *clsh) +{ + return clsh->state; +} +EXPORT_SYMBOL(wcd_clsh_get_clsh_state); + +void wcd_clsh_init(struct wcd_clsh_cdc_data *clsh) +{ + int i; + + clsh->state = WCD_CLSH_STATE_IDLE; + + for (i = 0; i < NUM_CLSH_STATES_V2; i++) + clsh_state_fp[i] = wcd_clsh_state_err; + + clsh_state_fp[WCD_CLSH_STATE_EAR] = wcd_clsh_state_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHL] = + wcd_clsh_state_hph_l; + clsh_state_fp[WCD_CLSH_STATE_HPHR] = + wcd_clsh_state_hph_r; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST] = + wcd_clsh_state_hph_st; + clsh_state_fp[WCD_CLSH_STATE_LO] = wcd_clsh_state_lo; + clsh_state_fp[WCD_CLSH_STATE_HPHL_EAR] = + wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHR_EAR] = + wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST_EAR] = + wcd_clsh_state_hph_ear; + clsh_state_fp[WCD_CLSH_STATE_HPHL_LO] = wcd_clsh_state_hph_lo; + clsh_state_fp[WCD_CLSH_STATE_HPHR_LO] = wcd_clsh_state_hph_lo; + clsh_state_fp[WCD_CLSH_STATE_HPH_ST_LO] = + wcd_clsh_state_hph_lo; + clsh_state_fp[WCD_CLSH_STATE_EAR_LO] = wcd_clsh_state_ear_lo; + /* Set interpolaotr modes to NONE */ + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_EAR, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHL, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHR, CLS_NONE); + wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_LO, CLS_NONE); + clsh->flyback_users = 0; + clsh->buck_users = 0; + clsh->clsh_users = 0; +} +EXPORT_SYMBOL(wcd_clsh_init); + +MODULE_DESCRIPTION("WCD9XXX Common Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9xxx-common-v2.h b/sound/soc/codecs/wcd9xxx-common-v2.h new file mode 100644 index 000000000000..53c9a84b51ad --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-common-v2.h @@ -0,0 +1,236 @@ +/* + * 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 _WCD9XXX_COMMON_V2 + +#define _WCD9XXX_COMMON_V2 + +#define CLSH_REQ_ENABLE true +#define CLSH_REQ_DISABLE false + +#define WCD_CLSH_EVENT_PRE_DAC 0x01 +#define WCD_CLSH_EVENT_POST_PA 0x02 +#define MAX_VBAT_MONITOR_WRITES 17 +/* + * Basic states for Class H state machine. + * represented as a bit mask within a u8 data type + * bit 0: EAR mode + * bit 1: HPH Left mode + * bit 2: HPH Right mode + * bit 3: Lineout mode + */ +#define WCD_CLSH_STATE_IDLE 0x00 +#define WCD_CLSH_STATE_EAR (0x01 << 0) +#define WCD_CLSH_STATE_HPHL (0x01 << 1) +#define WCD_CLSH_STATE_HPHR (0x01 << 2) +#define WCD_CLSH_STATE_LO (0x01 << 3) + +/* + * Though number of CLSH states are 4, max state shoulbe be 5 + * because state array index starts from 1. + */ +#define WCD_CLSH_STATE_MAX 5 +#define NUM_CLSH_STATES_V2 (0x01 << WCD_CLSH_STATE_MAX) + + +/* Derived State: Bits 1 and 2 should be set for Headphone stereo */ +#define WCD_CLSH_STATE_HPH_ST (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_HPHR) + +#define WCD_CLSH_STATE_HPHL_LO (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_HPHR_LO (WCD_CLSH_STATE_HPHR | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_HPH_ST_LO (WCD_CLSH_STATE_HPH_ST | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_EAR_LO (WCD_CLSH_STATE_EAR | \ + WCD_CLSH_STATE_LO) +#define WCD_CLSH_STATE_HPHL_EAR (WCD_CLSH_STATE_HPHL | \ + WCD_CLSH_STATE_EAR) +#define WCD_CLSH_STATE_HPHR_EAR (WCD_CLSH_STATE_HPHR | \ + WCD_CLSH_STATE_EAR) +#define WCD_CLSH_STATE_HPH_ST_EAR (WCD_CLSH_STATE_HPH_ST | \ + WCD_CLSH_STATE_EAR) + +enum { + CLS_H_NORMAL = 0, /* Class-H Default */ + CLS_H_HIFI, /* Class-H HiFi */ + CLS_H_LP, /* Class-H Low Power */ + CLS_AB, /* Class-AB Low HIFI*/ + CLS_H_LOHIFI, /* LoHIFI */ + CLS_H_ULP, /* Ultra Low power */ + CLS_AB_HIFI, /* Class-AB */ + CLS_NONE, /* None of the above modes */ +}; + +/* Class H data that the codec driver will maintain */ +struct wcd_clsh_cdc_data { + u8 state; + int flyback_users; + int buck_users; + int clsh_users; + int interpolator_modes[WCD_CLSH_STATE_MAX]; +}; + +struct wcd_mad_audio_header { + u32 reserved[3]; + u32 num_reg_cfg; +}; + +struct wcd_mad_microphone_info { + uint8_t input_microphone; + uint8_t cycle_time; + uint8_t settle_time; + uint8_t padding; +} __packed; + +struct wcd_mad_micbias_info { + uint8_t micbias; + uint8_t k_factor; + uint8_t external_bypass_capacitor; + uint8_t internal_biasing; + uint8_t cfilter; + uint8_t padding[3]; +} __packed; + +struct wcd_mad_rms_audio_beacon_info { + uint8_t rms_omit_samples; + uint8_t rms_comp_time; + uint8_t detection_mechanism; + uint8_t rms_diff_threshold; + uint8_t rms_threshold_lsb; + uint8_t rms_threshold_msb; + uint8_t padding[2]; + uint8_t iir_coefficients[36]; +} __packed; + +struct wcd_mad_rms_ultrasound_info { + uint8_t rms_comp_time; + uint8_t detection_mechanism; + uint8_t rms_diff_threshold; + uint8_t rms_threshold_lsb; + uint8_t rms_threshold_msb; + uint8_t padding[3]; + uint8_t iir_coefficients[36]; +} __packed; + +struct wcd_mad_audio_cal { + uint32_t version; + struct wcd_mad_microphone_info microphone_info; + struct wcd_mad_micbias_info micbias_info; + struct wcd_mad_rms_audio_beacon_info audio_info; + struct wcd_mad_rms_audio_beacon_info beacon_info; + struct wcd_mad_rms_ultrasound_info ultrasound_info; +} __packed; + +struct wcd9xxx_anc_header { + u32 reserved[3]; + u32 num_anc_slots; +}; + +struct vbat_monitor_reg { + u32 size; + u32 writes[MAX_VBAT_MONITOR_WRITES]; +} __packed; + +struct wcd_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +extern void wcd_clsh_fsm(struct snd_soc_codec *codec, + struct wcd_clsh_cdc_data *cdc_clsh_d, + u8 clsh_event, u8 req_state, + int int_mode); + +extern void wcd_clsh_init(struct wcd_clsh_cdc_data *clsh); +extern int wcd_clsh_get_clsh_state(struct wcd_clsh_cdc_data *clsh); +extern void wcd_clsh_imped_config(struct snd_soc_codec *codec, int imped, + bool reset); + +enum { + RESERVED = 0, + AANC_LPF_FF_FB = 1, + AANC_LPF_COEFF_MSB, + AANC_LPF_COEFF_LSB, + HW_MAD_AUDIO_ENABLE, + HW_MAD_ULTR_ENABLE, + HW_MAD_BEACON_ENABLE, + HW_MAD_AUDIO_SLEEP_TIME, + HW_MAD_ULTR_SLEEP_TIME, + HW_MAD_BEACON_SLEEP_TIME, + HW_MAD_TX_AUDIO_SWITCH_OFF, + HW_MAD_TX_ULTR_SWITCH_OFF, + HW_MAD_TX_BEACON_SWITCH_OFF, + MAD_AUDIO_INT_DEST_SELECT_REG, + MAD_ULT_INT_DEST_SELECT_REG, + MAD_BEACON_INT_DEST_SELECT_REG, + MAD_CLIP_INT_DEST_SELECT_REG, + VBAT_INT_DEST_SELECT_REG, + MAD_AUDIO_INT_MASK_REG, + MAD_ULT_INT_MASK_REG, + MAD_BEACON_INT_MASK_REG, + MAD_CLIP_INT_MASK_REG, + VBAT_INT_MASK_REG, + MAD_AUDIO_INT_STATUS_REG, + MAD_ULT_INT_STATUS_REG, + MAD_BEACON_INT_STATUS_REG, + MAD_CLIP_INT_STATUS_REG, + VBAT_INT_STATUS_REG, + MAD_AUDIO_INT_CLEAR_REG, + MAD_ULT_INT_CLEAR_REG, + MAD_BEACON_INT_CLEAR_REG, + MAD_CLIP_INT_CLEAR_REG, + VBAT_INT_CLEAR_REG, + SB_PGD_PORT_TX_WATERMARK_N, + SB_PGD_PORT_TX_ENABLE_N, + SB_PGD_PORT_RX_WATERMARK_N, + SB_PGD_PORT_RX_ENABLE_N, + SB_PGD_TX_PORTn_MULTI_CHNL_0, + SB_PGD_TX_PORTn_MULTI_CHNL_1, + SB_PGD_RX_PORTn_MULTI_CHNL_0, + SB_PGD_RX_PORTn_MULTI_CHNL_1, + AANC_FF_GAIN_ADAPTIVE, + AANC_FFGAIN_ADAPTIVE_EN, + AANC_GAIN_CONTROL, + SPKR_CLIP_PIPE_BANK_SEL, + SPKR_CLIPDET_VAL0, + SPKR_CLIPDET_VAL1, + SPKR_CLIPDET_VAL2, + SPKR_CLIPDET_VAL3, + SPKR_CLIPDET_VAL4, + SPKR_CLIPDET_VAL5, + SPKR_CLIPDET_VAL6, + SPKR_CLIPDET_VAL7, + VBAT_RELEASE_INT_DEST_SELECT_REG, + VBAT_RELEASE_INT_MASK_REG, + VBAT_RELEASE_INT_STATUS_REG, + VBAT_RELEASE_INT_CLEAR_REG, + MAD2_CLIP_INT_DEST_SELECT_REG, + MAD2_CLIP_INT_MASK_REG, + MAD2_CLIP_INT_STATUS_REG, + MAD2_CLIP_INT_CLEAR_REG, + SPKR2_CLIP_PIPE_BANK_SEL, + SPKR2_CLIPDET_VAL0, + SPKR2_CLIPDET_VAL1, + SPKR2_CLIPDET_VAL2, + SPKR2_CLIPDET_VAL3, + SPKR2_CLIPDET_VAL4, + SPKR2_CLIPDET_VAL5, + SPKR2_CLIPDET_VAL6, + SPKR2_CLIPDET_VAL7, + MAX_CFG_REGISTERS, +}; + +#endif diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.c b/sound/soc/codecs/wcd9xxx-resmgr-v2.c new file mode 100644 index 000000000000..feef0a48af79 --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-resmgr-v2.c @@ -0,0 +1,693 @@ +/* + * 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 "wcd9xxx-resmgr-v2.h" + +#define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 5000 +#define WCD93XX_ANA_BIAS 0x0601 +#define WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41 +#define WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42 + + +static const char *wcd_resmgr_clk_type_to_str(enum wcd_clock_type clk_type) +{ + if (clk_type == WCD_CLK_OFF) + return "WCD_CLK_OFF"; + else if (clk_type == WCD_CLK_RCO) + return "WCD_CLK_RCO"; + else if (clk_type == WCD_CLK_MCLK) + return "WCD_CLK_MCLK"; + else + return "WCD_CLK_UNDEFINED"; +} + +static int wcd_resmgr_codec_reg_update_bits(struct wcd9xxx_resmgr_v2 *resmgr, + u16 reg, u8 mask, u8 val) +{ + bool change; + int ret; + + if (resmgr->codec_type == WCD934X) { + /* Tavil does not support ANA_CLK_TOP register */ + if (reg == WCD9335_ANA_CLK_TOP) + return 0; + } else { + /* Tasha does not support CLK_SYS_MCLK_PRG register */ + if (reg == WCD934X_CLK_SYS_MCLK_PRG) + return 0; + } + if (resmgr->codec) { + ret = snd_soc_update_bits(resmgr->codec, reg, mask, val); + } else if (resmgr->core_res->wcd_core_regmap) { + ret = regmap_update_bits_check( + resmgr->core_res->wcd_core_regmap, + reg, mask, val, &change); + if (!ret) + ret = change; + } else { + pr_err("%s: codec/regmap not defined\n", __func__); + ret = -EINVAL; + } + + return ret; +} + +static int wcd_resmgr_codec_reg_read(struct wcd9xxx_resmgr_v2 *resmgr, + unsigned int reg) +{ + int val, ret; + + if (resmgr->codec_type == WCD934X) { + if (reg == WCD9335_ANA_CLK_TOP) + return 0; + } else { + if (reg == WCD934X_CLK_SYS_MCLK_PRG) + return 0; + } + if (resmgr->codec) { + val = snd_soc_read(resmgr->codec, reg); + } else if (resmgr->core_res->wcd_core_regmap) { + ret = regmap_read(resmgr->core_res->wcd_core_regmap, + reg, &val); + if (ret) + val = ret; + } else { + pr_err("%s: wcd regmap is null\n", __func__); + return -EINVAL; + } + + return val; +} + +/* + * wcd_resmgr_get_clk_type() + * Returns clk type that is currently enabled + */ +int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if (!resmgr) { + pr_err("%s: resmgr not initialized\n", __func__); + return -EINVAL; + } + return resmgr->clk_type; +} +EXPORT_SYMBOL(wcd_resmgr_get_clk_type); + +static void wcd_resmgr_cdc_specific_get_clk(struct wcd9xxx_resmgr_v2 *resmgr, + int clk_users) +{ + /* Caller of this function should have acquired BG_CLK lock */ + if (clk_users) { + if (resmgr->resmgr_cb && + resmgr->resmgr_cb->cdc_rco_ctrl) { + while (clk_users--) + resmgr->resmgr_cb->cdc_rco_ctrl(resmgr->codec, + true); + } + } +} + +/* + * wcd_resmgr_post_ssr_v2 + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr) +{ + int old_bg_audio_users; + int old_clk_rco_users, old_clk_mclk_users; + + WCD9XXX_V2_BG_CLK_LOCK(resmgr); + + old_bg_audio_users = resmgr->master_bias_users; + old_clk_mclk_users = resmgr->clk_mclk_users; + old_clk_rco_users = resmgr->clk_rco_users; + resmgr->master_bias_users = 0; + resmgr->clk_mclk_users = 0; + resmgr->clk_rco_users = 0; + resmgr->clk_type = WCD_CLK_OFF; + + pr_debug("%s: old_bg_audio_users=%d old_clk_mclk_users=%d old_clk_rco_users=%d\n", + __func__, old_bg_audio_users, + old_clk_mclk_users, old_clk_rco_users); + + if (old_bg_audio_users) { + while (old_bg_audio_users--) + wcd_resmgr_enable_master_bias(resmgr); + } + + if (old_clk_mclk_users) { + while (old_clk_mclk_users--) + wcd_resmgr_enable_clk_block(resmgr, WCD_CLK_MCLK); + } + + if (old_clk_rco_users) + wcd_resmgr_cdc_specific_get_clk(resmgr, old_clk_rco_users); + + WCD9XXX_V2_BG_CLK_UNLOCK(resmgr); +} +EXPORT_SYMBOL(wcd_resmgr_post_ssr_v2); + +/* + * wcd_resmgr_enable_master_bias: enable codec master bias + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_lock(&resmgr->master_bias_lock); + + resmgr->master_bias_users++; + if (resmgr->master_bias_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x80, 0x80); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x40, 0x40); + /* + * 1ms delay is required after pre-charge is enabled + * as per HW requirement + */ + usleep_range(1000, 1100); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x40, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_BIAS, 0x20, 0x00); + } + + pr_debug("%s: current master bias users: %d\n", __func__, + resmgr->master_bias_users); + + mutex_unlock(&resmgr->master_bias_lock); + return 0; +} +EXPORT_SYMBOL(wcd_resmgr_enable_master_bias); + +/* + * wcd_resmgr_disable_master_bias: disable codec master bias + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_lock(&resmgr->master_bias_lock); + if (resmgr->master_bias_users <= 0) { + mutex_unlock(&resmgr->master_bias_lock); + return -EINVAL; + } + + resmgr->master_bias_users--; + if (resmgr->master_bias_users == 0) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS, + 0x80, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_ANA_BIAS, 0x20, 0x00); + } + mutex_unlock(&resmgr->master_bias_lock); + return 0; +} +EXPORT_SYMBOL(wcd_resmgr_disable_master_bias); + +static int wcd_resmgr_enable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr) +{ + /* Enable mclk requires master bias to be enabled first */ + if (resmgr->master_bias_users <= 0) { + pr_err("%s: Cannot turn on MCLK, BG is not enabled\n", + __func__); + return -EINVAL; + } + + if (((resmgr->clk_mclk_users == 0) && + (resmgr->clk_type == WCD_CLK_MCLK)) || + ((resmgr->clk_mclk_users > 0) && + (resmgr->clk_type != WCD_CLK_MCLK))) { + pr_err("%s: Error enabling MCLK, clk_type: %s\n", + __func__, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + return -EINVAL; + } + + if (++resmgr->clk_mclk_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, 0x80, 0x80); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, 0x08, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, 0x04, 0x04); + if (resmgr->codec_type == WCD934X) { + /* + * In tavil clock contrl register is changed + * to CLK_SYS_MCLK_PRG + */ + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x80, 0x80); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x30, 0x10); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CODEC_RPM_CLK_GATE, 0x03, 0x00); + } else { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + } + /* + * 10us sleep is required after clock is enabled + * as per HW requirement + */ + usleep_range(10, 15); + } + + resmgr->clk_type = WCD_CLK_MCLK; + + pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__, + resmgr->clk_mclk_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static int wcd_resmgr_disable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if (resmgr->clk_mclk_users <= 0) { + pr_err("%s: No mclk users, cannot disable mclk\n", __func__); + return -EINVAL; + } + + if (--resmgr->clk_mclk_users == 0) { + if (resmgr->clk_rco_users > 0) { + /* MCLK to RCO switch */ + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, + 0x08, 0x08); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x02); + /* Disable clock buffer */ + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x80, 0x00); + resmgr->clk_type = WCD_CLK_RCO; + } else { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, + 0x04, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, 0x81, 0x00); + resmgr->clk_type = WCD_CLK_OFF; + } + + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP, + 0x80, 0x00); + } + + if ((resmgr->codec_type == WCD934X) && + (resmgr->clk_type == WCD_CLK_OFF)) + wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL); + + pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__, + resmgr->clk_mclk_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static void wcd_resmgr_set_buck_accuracy(struct wcd9xxx_resmgr_v2 *resmgr) +{ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x02, 0x02); + /* 100us sleep needed after HIGH_ACCURACY_PRE_EN1 */ + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x01, 0x01); + /* 100us sleep needed after HIGH_ACCURACY_PRE_EN2 */ + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x04, 0x04); + /* 100us sleep needed after HIGH_ACCURACY_EN */ + usleep_range(100, 110); +} + +static int wcd_resmgr_enable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr) +{ + bool rco_cal_done = true; + + resmgr->clk_rco_users++; + if ((resmgr->clk_rco_users == 1) && + ((resmgr->clk_type == WCD_CLK_OFF) || + (resmgr->clk_mclk_users == 0))) { + pr_warn("%s: RCO enable requires MCLK to be ON first\n", + __func__); + resmgr->clk_rco_users--; + return -EINVAL; + } else if ((resmgr->clk_rco_users == 1) && + (resmgr->clk_mclk_users)) { + /* RCO Enable */ + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_RCO, + 0x80, 0x80); + if (resmgr->codec_type == WCD934X) + wcd_resmgr_set_buck_accuracy(resmgr); + } + + /* + * 20us required after RCO BG is enabled as per HW + * requirements + */ + usleep_range(20, 25); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x40, 0x40); + /* + * 20us required after RCO is enabled as per HW + * requirements + */ + usleep_range(20, 25); + /* RCO Calibration */ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x04, 0x04); + if (resmgr->codec_type == WCD934X) + /* + * For wcd934x codec, 20us sleep is needed + * after enabling RCO calibration + */ + usleep_range(20, 25); + + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x04, 0x00); + if (resmgr->codec_type == WCD934X) + /* + * For wcd934x codec, 20us sleep is needed + * after disabling RCO calibration + */ + usleep_range(20, 25); + + /* RCO calibration takes app. 5ms to complete */ + usleep_range(WCD9XXX_RCO_CALIBRATION_DELAY_INC_US, + WCD9XXX_RCO_CALIBRATION_DELAY_INC_US + 100); + if (wcd_resmgr_codec_reg_read(resmgr, WCD9335_ANA_RCO) & 0x02) + rco_cal_done = false; + + WARN((!rco_cal_done), "RCO Calibration failed\n"); + + /* Switch MUX to RCO */ + if (resmgr->clk_mclk_users == 1) { + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_CLK_TOP, + 0x08, 0x08); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, + 0x02, 0x02); + resmgr->clk_type = WCD_CLK_RCO; + } + } + pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__, + resmgr->clk_rco_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +static int wcd_resmgr_disable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr) +{ + if ((resmgr->clk_rco_users <= 0) || + (resmgr->clk_type == WCD_CLK_OFF)) { + pr_err("%s: rco_clk_users = %d, clk_type = %d, cannot disable\n", + __func__, resmgr->clk_rco_users, resmgr->clk_type); + return -EINVAL; + } + + resmgr->clk_rco_users--; + + if ((resmgr->clk_rco_users == 0) && + (resmgr->clk_type == WCD_CLK_RCO)) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP, + 0x08, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, + 0x02, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP, + 0x04, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x40, 0x00); + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_RCO, + 0x80, 0x00); + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD934X_CLK_SYS_MCLK_PRG, + 0x01, 0x00); + resmgr->clk_type = WCD_CLK_OFF; + } else if ((resmgr->clk_rco_users == 0) && + (resmgr->clk_mclk_users)) { + /* Disable RCO while MCLK is ON */ + wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO, + 0x40, 0x00); + if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) + wcd_resmgr_codec_reg_update_bits(resmgr, + WCD9335_ANA_RCO, + 0x80, 0x00); + } + + if ((resmgr->codec_type == WCD934X) && + (resmgr->clk_type == WCD_CLK_OFF)) + wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL); + + pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__, + resmgr->clk_rco_users, + wcd_resmgr_clk_type_to_str(resmgr->clk_type)); + + return 0; +} + +/* + * wcd_resmgr_enable_clk_block: enable MCLK or RCO + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @type: Clock type to enable + */ +int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type) +{ + int ret; + + switch (type) { + case WCD_CLK_MCLK: + ret = wcd_resmgr_enable_clk_mclk(resmgr); + break; + case WCD_CLK_RCO: + ret = wcd_resmgr_enable_clk_rco(resmgr); + break; + default: + pr_err("%s: Unknown Clock type: %s\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + ret = -EINVAL; + break; + }; + + if (ret) + pr_err("%s: Enable clock %s failed\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + + return ret; +} +EXPORT_SYMBOL(wcd_resmgr_enable_clk_block); + +void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src) +{ + if (!resmgr) + return; + + if (sido_src == resmgr->sido_input_src) + return; + + if (sido_src == SIDO_SOURCE_INTERNAL) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x04, 0x00); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x03, 0x00); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_RCO, + 0x80, 0x00); + usleep_range(100, 110); + resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + pr_debug("%s: sido input src to internal\n", __func__); + } else if (sido_src == SIDO_SOURCE_RCO_BG) { + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_RCO, + 0x80, 0x80); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x02, 0x02); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x01, 0x01); + usleep_range(100, 110); + wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL, + 0x04, 0x04); + usleep_range(100, 110); + resmgr->sido_input_src = SIDO_SOURCE_RCO_BG; + pr_debug("%s: sido input src to external\n", __func__); + } +} +EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src); + +/* + * wcd_resmgr_set_sido_input_src_locked: + * Set SIDO input in BG_CLK locked context + * + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @sido_src: Select the SIDO input source + */ +void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src) +{ + if (!resmgr) + return; + + WCD9XXX_V2_BG_CLK_LOCK(resmgr); + wcd_resmgr_set_sido_input_src(resmgr, sido_src); + WCD9XXX_V2_BG_CLK_UNLOCK(resmgr); +} +EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src_locked); + +/* + * wcd_resmgr_disable_clk_block: disable MCLK or RCO + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + * @type: Clock type to disable + */ +int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type) +{ + int ret; + + switch (type) { + case WCD_CLK_MCLK: + ret = wcd_resmgr_disable_clk_mclk(resmgr); + break; + case WCD_CLK_RCO: + ret = wcd_resmgr_disable_clk_rco(resmgr); + break; + default: + pr_err("%s: Unknown Clock type: %s\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + ret = -EINVAL; + break; + }; + + if (ret) + pr_err("%s: Disable clock %s failed\n", __func__, + wcd_resmgr_clk_type_to_str(type)); + + return ret; +} +EXPORT_SYMBOL(wcd_resmgr_disable_clk_block); + +/* + * wcd_resmgr_init: initialize wcd resource manager + * @core_res: handle to struct wcd9xxx_core_resource + * + * Early init call without a handle to snd_soc_codec * + */ +struct wcd9xxx_resmgr_v2 *wcd_resmgr_init( + struct wcd9xxx_core_resource *core_res, + struct snd_soc_codec *codec) +{ + struct wcd9xxx_resmgr_v2 *resmgr; + struct wcd9xxx *wcd9xxx; + + resmgr = kzalloc(sizeof(struct wcd9xxx_resmgr_v2), GFP_KERNEL); + if (!resmgr) + return ERR_PTR(-ENOMEM); + + wcd9xxx = container_of(core_res, struct wcd9xxx, core_res); + if (!wcd9xxx) { + kfree(resmgr); + pr_err("%s: Cannot get wcd9xx pointer\n", __func__); + return ERR_PTR(-EINVAL); + } + + mutex_init(&resmgr->codec_bg_clk_lock); + mutex_init(&resmgr->master_bias_lock); + resmgr->master_bias_users = 0; + resmgr->clk_mclk_users = 0; + resmgr->clk_rco_users = 0; + resmgr->master_bias_users = 0; + resmgr->codec = codec; + resmgr->core_res = core_res; + resmgr->sido_input_src = SIDO_SOURCE_INTERNAL; + resmgr->codec_type = wcd9xxx->type; + + return resmgr; +} +EXPORT_SYMBOL(wcd_resmgr_init); + +/* + * wcd_resmgr_remove: Clean-up wcd resource manager + * @resmgr: handle to struct wcd9xxx_resmgr_v2 + */ +void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr) +{ + mutex_destroy(&resmgr->master_bias_lock); + kfree(resmgr); +} +EXPORT_SYMBOL(wcd_resmgr_remove); + +/* + * wcd_resmgr_post_init: post init call to assign codec handle + * @resmgr: handle to struct wcd9xxx_resmgr_v2 created during early init + * @resmgr_cb: codec callback function for resmgr + * @codec: handle to struct snd_soc_codec + */ +int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr, + const struct wcd_resmgr_cb *resmgr_cb, + struct snd_soc_codec *codec) +{ + if (!resmgr) { + pr_err("%s: resmgr not allocated\n", __func__); + return -EINVAL; + } + + if (!codec) { + pr_err("%s: Codec memory is NULL, nothing to post init\n", + __func__); + return -EINVAL; + } + + resmgr->codec = codec; + resmgr->resmgr_cb = resmgr_cb; + + return 0; +} +EXPORT_SYMBOL(wcd_resmgr_post_init); + +MODULE_DESCRIPTION("wcd9xxx resmgr v2 module"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.h b/sound/soc/codecs/wcd9xxx-resmgr-v2.h new file mode 100644 index 000000000000..e831ba61e9c2 --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-resmgr-v2.h @@ -0,0 +1,93 @@ +/* + * 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 __WCD9XXX_COMMON_V2_H__ +#define __WCD9XXX_COMMON_V2_H__ + +#include +#include + +enum wcd_clock_type { + WCD_CLK_OFF, + WCD_CLK_RCO, + WCD_CLK_MCLK, +}; + +enum { + SIDO_SOURCE_INTERNAL, + SIDO_SOURCE_RCO_BG, +}; + +struct wcd_resmgr_cb { + int (*cdc_rco_ctrl)(struct snd_soc_codec *, bool); +}; + +struct wcd9xxx_resmgr_v2 { + struct snd_soc_codec *codec; + struct wcd9xxx_core_resource *core_res; + + int master_bias_users; + int clk_mclk_users; + int clk_rco_users; + + struct mutex codec_bg_clk_lock; + struct mutex master_bias_lock; + + enum codec_variant codec_type; + enum wcd_clock_type clk_type; + + const struct wcd_resmgr_cb *resmgr_cb; + int sido_input_src; +}; + +#define WCD9XXX_V2_BG_CLK_LOCK(resmgr) \ +{ \ + struct wcd9xxx_resmgr_v2 *__resmgr = resmgr; \ + pr_debug("%s: Acquiring BG_CLK\n", __func__); \ + mutex_lock(&__resmgr->codec_bg_clk_lock); \ + pr_debug("%s: Acquiring BG_CLK done\n", __func__); \ +} + +#define WCD9XXX_V2_BG_CLK_UNLOCK(resmgr) \ +{ \ + struct wcd9xxx_resmgr_v2 *__resmgr = resmgr; \ + pr_debug("%s: Releasing BG_CLK\n", __func__); \ + mutex_unlock(&__resmgr->codec_bg_clk_lock); \ +} + +#define WCD9XXX_V2_BG_CLK_ASSERT_LOCKED(resmgr) \ +{ \ + WARN_ONCE(!mutex_is_locked(&resmgr->codec_bg_clk_lock), \ + "%s: BG_CLK lock should have acquired\n", __func__); \ +} + +int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr); +int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr); +struct wcd9xxx_resmgr_v2 *wcd_resmgr_init( + struct wcd9xxx_core_resource *core_res, + struct snd_soc_codec *codec); +void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr); +int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr, + const struct wcd_resmgr_cb *resmgr_cb, + struct snd_soc_codec *codec); +int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type); +int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr, + enum wcd_clock_type type); +int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr); +void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr); +void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src); +void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr, + int sido_src); + +#endif diff --git a/sound/soc/codecs/wcd9xxx-soc-init.c b/sound/soc/codecs/wcd9xxx-soc-init.c new file mode 100644 index 000000000000..fa8abb7de5e4 --- /dev/null +++ b/sound/soc/codecs/wcd9xxx-soc-init.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 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 "audio-ext-clk-up.h" + +static int __init wcd9xxx_soc_init(void) +{ + int ret = 0; + + ret = wcd_dsp_mgr_init(); + if (!ret) { + ret = audio_ref_clk_platform_init(); + if (ret) { + pr_err("%s: init extclk fail: %d\n", __func__, ret); + wcd_dsp_mgr_exit(); + } + } else { + pr_err("%s: init dsp mgr fail: %d\n", __func__, ret); + } + + return ret; +} +module_init(wcd9xxx_soc_init); + +static void __exit wcd9xxx_soc_exit(void) +{ + audio_ref_clk_platform_exit(); + wcd_dsp_mgr_exit(); +} +module_exit(wcd9xxx_soc_exit); + +MODULE_DESCRIPTION("WCD9XXX CODEC soc init driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd_cmi_api.h b/sound/soc/codecs/wcd_cmi_api.h new file mode 100644 index 000000000000..39be6417e327 --- /dev/null +++ b/sound/soc/codecs/wcd_cmi_api.h @@ -0,0 +1,43 @@ +/* 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 __CMI_API__ +#define __CMI_API__ + +enum cmi_api_result { + CMI_API_FAILED = 1, + CMI_API_BUSY, + CMI_API_NO_MEMORY, + CMI_API_NOT_READY, +}; + +enum cmi_api_event { + CMI_API_MSG = 1, + CMI_API_OFFLINE, + CMI_API_ONLINE, + CMI_API_DEINITIALIZED, +}; + +struct cmi_api_notification { + enum cmi_api_event event; + enum cmi_api_result result; + void *message; +}; + +void *cmi_register( + void notification_callback + (const struct cmi_api_notification *parameter), + u32 service); +enum cmi_api_result cmi_deregister(void *reg_handle); +enum cmi_api_result cmi_send_msg(void *message); + +#endif /*__CMI_API__*/ diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c new file mode 100644 index 000000000000..f2a20d51d0e2 --- /dev/null +++ b/sound/soc/codecs/wcd_cpe_core.c @@ -0,0 +1,4579 @@ +/* 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 "wcd_cpe_core.h" +#include "wcd_cpe_services.h" +#include "wcd_cmi_api.h" + +#define CMI_CMD_TIMEOUT (10 * HZ) +#define WCD_CPE_LSM_MAX_SESSIONS 2 +#define WCD_CPE_AFE_MAX_PORTS 4 +#define AFE_SVC_EXPLICIT_PORT_START 1 +#define WCD_CPE_EC_PP_BUF_SIZE 480 /* 5 msec buffer */ + +#define ELF_FLAG_EXECUTE (1 << 0) +#define ELF_FLAG_WRITE (1 << 1) +#define ELF_FLAG_READ (1 << 2) + +#define ELF_FLAG_RW (ELF_FLAG_READ | ELF_FLAG_WRITE) + +#define WCD_CPE_GRAB_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock acquire\n", \ + __func__, name); \ + mutex_lock(lock); \ +} + +#define WCD_CPE_REL_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock release\n", \ + __func__, name); \ + mutex_unlock(lock); \ +} + +#define WCD_CPE_STATE_MAX_LEN 11 +#define CPE_OFFLINE_WAIT_TIMEOUT (2 * HZ) +#define CPE_READY_WAIT_TIMEOUT (3 * HZ) +#define WCD_CPE_SYSFS_DIR_MAX_LENGTH 32 + +#define CPE_ERR_IRQ_CB(core) \ + (core->cpe_cdc_cb->cpe_err_irq_control) + +/* + * AFE output buffer size is always + * (sample_rate * number of bytes per sample/2*1000) + */ +#define AFE_OUT_BUF_SIZE(bit_width, sample_rate) \ + (((sample_rate) * (bit_width / BITS_PER_BYTE))/(2*1000)) + +enum afe_port_state { + AFE_PORT_STATE_DEINIT = 0, + AFE_PORT_STATE_INIT, + AFE_PORT_STATE_CONFIG, + AFE_PORT_STATE_STARTED, + AFE_PORT_STATE_SUSPENDED, +}; + +struct wcd_cmi_afe_port_data { + u8 port_id; + struct mutex afe_lock; + struct completion afe_cmd_complete; + enum afe_port_state port_state; + u8 cmd_result; + u32 mem_handle; +}; + +struct cpe_lsm_ids { + u32 module_id; + u32 param_id; +}; + +static struct wcd_cpe_core *core_d; +static struct cpe_lsm_session + *lsm_sessions[WCD_CPE_LSM_MAX_SESSIONS + 1]; +struct wcd_cpe_core * (*wcd_get_cpe_core)(struct snd_soc_codec *); +static struct wcd_cmi_afe_port_data afe_ports[WCD_CPE_AFE_MAX_PORTS + 1]; +static void wcd_cpe_svc_event_cb(const struct cpe_svc_notification *param); +static int wcd_cpe_setup_irqs(struct wcd_cpe_core *core); +static void wcd_cpe_cleanup_irqs(struct wcd_cpe_core *core); +static ssize_t cpe_ftm_test_trigger(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos); +static u32 ramdump_enable; +static u32 cpe_ftm_test_status; +static const struct file_operations cpe_ftm_test_trigger_fops = { + .open = simple_open, + .write = cpe_ftm_test_trigger, +}; + +static int wcd_cpe_afe_svc_cmd_mode(void *core_handle, + u8 mode); +struct wcd_cpe_attribute { + struct attribute attr; + ssize_t (*show)(struct wcd_cpe_core *core, char *buf); + ssize_t (*store)(struct wcd_cpe_core *core, const char *buf, + ssize_t count); +}; + +#define WCD_CPE_ATTR(_name, _mode, _show, _store) \ +static struct wcd_cpe_attribute cpe_attr_##_name = { \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ + .show = _show, \ + .store = _store, \ +} + +#define to_wcd_cpe_attr(a) \ + container_of((a), struct wcd_cpe_attribute, attr) + +#define kobj_to_cpe_core(kobj) \ + container_of((kobj), struct wcd_cpe_core, cpe_kobj) + +/* wcd_cpe_lsm_session_active: check if any session is active + * return true if any session is active. + */ +static bool wcd_cpe_lsm_session_active(void) +{ + int index = 1; + bool lsm_active = false; + + /* session starts from index 1 */ + for (; index <= WCD_CPE_LSM_MAX_SESSIONS; index++) { + if (lsm_sessions[index] != NULL) { + lsm_active = true; + break; + } else { + lsm_active = false; + } + } + return lsm_active; +} + +static int wcd_cpe_get_sfr_dump(struct wcd_cpe_core *core) +{ + struct cpe_svc_mem_segment dump_seg; + int rc; + u8 *sfr_dump; + + sfr_dump = kzalloc(core->sfr_buf_size, GFP_KERNEL); + if (!sfr_dump) + goto done; + + dump_seg.type = CPE_SVC_DATA_MEM; + dump_seg.cpe_addr = core->sfr_buf_addr; + dump_seg.size = core->sfr_buf_size; + dump_seg.data = sfr_dump; + dev_dbg(core->dev, + "%s: reading SFR from CPE, size = %zu\n", + __func__, core->sfr_buf_size); + + rc = cpe_svc_ramdump(core->cpe_handle, &dump_seg); + if (rc < 0) { + dev_err(core->dev, + "%s: Failed to read cpe sfr_dump, err = %d\n", + __func__, rc); + goto free_sfr_dump; + } + + dev_info(core->dev, + "%s: cpe_sfr = %s\n", __func__, sfr_dump); + +free_sfr_dump: + kfree(sfr_dump); +done: + /* Even if SFR dump failed, do not return error */ + return 0; +} + +static int wcd_cpe_collect_ramdump(struct wcd_cpe_core *core) +{ + struct cpe_svc_mem_segment dump_seg; + int rc; + + if (!core->cpe_ramdump_dev || !core->cpe_dump_v_addr || + core->hw_info.dram_size == 0) { + dev_err(core->dev, + "%s: Ramdump devices not set up, size = %zu\n", + __func__, core->hw_info.dram_size); + return -EINVAL; + } + + dump_seg.type = CPE_SVC_DATA_MEM; + dump_seg.cpe_addr = core->hw_info.dram_offset; + dump_seg.size = core->hw_info.dram_size; + dump_seg.data = core->cpe_dump_v_addr; + + dev_dbg(core->dev, + "%s: Reading ramdump from CPE\n", + __func__); + + rc = cpe_svc_ramdump(core->cpe_handle, &dump_seg); + if (rc < 0) { + dev_err(core->dev, + "%s: Failed to read CPE ramdump, err = %d\n", + __func__, rc); + return rc; + } + + dev_dbg(core->dev, + "%s: completed reading ramdump from CPE\n", + __func__); + + core->cpe_ramdump_seg.address = (unsigned long) core->cpe_dump_addr; + core->cpe_ramdump_seg.size = core->hw_info.dram_size; + core->cpe_ramdump_seg.v_address = core->cpe_dump_v_addr; + + rc = do_ramdump(core->cpe_ramdump_dev, + &core->cpe_ramdump_seg, 1); + if (rc) + dev_err(core->dev, + "%s: fail to dump cpe ram to device, err = %d\n", + __func__, rc); + return rc; +} + +/* wcd_cpe_is_valid_elf_hdr: check if the ELF header is valid + * @core: handle to wcd_cpe_core + * @fw_size: size of firmware from request_firmware + * @ehdr: the elf header to be checked for + * return true if all checks pass, true if any elf check fails + */ +static bool wcd_cpe_is_valid_elf_hdr(struct wcd_cpe_core *core, size_t fw_size, + const struct elf32_hdr *ehdr) +{ + if (fw_size < sizeof(*ehdr)) { + dev_err(core->dev, "%s:Firmware too small\n", __func__); + goto elf_check_fail; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) { + dev_err(core->dev, "%s: Not an ELF file\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) { + dev_err(core->dev, "%s: Not a executable image\n", __func__); + goto elf_check_fail; + } + + if (ehdr->e_phnum == 0) { + dev_err(core->dev, "%s: no segments to load\n", __func__); + goto elf_check_fail; + } + + if (sizeof(struct elf32_phdr) * ehdr->e_phnum + + sizeof(struct elf32_hdr) > fw_size) { + dev_err(core->dev, "%s: Too small MDT file\n", __func__); + goto elf_check_fail; + } + + return true; + +elf_check_fail: + return false; +} + +/* + * wcd_cpe_load_each_segment: download segment to CPE + * @core: handle to struct wcd_cpe_core + * @file_idx: index of split firmware image file name + * @phdr: program header from metadata + */ +static int wcd_cpe_load_each_segment(struct wcd_cpe_core *core, + int file_idx, const struct elf32_phdr *phdr) +{ + const struct firmware *split_fw; + char split_fname[32]; + int ret = 0; + struct cpe_svc_mem_segment *segment; + + if (!core || !phdr) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + + /* file size can be 0 for bss segments */ + if (phdr->p_filesz == 0 || phdr->p_memsz == 0) + return 0; + + segment = kzalloc(sizeof(struct cpe_svc_mem_segment), GFP_KERNEL); + if (!segment) + return -ENOMEM; + + snprintf(split_fname, sizeof(split_fname), "%s.b%02d", + core->fname, file_idx); + + ret = request_firmware(&split_fw, split_fname, core->dev); + if (ret) { + dev_err(core->dev, "firmware %s not found\n", + split_fname); + ret = -EIO; + goto fw_req_fail; + } + + if (phdr->p_flags & ELF_FLAG_EXECUTE) + segment->type = CPE_SVC_INSTRUCTION_MEM; + else if (phdr->p_flags & ELF_FLAG_RW) + segment->type = CPE_SVC_DATA_MEM; + else { + dev_err(core->dev, "%s invalid flags 0x%x\n", + __func__, phdr->p_flags); + goto done; + } + + segment->cpe_addr = phdr->p_paddr; + segment->size = phdr->p_filesz; + segment->data = (u8 *) split_fw->data; + + dev_dbg(core->dev, + "%s: cpe segment type %s read from firmware\n", __func__, + (segment->type == CPE_SVC_INSTRUCTION_MEM) ? + "INSTRUCTION" : "DATA"); + + ret = cpe_svc_download_segment(core->cpe_handle, segment); + if (ret) { + dev_err(core->dev, + "%s: Failed to download %s, error = %d\n", + __func__, split_fname, ret); + goto done; + } + +done: + release_firmware(split_fw); + +fw_req_fail: + kfree(segment); + return ret; +} + +/* + * wcd_cpe_enable_cpe_clks: enable the clocks for CPE + * @core: handle to wcd_cpe_core + * @enable: flag indicating whether to enable/disable cpe clocks + */ +static int wcd_cpe_enable_cpe_clks(struct wcd_cpe_core *core, bool enable) +{ + int ret, ret1; + + if (!core || !core->cpe_cdc_cb || + !core->cpe_cdc_cb->cpe_clk_en) { + pr_err("%s: invalid handle\n", + __func__); + return -EINVAL; + } + + ret = core->cpe_cdc_cb->cdc_clk_en(core->codec, enable); + if (ret) { + dev_err(core->dev, "%s: Failed to enable RCO\n", + __func__); + return ret; + } + + if (!enable && core->cpe_clk_ref > 0) + core->cpe_clk_ref--; + + /* + * CPE clk will be enabled at the first time + * and be disabled at the last time. + */ + if (core->cpe_clk_ref == 0) { + ret = core->cpe_cdc_cb->cpe_clk_en(core->codec, enable); + if (ret) { + dev_err(core->dev, + "%s: cpe_clk_en() failed, err = %d\n", + __func__, ret); + goto cpe_clk_fail; + } + } + + if (enable) + core->cpe_clk_ref++; + + return 0; + +cpe_clk_fail: + /* Release the codec clk if CPE clk enable failed */ + if (enable) { + ret1 = core->cpe_cdc_cb->cdc_clk_en(core->codec, !enable); + if (ret1) + dev_err(core->dev, + "%s: Fail to release codec clk, err = %d\n", + __func__, ret1); + } + + return ret; +} + +/* + * wcd_cpe_bus_vote_max_bw: Function to vote for max bandwidth on codec bus + * @core: handle to core for cpe + * @vote: flag to indicate enable/disable of vote + * + * This function will try to use the codec provided callback to + * vote/unvote for the max bandwidth of the bus that is used by + * the codec for register reads/writes. + */ +static int wcd_cpe_bus_vote_max_bw(struct wcd_cpe_core *core, + bool vote) +{ + if (!core || !core->cpe_cdc_cb) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!core) ? "core" : "codec callbacks"); + return -EINVAL; + } + + if (core->cpe_cdc_cb->bus_vote_bw) { + dev_dbg(core->dev, "%s: %s cdc bus max bandwidth\n", + __func__, vote ? "Vote" : "Unvote"); + core->cpe_cdc_cb->bus_vote_bw(core->codec, vote); + } + + return 0; +} + +/* + * wcd_cpe_load_fw: Function to load the fw image + * @core: cpe core pointer + * @load_type: indicates whether to load to data section + * or the instruction section + * + * Parse the mdt file to look for program headers, load each + * split file corresponding to the program headers. + */ +static int wcd_cpe_load_fw(struct wcd_cpe_core *core, + unsigned int load_type) +{ + + int ret, phdr_idx; + struct snd_soc_codec *codec = NULL; + struct wcd9xxx *wcd9xxx = NULL; + const struct elf32_hdr *ehdr; + const struct elf32_phdr *phdr; + const struct firmware *fw; + const u8 *elf_ptr; + char mdt_name[64]; + bool img_dload_fail = false; + bool load_segment; + + if (!core || !core->cpe_handle) { + pr_err("%s: Error CPE core %pK\n", __func__, + core); + return -EINVAL; + } + codec = core->codec; + wcd9xxx = dev_get_drvdata(codec->dev->parent); + snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", core->fname); + ret = request_firmware(&fw, mdt_name, core->dev); + if (ret < 0) { + dev_err(core->dev, "firmware %s not found\n", mdt_name); + return ret; + } + + ehdr = (struct elf32_hdr *) fw->data; + if (!wcd_cpe_is_valid_elf_hdr(core, fw->size, ehdr)) { + dev_err(core->dev, "%s: fw mdt %s is invalid\n", + __func__, mdt_name); + ret = -EINVAL; + goto done; + } + + elf_ptr = fw->data + sizeof(*ehdr); + + if (load_type == ELF_FLAG_EXECUTE) { + /* Reset CPE first */ + ret = cpe_svc_reset(core->cpe_handle); + if (ret < 0) { + dev_err(core->dev, + "%s: Failed to reset CPE with error %d\n", + __func__, ret); + goto done; + } + } + + dev_dbg(core->dev, "%s: start image dload, name = %s, load_type = 0x%x\n", + __func__, core->fname, load_type); + + wcd_cpe_bus_vote_max_bw(core, true); + + /* parse every program header and request corresponding firmware */ + for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) { + phdr = (struct elf32_phdr *)elf_ptr; + load_segment = false; + + dev_dbg(core->dev, + "index = %d, vaddr = 0x%x, paddr = 0x%x, filesz = 0x%x, memsz = 0x%x, flags = 0x%x\n" + , phdr_idx, phdr->p_vaddr, phdr->p_paddr, + phdr->p_filesz, phdr->p_memsz, phdr->p_flags); + + switch (load_type) { + case ELF_FLAG_EXECUTE: + if (phdr->p_flags & load_type) + load_segment = true; + break; + case ELF_FLAG_RW: + if (!(phdr->p_flags & ELF_FLAG_EXECUTE) && + (phdr->p_flags & load_type)) + load_segment = true; + break; + default: + pr_err("%s: Invalid load_type 0x%x\n", + __func__, load_type); + ret = -EINVAL; + goto rel_bus_vote; + } + + if (load_segment) { + ret = wcd_cpe_load_each_segment(core, + phdr_idx, phdr); + if (ret < 0) { + dev_err(core->dev, + "Failed to load segment %d, aborting img dload\n", + phdr_idx); + img_dload_fail = true; + goto rel_bus_vote; + } + } else { + dev_dbg(core->dev, + "%s: skipped segment with index %d\n", + __func__, phdr_idx); + } + + elf_ptr = elf_ptr + sizeof(*phdr); + } + if (load_type == ELF_FLAG_EXECUTE) + core->ssr_type = WCD_CPE_IMEM_DOWNLOADED; + +rel_bus_vote: + wcd_cpe_bus_vote_max_bw(core, false); + +done: + release_firmware(fw); + return ret; +} + +/* + * wcd_cpe_change_online_state - mark cpe online/offline state + * @core: core session to mark + * @online: whether online of offline + * + */ +static void wcd_cpe_change_online_state(struct wcd_cpe_core *core, + int online) +{ + struct wcd_cpe_ssr_entry *ssr_entry = NULL; + unsigned long ret; + + if (!core) { + pr_err("%s: Invalid core handle\n", + __func__); + return; + } + + ssr_entry = &core->ssr_entry; + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + ssr_entry->offline = !online; + + /* Make sure write to offline state is completed. */ + wmb(); + ret = xchg(&ssr_entry->offline_change, 1); + wake_up_interruptible(&ssr_entry->offline_poll_wait); + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); + pr_debug("%s: change state 0x%x offline_change 0x%x\n" + " core->offline 0x%x, ret = %ld\n", + __func__, online, + ssr_entry->offline_change, + core->ssr_entry.offline, ret); +} + +/* + * wcd_cpe_load_fw_image: work function to load the fw image + * @work: work that is scheduled to perform the image loading + * + * Parse the mdt file to look for program headers, load each + * split file corresponding to the program headers. + */ +static void wcd_cpe_load_fw_image(struct work_struct *work) +{ + struct wcd_cpe_core *core; + int ret = 0; + + core = container_of(work, struct wcd_cpe_core, load_fw_work); + ret = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (!ret) + wcd_cpe_change_online_state(core, 1); + else + pr_err("%s: failed to load instruction section, err = %d\n", + __func__, ret); +} + +/* + * wcd_cpe_get_core_handle: get the handle to wcd_cpe_core + * @codec: codec from which this handle is to be obtained + * Codec driver should provide a callback function to obtain + * handle to wcd_cpe_core during initialization of wcd_cpe_core + */ +void *wcd_cpe_get_core_handle( + struct snd_soc_codec *codec) +{ + struct wcd_cpe_core *core = NULL; + + if (!codec) { + pr_err("%s: Invalid codec handle\n", + __func__); + goto done; + } + + if (!wcd_get_cpe_core) { + dev_err(codec->dev, + "%s: codec callback not available\n", + __func__); + goto done; + } + + core = wcd_get_cpe_core(codec); + + if (!core) + dev_err(codec->dev, + "%s: handle to core not available\n", + __func__); +done: + return core; +} +EXPORT_SYMBOL(wcd_cpe_get_core_handle); + +/* + * svass_engine_irq: threaded interrupt handler for svass engine irq + * @irq: interrupt number + * @data: data pointer passed during irq registration + */ +static irqreturn_t svass_engine_irq(int irq, void *data) +{ + struct wcd_cpe_core *core = data; + int ret = 0; + + if (!core) { + pr_err("%s: Invalid data for interrupt handler\n", + __func__); + goto done; + } + + ret = cpe_svc_process_irq(core->cpe_handle, CPE_IRQ_OUTBOX_IRQ); + if (ret < 0) + dev_err(core->dev, + "%s: Error processing irq from cpe_Services\n", + __func__); +done: + return IRQ_HANDLED; +} + +/* + * wcd_cpe_state_read - update read status in procfs + * @entry: snd_info_entry + * @buf: buffer where the read status is updated. + * + */ +static ssize_t wcd_cpe_state_read(struct snd_info_entry *entry, + void *file_private_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + int len = 0; + char buffer[WCD_CPE_STATE_MAX_LEN]; + struct wcd_cpe_core *core = NULL; + struct wcd_cpe_ssr_entry *ssr_entry = NULL; + + core = (struct wcd_cpe_core *) entry->private_data; + if (!core) { + pr_err("%s: CPE core NULL\n", __func__); + return -EINVAL; + } + ssr_entry = &core->ssr_entry; + + /* Make sure read from ssr_entry is completed. */ + rmb(); + dev_dbg(core->dev, + "%s: Offline 0x%x\n", __func__, + ssr_entry->offline); + + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + len = snprintf(buffer, sizeof(buffer), "%s\n", + ssr_entry->offline ? "OFFLINE" : "ONLINE"); + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +/* + * wcd_cpe_state_poll - polls for change state + * @entry: snd_info_entry + * @wait: wait for duration for poll wait + * + */ +static unsigned int wcd_cpe_state_poll(struct snd_info_entry *entry, + void *private_data, struct file *file, + poll_table *wait) +{ + struct wcd_cpe_core *core = NULL; + struct wcd_cpe_ssr_entry *ssr_entry = NULL; + int ret = 0; + + core = (struct wcd_cpe_core *) entry->private_data; + if (!core) { + pr_err("%s: CPE core NULL\n", __func__); + return -EINVAL; + } + + ssr_entry = &core->ssr_entry; + + dev_dbg(core->dev, "%s: CPE Poll wait\n", + __func__); + poll_wait(file, &ssr_entry->offline_poll_wait, wait); + dev_dbg(core->dev, "%s: Wake-up Poll wait\n", + __func__); + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + + if (xchg(&ssr_entry->offline_change, 0)) + ret = POLLIN | POLLPRI | POLLRDNORM; + + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); + + dev_dbg(core->dev, "%s: ret (%d) from poll_wait\n", + __func__, ret); + return ret; +} + +/* + * wcd_cpe_is_online_state - return true if card is online state + * @core: core offline to query + */ +static bool wcd_cpe_is_online_state(void *core_handle) +{ + struct wcd_cpe_core *core = core_handle; + + if (core_handle) { + return !core->ssr_entry.offline; + } else { + pr_err("%s: Core handle NULL\n", __func__); + /* still return 1- offline if core ptr null */ + return false; + } +} + +static struct snd_info_entry_ops wcd_cpe_state_proc_ops = { + .read = wcd_cpe_state_read, + .poll = wcd_cpe_state_poll, +}; + +static int wcd_cpe_check_new_image(struct wcd_cpe_core *core) +{ + int rc = 0; + char temp_img_name[WCD_CPE_IMAGE_FNAME_MAX]; + + if (!strcmp(core->fname, core->dyn_fname) && + core->ssr_type != WCD_CPE_INITIALIZED) { + dev_dbg(core->dev, + "%s: Firmware unchanged, fname = %s, ssr_type 0x%x\n", + __func__, core->fname, core->ssr_type); + goto done; + } + + /* + * Different firmware name requested, + * Re-load the instruction section + */ + strlcpy(temp_img_name, core->fname, + WCD_CPE_IMAGE_FNAME_MAX); + strlcpy(core->fname, core->dyn_fname, + WCD_CPE_IMAGE_FNAME_MAX); + + rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (rc) { + dev_err(core->dev, + "%s: Failed to dload new image %s, err = %d\n", + __func__, core->fname, rc); + /* If new image download failed, revert back to old image */ + strlcpy(core->fname, temp_img_name, + WCD_CPE_IMAGE_FNAME_MAX); + rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (rc) + dev_err(core->dev, + "%s: Failed to re-dload image %s, err = %d\n", + __func__, core->fname, rc); + } else { + dev_info(core->dev, "%s: fw changed to %s\n", + __func__, core->fname); + } +done: + return rc; +} + +static int wcd_cpe_enable(struct wcd_cpe_core *core, + bool enable) +{ + int ret = 0; + + if (enable) { + /* Reset CPE first */ + ret = cpe_svc_reset(core->cpe_handle); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE Reset failed, error = %d\n", + __func__, ret); + goto done; + } + + ret = wcd_cpe_setup_irqs(core); + if (ret) { + dev_err(core->dev, + "%s: CPE IRQs setup failed, error = %d\n", + __func__, ret); + goto done; + } + ret = wcd_cpe_check_new_image(core); + if (ret) + goto fail_boot; + + /* Dload data section */ + ret = wcd_cpe_load_fw(core, ELF_FLAG_RW); + if (ret) { + dev_err(core->dev, + "%s: Failed to dload data section, err = %d\n", + __func__, ret); + goto fail_boot; + } + + ret = wcd_cpe_enable_cpe_clks(core, true); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE clk enable failed, err = %d\n", + __func__, ret); + goto fail_boot; + } + + ret = cpe_svc_boot(core->cpe_handle, + core->cpe_debug_mode); + if (ret < 0) { + dev_err(core->dev, + "%s: Failed to boot CPE\n", + __func__); + goto fail_boot; + } + + /* wait for CPE to be online */ + dev_dbg(core->dev, + "%s: waiting for CPE bootup\n", + __func__); + + wait_for_completion(&core->online_compl); + + dev_dbg(core->dev, + "%s: CPE bootup done\n", + __func__); + + core->ssr_type = WCD_CPE_ENABLED; + } else { + if (core->ssr_type == WCD_CPE_BUS_DOWN_EVENT || + core->ssr_type == WCD_CPE_SSR_EVENT) { + /* + * If this disable vote is when + * SSR is in progress, do not disable CPE here, + * instead SSR handler will control CPE. + */ + wcd_cpe_enable_cpe_clks(core, false); + wcd_cpe_cleanup_irqs(core); + goto done; + } + + ret = cpe_svc_shutdown(core->cpe_handle); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE shutdown failed, error %d\n", + __func__, ret); + goto done; + } + + wcd_cpe_enable_cpe_clks(core, false); + wcd_cpe_cleanup_irqs(core); + core->ssr_type = WCD_CPE_IMEM_DOWNLOADED; + } + + return ret; + +fail_boot: + wcd_cpe_cleanup_irqs(core); + +done: + return ret; +} + +/* + * wcd_cpe_boot_ssr: Load the images to CPE after ssr and bootup cpe + * @core: handle to the core + */ +static int wcd_cpe_boot_ssr(struct wcd_cpe_core *core) +{ + int rc = 0; + + if (!core || !core->cpe_handle) { + pr_err("%s: Invalid handle\n", __func__); + rc = -EINVAL; + goto fail; + } + /* Load the instruction section and mark CPE as online */ + rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE); + if (rc) { + dev_err(core->dev, + "%s: Failed to load instruction, err = %d\n", + __func__, rc); + goto fail; + } else { + wcd_cpe_change_online_state(core, 1); + } + +fail: + return rc; +} + +/* + * wcd_cpe_clr_ready_status: + * Clear the value from the ready status for CPE + * @core: handle to the core + * @value: flag/bitmask that is to be cleared + * + * This function should not be invoked with ssr_lock acquired + */ +static void wcd_cpe_clr_ready_status(struct wcd_cpe_core *core, + u8 value) +{ + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + core->ready_status &= ~(value); + dev_dbg(core->dev, + "%s: ready_status = 0x%x\n", + __func__, core->ready_status); + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); +} + +/* + * wcd_cpe_set_and_complete: + * Set the ready status with the provided value and + * flag the completion object if ready status moves + * to ready to download + * @core: handle to the core + * @value: flag/bitmask that is to be set + */ +static void wcd_cpe_set_and_complete(struct wcd_cpe_core *core, + u8 value) +{ + WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR"); + core->ready_status |= value; + if ((core->ready_status & WCD_CPE_READY_TO_DLOAD) == + WCD_CPE_READY_TO_DLOAD) { + dev_dbg(core->dev, + "%s: marking ready, status = 0x%x\n", + __func__, core->ready_status); + complete(&core->ready_compl); + } + WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR"); +} + + +/* + * wcd_cpe_ssr_work: work function to handle CPE SSR + * @work: work that is scheduled to perform CPE shutdown + * and restart + */ +static void wcd_cpe_ssr_work(struct work_struct *work) +{ + + int rc = 0; + u32 irq = 0; + struct wcd_cpe_core *core = NULL; + u8 status = 0; + + core = container_of(work, struct wcd_cpe_core, ssr_work); + if (!core) { + pr_err("%s: Core handle NULL\n", __func__); + return; + } + + /* Obtain pm request up in case of suspend mode */ + pm_qos_add_request(&core->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + PM_QOS_DEFAULT_VALUE); + pm_qos_update_request(&core->pm_qos_req, + msm_cpuidle_get_deep_idle_latency()); + + dev_dbg(core->dev, + "%s: CPE SSR with event %d\n", + __func__, core->ssr_type); + + if (core->ssr_type == WCD_CPE_SSR_EVENT) { + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_STATUS, + &status); + if (status & core->irq_info.cpe_fatal_irqs) + irq = CPE_IRQ_WDOG_BITE; + } else { + /* If bus is down, cdc reg cannot be read */ + irq = CPE_IRQ_WDOG_BITE; + } + + if (core->cpe_users > 0) { + rc = cpe_svc_process_irq(core->cpe_handle, irq); + if (rc < 0) + /* + * Even if process_irq fails, + * wait for cpe to move to offline state + */ + dev_err(core->dev, + "%s: irq processing failed, error = %d\n", + __func__, rc); + + rc = wait_for_completion_timeout(&core->offline_compl, + CPE_OFFLINE_WAIT_TIMEOUT); + if (!rc) { + dev_err(core->dev, + "%s: wait for cpe offline timed out\n", + __func__); + goto err_ret; + } + if (core->ssr_type != WCD_CPE_BUS_DOWN_EVENT) { + wcd_cpe_get_sfr_dump(core); + + /* + * Ramdump has to be explicitly enabled + * through debugfs and cannot be collected + * when bus is down. + */ + if (ramdump_enable) + wcd_cpe_collect_ramdump(core); + } + } else { + pr_err("%s: no cpe users, mark as offline\n", __func__); + wcd_cpe_change_online_state(core, 0); + wcd_cpe_set_and_complete(core, + WCD_CPE_BLK_READY); + } + + rc = wait_for_completion_timeout(&core->ready_compl, + CPE_READY_WAIT_TIMEOUT); + if (!rc) { + dev_err(core->dev, + "%s: ready to online timed out, status = %u\n", + __func__, core->ready_status); + goto err_ret; + } + + rc = wcd_cpe_boot_ssr(core); + + /* Once image are downloaded make sure all + * error interrupts are cleared + */ + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_CLEAR, NULL); + +err_ret: + /* remove after default pm qos */ + pm_qos_update_request(&core->pm_qos_req, + PM_QOS_DEFAULT_VALUE); + pm_qos_remove_request(&core->pm_qos_req); +} + +/* + * wcd_cpe_ssr_handle: handle SSR events here. + * @core_handle: handle to the cpe core + * @event: indicates ADSP or CDSP SSR. + */ +int wcd_cpe_ssr_event(void *core_handle, + enum wcd_cpe_ssr_state_event event) +{ + struct wcd_cpe_core *core = core_handle; + + if (!core) { + pr_err("%s: Invalid handle to core\n", + __func__); + return -EINVAL; + } + + /* + * If CPE is not even enabled, the SSR event for + * CPE needs to be ignored + */ + if (core->ssr_type == WCD_CPE_INITIALIZED) { + dev_info(core->dev, + "%s: CPE initialized but not enabled, skip CPE ssr\n", + __func__); + return 0; + } + + dev_dbg(core->dev, + "%s: Schedule ssr work, event = %d\n", + __func__, core->ssr_type); + + switch (event) { + case WCD_CPE_BUS_DOWN_EVENT: + /* + * If bus down, then CPE block is also + * treated to be down + */ + wcd_cpe_clr_ready_status(core, WCD_CPE_READY_TO_DLOAD); + core->ssr_type = event; + schedule_work(&core->ssr_work); + break; + + case WCD_CPE_SSR_EVENT: + wcd_cpe_clr_ready_status(core, WCD_CPE_BLK_READY); + core->ssr_type = event; + schedule_work(&core->ssr_work); + break; + + case WCD_CPE_BUS_UP_EVENT: + wcd_cpe_set_and_complete(core, WCD_CPE_BUS_READY); + /* + * In case of bus up event ssr_type will be changed + * to WCD_CPE_ACTIVE once CPE is online + */ + break; + + default: + dev_err(core->dev, + "%s: unhandled SSR event %d\n", + __func__, event); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(wcd_cpe_ssr_event); + +/* + * svass_exception_irq: threaded irq handler for sva error interrupts + * @irq: interrupt number + * @data: data pointer passed during irq registration + * + * Once a error interrupt is received, it is not cleared, since + * clearing this interrupt will raise spurious interrupts unless + * CPE is reset. + */ +static irqreturn_t svass_exception_irq(int irq, void *data) +{ + struct wcd_cpe_core *core = data; + u8 status = 0; + + if (!core || !CPE_ERR_IRQ_CB(core)) { + pr_err("%s: Invalid %s\n", + __func__, + (!core) ? "core" : "cdc control"); + return IRQ_HANDLED; + } + + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_STATUS, &status); + + while (status != 0) { + if (status & core->irq_info.cpe_fatal_irqs) { + dev_err(core->dev, + "%s: CPE SSR event,err_status = 0x%02x\n", + __func__, status); + wcd_cpe_ssr_event(core, WCD_CPE_SSR_EVENT); + /* + * If fatal interrupt is received, + * trigger SSR and stop processing + * further interrupts + */ + break; + } + /* + * Mask the interrupt that was raised to + * avoid spurious interrupts + */ + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_MASK, &status); + + /* Clear only the interrupt that was raised */ + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_CLEAR, &status); + dev_err(core->dev, + "%s: err_interrupt status = 0x%x\n", + __func__, status); + + /* Read status for pending interrupts */ + core->cpe_cdc_cb->cpe_err_irq_control(core->codec, + CPE_ERR_IRQ_STATUS, &status); + } + + return IRQ_HANDLED; +} + +/* + * wcd_cpe_cmi_afe_cb: callback called on response to afe commands + * @param: parameter containing the response code, etc + * + * Process the request to the command sent to CPE and wakeup the + * command send wait. + */ +static void wcd_cpe_cmi_afe_cb(const struct cmi_api_notification *param) +{ + struct cmi_hdr *hdr; + struct wcd_cmi_afe_port_data *afe_port_d; + u8 port_id; + + if (!param) { + pr_err("%s: param is null\n", __func__); + return; + } + + if (param->event != CMI_API_MSG) { + pr_err("%s: unhandled event 0x%x\n", + __func__, param->event); + return; + } + + pr_debug("%s: param->result = %d\n", + __func__, param->result); + + hdr = (struct cmi_hdr *) param->message; + + /* + * for AFE cmd response, port id is + * stored at session id field of header + */ + port_id = CMI_HDR_GET_SESSION_ID(hdr); + if (port_id > WCD_CPE_AFE_MAX_PORTS) { + pr_err("%s: invalid port_id %d\n", + __func__, port_id); + return; + } + + afe_port_d = &(afe_ports[port_id]); + + if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) { + + u8 *payload = ((u8 *)param->message) + (sizeof(struct cmi_hdr)); + u8 result = payload[0]; + + afe_port_d->cmd_result = result; + complete(&afe_port_d->afe_cmd_complete); + + } else if (hdr->opcode == CPE_AFE_PORT_CMDRSP_SHARED_MEM_ALLOC) { + + struct cpe_cmdrsp_shmem_alloc *cmdrsp_shmem_alloc = + (struct cpe_cmdrsp_shmem_alloc *) param->message; + + if (cmdrsp_shmem_alloc->addr == 0) { + pr_err("%s: Failed AFE shared mem alloc\n", __func__); + afe_port_d->cmd_result = CMI_SHMEM_ALLOC_FAILED; + } else { + pr_debug("%s AFE shared mem addr = 0x%x\n", + __func__, cmdrsp_shmem_alloc->addr); + afe_port_d->mem_handle = cmdrsp_shmem_alloc->addr; + afe_port_d->cmd_result = 0; + } + complete(&afe_port_d->afe_cmd_complete); + } +} + +/* + * wcd_cpe_initialize_afe_port_data: Initialize all AFE ports + * + * Initialize the data for all the afe ports. Assign the + * afe port state to INIT state. + */ +static void wcd_cpe_initialize_afe_port_data(void) +{ + struct wcd_cmi_afe_port_data *afe_port_d; + int i; + + for (i = 0; i <= WCD_CPE_AFE_MAX_PORTS; i++) { + afe_port_d = &afe_ports[i]; + afe_port_d->port_id = i; + init_completion(&afe_port_d->afe_cmd_complete); + afe_port_d->port_state = AFE_PORT_STATE_INIT; + mutex_init(&afe_port_d->afe_lock); + } +} + +/* + * wcd_cpe_deinitialize_afe_port_data: De-initialize all AFE ports + * + * De-Initialize the data for all the afe ports. Assign the + * afe port state to DEINIT state. + */ +static void wcd_cpe_deinitialize_afe_port_data(void) +{ + struct wcd_cmi_afe_port_data *afe_port_d; + int i; + + for (i = 0; i <= WCD_CPE_AFE_MAX_PORTS; i++) { + afe_port_d = &afe_ports[i]; + afe_port_d->port_state = AFE_PORT_STATE_DEINIT; + mutex_destroy(&afe_port_d->afe_lock); + } +} + +/* + * wcd_cpe_svc_event_cb: callback from cpe services, indicating + * CPE is online or offline. + * @param: parameter / payload for event to be notified + */ +static void wcd_cpe_svc_event_cb(const struct cpe_svc_notification *param) +{ + struct snd_soc_codec *codec; + struct wcd_cpe_core *core; + struct cpe_svc_boot_event *boot_data; + bool active_sessions; + + if (!param) { + pr_err("%s: Invalid event\n", __func__); + return; + } + + codec = param->private_data; + if (!codec) { + pr_err("%s: Invalid handle to codec\n", + __func__); + return; + } + + core = wcd_cpe_get_core_handle(codec); + if (!core) { + pr_err("%s: Invalid handle to core\n", + __func__); + return; + } + + dev_dbg(core->dev, + "%s: event = 0x%x, ssr_type = 0x%x\n", + __func__, param->event, core->ssr_type); + + switch (param->event) { + case CPE_SVC_BOOT: + boot_data = (struct cpe_svc_boot_event *) + param->payload; + core->sfr_buf_addr = boot_data->debug_address; + core->sfr_buf_size = boot_data->debug_buffer_size; + dev_dbg(core->dev, + "%s: CPE booted, sfr_addr = %d, sfr_size = %zu\n", + __func__, core->sfr_buf_addr, + core->sfr_buf_size); + break; + case CPE_SVC_ONLINE: + core->ssr_type = WCD_CPE_ACTIVE; + dev_dbg(core->dev, "%s CPE is now online\n", + __func__); + complete(&core->online_compl); + break; + case CPE_SVC_OFFLINE: + /* + * offline can happen during normal shutdown, + * but we are interested in offline only during + * SSR. + */ + if (core->ssr_type != WCD_CPE_SSR_EVENT && + core->ssr_type != WCD_CPE_BUS_DOWN_EVENT) + break; + + active_sessions = wcd_cpe_lsm_session_active(); + wcd_cpe_change_online_state(core, 0); + complete(&core->offline_compl); + dev_err(core->dev, "%s: CPE is now offline\n", + __func__); + break; + case CPE_SVC_CMI_CLIENTS_DEREG: + + /* + * Only when either CPE SSR is in progress, + * or the bus is down, we need to mark the CPE + * as ready. In all other cases, this event is + * ignored + */ + if (core->ssr_type == WCD_CPE_SSR_EVENT || + core->ssr_type == WCD_CPE_BUS_DOWN_EVENT) + wcd_cpe_set_and_complete(core, + WCD_CPE_BLK_READY); + break; + default: + dev_err(core->dev, + "%s: unhandled notification\n", + __func__); + break; + } +} + +/* + * wcd_cpe_cleanup_irqs: free the irq resources required by cpe + * @core: handle the cpe core + * + * This API will free the IRQs for CPE but does not mask the + * CPE interrupts. If masking is needed, it has to be done + * explicity by caller. + */ +static void wcd_cpe_cleanup_irqs(struct wcd_cpe_core *core) +{ + + struct snd_soc_codec *codec = core->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + + wcd9xxx_free_irq(core_res, + core->irq_info.cpe_engine_irq, + core); + wcd9xxx_free_irq(core_res, + core->irq_info.cpe_err_irq, + core); + +} + +/* + * wcd_cpe_setup_sva_err_intr: setup the irqs for CPE + * @core: handle to wcd_cpe_core + * All interrupts needed for CPE are acquired. If any + * request_irq fails, then all irqs are free'd + */ +static int wcd_cpe_setup_irqs(struct wcd_cpe_core *core) +{ + int ret; + struct snd_soc_codec *codec = core->codec; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res; + + ret = wcd9xxx_request_irq(core_res, + core->irq_info.cpe_engine_irq, + svass_engine_irq, "SVASS_Engine", core); + if (ret) { + dev_err(core->dev, + "%s: Failed to request svass engine irq\n", + __func__); + goto fail_engine_irq; + } + + /* Make sure all error interrupts are cleared */ + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_CLEAR, + NULL); + + /* Enable required error interrupts */ + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_UNMASK, + NULL); + + ret = wcd9xxx_request_irq(core_res, + core->irq_info.cpe_err_irq, + svass_exception_irq, "SVASS_Exception", core); + if (ret) { + dev_err(core->dev, + "%s: Failed to request svass err irq\n", + __func__); + goto fail_exception_irq; + } + + return 0; + +fail_exception_irq: + wcd9xxx_free_irq(core_res, + core->irq_info.cpe_engine_irq, core); + +fail_engine_irq: + return ret; +} + +static int wcd_cpe_get_cal_index(int32_t cal_type) +{ + int cal_index = -EINVAL; + + if (cal_type == ULP_AFE_CAL_TYPE) + cal_index = WCD_CPE_LSM_CAL_AFE; + else if (cal_type == ULP_LSM_CAL_TYPE) + cal_index = WCD_CPE_LSM_CAL_LSM; + else if (cal_type == ULP_LSM_TOPOLOGY_ID_CAL_TYPE) + cal_index = WCD_CPE_LSM_CAL_TOPOLOGY_ID; + else + pr_err("%s: invalid cal_type %d\n", + __func__, cal_type); + + return cal_index; +} + +static int wcd_cpe_alloc_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = wcd_cpe_get_cal_index(cal_type); + if (cal_index < 0) { + pr_err("%s: invalid caltype %d\n", + __func__, cal_type); + return -EINVAL; + } + + ret = cal_utils_alloc_cal(data_size, data, + core_d->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); + return ret; +} + +static int wcd_cpe_dealloc_cal(int32_t cal_type, size_t data_size, + void *data) +{ + int ret = 0; + int cal_index; + + cal_index = wcd_cpe_get_cal_index(cal_type); + if (cal_index < 0) { + pr_err("%s: invalid caltype %d\n", + __func__, cal_type); + return -EINVAL; + } + + ret = cal_utils_dealloc_cal(data_size, data, + core_d->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); + return ret; +} + +static int wcd_cpe_set_cal(int32_t cal_type, size_t data_size, void *data) +{ + int ret = 0; + int cal_index; + + cal_index = wcd_cpe_get_cal_index(cal_type); + if (cal_index < 0) { + pr_err("%s: invalid caltype %d\n", + __func__, cal_type); + return -EINVAL; + } + + ret = cal_utils_set_cal(data_size, data, + core_d->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); + return ret; +} + +static int wcd_cpe_cal_init(struct wcd_cpe_core *core) +{ + int ret = 0; + + struct cal_type_info cal_type_info[] = { + {{ULP_AFE_CAL_TYPE, + {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL, + wcd_cpe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ULP_LSM_CAL_TYPE, + {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL, + wcd_cpe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + + {{ULP_LSM_TOPOLOGY_ID_CAL_TYPE, + {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL, + wcd_cpe_set_cal, NULL, NULL} }, + {NULL, NULL, cal_utils_match_buf_num} }, + }; + + ret = cal_utils_create_cal_types(WCD_CPE_LSM_CAL_MAX, + core->cal_data, + cal_type_info); + if (ret < 0) + pr_err("%s: could not create cal type!\n", + __func__); + return ret; +} + +/* + * wcd_cpe_enable: setup the cpe interrupts and schedule + * the work to download image and bootup the CPE. + * core: handle to cpe core structure + */ +static int wcd_cpe_vote(struct wcd_cpe_core *core, + bool enable) +{ + int ret = 0; + + if (!core) { + pr_err("%s: Invalid handle to core\n", + __func__); + ret = -EINVAL; + goto done; + } + + dev_dbg(core->dev, + "%s: enter, enable = %s, cpe_users = %u\n", + __func__, (enable ? "true" : "false"), + core->cpe_users); + + if (enable) { + core->cpe_users++; + if (core->cpe_users == 1) { + ret = wcd_cpe_enable(core, enable); + if (ret) { + dev_err(core->dev, + "%s: CPE enable failed, err = %d\n", + __func__, ret); + goto done; + } + } else { + dev_dbg(core->dev, + "%s: cpe already enabled, users = %u\n", + __func__, core->cpe_users); + goto done; + } + } else { + core->cpe_users--; + if (core->cpe_users == 0) { + ret = wcd_cpe_enable(core, enable); + if (ret) { + dev_err(core->dev, + "%s: CPE disable failed, err = %d\n", + __func__, ret); + goto done; + } + } else { + dev_dbg(core->dev, + "%s: %u valid users on cpe\n", + __func__, core->cpe_users); + goto done; + } + } + + dev_dbg(core->dev, + "%s: leave, enable = %s, cpe_users = %u\n", + __func__, (enable ? "true" : "false"), + core->cpe_users); + +done: + return ret; +} + +static int wcd_cpe_debugfs_init(struct wcd_cpe_core *core) +{ + int rc = 0; + + struct dentry *dir = debugfs_create_dir("wcd_cpe", NULL); + + if (IS_ERR_OR_NULL(dir)) { + dir = NULL; + rc = -ENODEV; + goto err_create_dir; + } + + if (!debugfs_create_u32("ramdump_enable", 0644, + dir, &ramdump_enable)) { + dev_err(core->dev, "%s: Failed to create debugfs node %s\n", + __func__, "ramdump_enable"); + rc = -ENODEV; + goto err_create_entry; + } + + if (!debugfs_create_file("cpe_ftm_test_trigger", 0200, + dir, core, &cpe_ftm_test_trigger_fops)) { + dev_err(core->dev, "%s: Failed to create debugfs node %s\n", + __func__, "cpe_ftm_test_trigger"); + rc = -ENODEV; + goto err_create_entry; + } + + if (!debugfs_create_u32("cpe_ftm_test_status", 0444, + dir, &cpe_ftm_test_status)) { + dev_err(core->dev, "%s: Failed to create debugfs node %s\n", + __func__, "cpe_ftm_test_status"); + rc = -ENODEV; + goto err_create_entry; + } + +err_create_entry: + debugfs_remove(dir); + +err_create_dir: + return rc; +} + +static ssize_t fw_name_show(struct wcd_cpe_core *core, char *buf) +{ + return snprintf(buf, WCD_CPE_IMAGE_FNAME_MAX, "%s", + core->dyn_fname); +} + +static ssize_t fw_name_store(struct wcd_cpe_core *core, + const char *buf, ssize_t count) +{ + int copy_count = count; + const char *pos; + + pos = memchr(buf, '\n', count); + if (pos) + copy_count = pos - buf; + + if (copy_count > (WCD_CPE_IMAGE_FNAME_MAX - 1)) { + dev_err(core->dev, + "%s: Invalid length %d, max allowed %d\n", + __func__, copy_count, WCD_CPE_IMAGE_FNAME_MAX - 1); + return -EINVAL; + } + + strlcpy(core->dyn_fname, buf, copy_count + 1); + + return count; +} + +WCD_CPE_ATTR(fw_name, 0660, fw_name_show, fw_name_store); + +static ssize_t wcd_cpe_sysfs_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct wcd_cpe_attribute *cpe_attr = to_wcd_cpe_attr(attr); + struct wcd_cpe_core *core = kobj_to_cpe_core(kobj); + ssize_t ret = -EINVAL; + + if (core && cpe_attr->show) + ret = cpe_attr->show(core, buf); + + return ret; +} + +static ssize_t wcd_cpe_sysfs_store(struct kobject *kobj, + struct attribute *attr, const char *buf, + size_t count) +{ + struct wcd_cpe_attribute *cpe_attr = to_wcd_cpe_attr(attr); + struct wcd_cpe_core *core = kobj_to_cpe_core(kobj); + ssize_t ret = -EINVAL; + + if (core && cpe_attr->store) + ret = cpe_attr->store(core, buf, count); + + return ret; +} + +static const struct sysfs_ops wcd_cpe_sysfs_ops = { + .show = wcd_cpe_sysfs_show, + .store = wcd_cpe_sysfs_store, +}; + +static struct kobj_type wcd_cpe_ktype = { + .sysfs_ops = &wcd_cpe_sysfs_ops, +}; + +static int wcd_cpe_sysfs_init(struct wcd_cpe_core *core, int id) +{ + char sysfs_dir_name[WCD_CPE_SYSFS_DIR_MAX_LENGTH]; + int rc = 0; + + snprintf(sysfs_dir_name, WCD_CPE_SYSFS_DIR_MAX_LENGTH, + "%s%d", "wcd_cpe", id); + + rc = kobject_init_and_add(&core->cpe_kobj, &wcd_cpe_ktype, + kernel_kobj, + sysfs_dir_name); + if (unlikely(rc)) { + dev_err(core->dev, + "%s: Failed to add kobject %s, err = %d\n", + __func__, sysfs_dir_name, rc); + goto done; + } + + rc = sysfs_create_file(&core->cpe_kobj, &cpe_attr_fw_name.attr); + if (rc) { + dev_err(core->dev, + "%s: Failed to fw_name sysfs entry to %s\n", + __func__, sysfs_dir_name); + goto fail_create_file; + } + + return 0; + +fail_create_file: + kobject_put(&core->cpe_kobj); +done: + return rc; +} + +static ssize_t cpe_ftm_test_trigger(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct wcd_cpe_core *core = file->private_data; + int ret = 0; + + /* Enable the clks for cpe */ + ret = wcd_cpe_enable_cpe_clks(core, true); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE clk enable failed, err = %d\n", + __func__, ret); + goto done; + } + + /* Get the CPE_STATUS */ + ret = cpe_svc_ftm_test(core->cpe_handle, &cpe_ftm_test_status); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE FTM test failed, err = %d\n", + __func__, ret); + if (ret == CPE_SVC_BUSY) { + cpe_ftm_test_status = 1; + ret = 0; + } + } + + /* Disable the clks for cpe */ + ret = wcd_cpe_enable_cpe_clks(core, false); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE clk disable failed, err = %d\n", + __func__, ret); + } + +done: + if (ret < 0) + return ret; + else + return count; +} + +static int wcd_cpe_validate_params( + struct snd_soc_codec *codec, + struct wcd_cpe_params *params) +{ + + if (!codec) { + pr_err("%s: Invalid codec\n", __func__); + return -EINVAL; + } + + if (!params) { + dev_err(codec->dev, + "%s: No params supplied for codec %s\n", + __func__, codec->component.name); + return -EINVAL; + } + + if (!params->codec || !params->get_cpe_core || + !params->cdc_cb) { + dev_err(codec->dev, + "%s: Invalid params for codec %s\n", + __func__, codec->component.name); + return -EINVAL; + } + + return 0; +} + +/* + * wcd_cpe_init: Initialize CPE related structures + * @img_fname: filename for firmware image + * @codec: handle to codec requesting for image download + * @params: parameter structure passed from caller + * + * This API will initialize the cpe core but will not + * download the image or boot the cpe core. + */ +struct wcd_cpe_core *wcd_cpe_init(const char *img_fname, + struct snd_soc_codec *codec, + struct wcd_cpe_params *params) +{ + struct wcd_cpe_core *core; + int ret = 0; + struct snd_card *card = NULL; + struct snd_info_entry *entry = NULL; + char proc_name[WCD_CPE_STATE_MAX_LEN]; + const char *cpe_name = "cpe"; + const char *state_name = "_state"; + const struct cpe_svc_hw_cfg *hw_info; + int id = 0; + + if (wcd_cpe_validate_params(codec, params)) + return NULL; + + core = kzalloc(sizeof(struct wcd_cpe_core), GFP_KERNEL); + if (!core) + return NULL; + + snprintf(core->fname, sizeof(core->fname), "%s", img_fname); + strlcpy(core->dyn_fname, core->fname, WCD_CPE_IMAGE_FNAME_MAX); + + wcd_get_cpe_core = params->get_cpe_core; + + core->codec = params->codec; + core->dev = params->codec->dev; + core->cpe_debug_mode = params->dbg_mode; + + core->cdc_info.major_version = params->cdc_major_ver; + core->cdc_info.minor_version = params->cdc_minor_ver; + core->cdc_info.id = params->cdc_id; + + core->cpe_cdc_cb = params->cdc_cb; + + memcpy(&core->irq_info, ¶ms->cdc_irq_info, + sizeof(core->irq_info)); + + INIT_WORK(&core->load_fw_work, wcd_cpe_load_fw_image); + INIT_WORK(&core->ssr_work, wcd_cpe_ssr_work); + init_completion(&core->offline_compl); + init_completion(&core->ready_compl); + init_completion(&core->online_compl); + init_waitqueue_head(&core->ssr_entry.offline_poll_wait); + mutex_init(&core->ssr_lock); + core->cpe_users = 0; + core->cpe_clk_ref = 0; + + /* + * By default, during probe, it is assumed that + * both CPE hardware block and underlying bus to codec + * are ready + */ + core->ready_status = WCD_CPE_READY_TO_DLOAD; + + core->cpe_handle = cpe_svc_initialize(NULL, &core->cdc_info, + params->cpe_svc_params); + if (!core->cpe_handle) { + dev_err(core->dev, + "%s: failed to initialize cpe services\n", + __func__); + goto fail_cpe_initialize; + } + + core->cpe_reg_handle = cpe_svc_register(core->cpe_handle, + wcd_cpe_svc_event_cb, + CPE_SVC_ONLINE | CPE_SVC_OFFLINE | + CPE_SVC_BOOT | + CPE_SVC_CMI_CLIENTS_DEREG, + "codec cpe handler"); + if (!core->cpe_reg_handle) { + dev_err(core->dev, + "%s: failed to register cpe service\n", + __func__); + goto fail_cpe_register; + } + + card = codec->component.card->snd_card; + snprintf(proc_name, (sizeof("cpe") + sizeof("_state") + + sizeof(id) - 2), "%s%d%s", cpe_name, id, state_name); + entry = snd_info_create_card_entry(card, proc_name, + card->proc_root); + if (entry) { + core->ssr_entry.entry = entry; + core->ssr_entry.offline = 1; + entry->size = WCD_CPE_STATE_MAX_LEN; + entry->content = SNDRV_INFO_CONTENT_DATA; + entry->c.ops = &wcd_cpe_state_proc_ops; + entry->private_data = core; + ret = snd_info_register(entry); + if (ret < 0) { + dev_err(core->dev, + "%s: snd_info_register failed (%d)\n", + __func__, ret); + snd_info_free_entry(entry); + entry = NULL; + } + } else { + dev_err(core->dev, + "%s: Failed to create CPE SSR status entry\n", + __func__); + /* + * Even if SSR entry creation fails, continue + * with image download + */ + } + + core_d = core; + ret = wcd_cpe_cal_init(core); + if (ret < 0) { + dev_err(core->dev, + "%s: CPE calibration init failed, err = %d\n", + __func__, ret); + goto fail_cpe_reset; + } + + wcd_cpe_debugfs_init(core); + + wcd_cpe_sysfs_init(core, id); + + hw_info = cpe_svc_get_hw_cfg(core->cpe_handle); + if (!hw_info) { + dev_err(core->dev, + "%s: hw info not available\n", + __func__); + goto schedule_dload_work; + } else { + core->hw_info.dram_offset = hw_info->DRAM_offset; + core->hw_info.dram_size = hw_info->DRAM_size; + core->hw_info.iram_offset = hw_info->IRAM_offset; + core->hw_info.iram_size = hw_info->IRAM_size; + } + + /* Setup the ramdump device and buffer */ + core->cpe_ramdump_dev = create_ramdump_device("cpe", + core->dev); + if (!core->cpe_ramdump_dev) { + dev_err(core->dev, + "%s: Failed to create ramdump device\n", + __func__); + goto schedule_dload_work; + } + + arch_setup_dma_ops(core->dev, 0, 0, NULL, 0); + core->cpe_dump_v_addr = dma_alloc_coherent(core->dev, + core->hw_info.dram_size, + &core->cpe_dump_addr, + GFP_KERNEL); + if (!core->cpe_dump_v_addr) { + dev_err(core->dev, + "%s: Failed to alloc memory for cpe dump, size = %zd\n", + __func__, core->hw_info.dram_size); + goto schedule_dload_work; + } else { + memset(core->cpe_dump_v_addr, 0, core->hw_info.dram_size); + } + +schedule_dload_work: + core->ssr_type = WCD_CPE_INITIALIZED; + schedule_work(&core->load_fw_work); + return core; + +fail_cpe_reset: + cpe_svc_deregister(core->cpe_handle, core->cpe_reg_handle); + +fail_cpe_register: + cpe_svc_deinitialize(core->cpe_handle); + +fail_cpe_initialize: + kfree(core); + return NULL; +} +EXPORT_SYMBOL(wcd_cpe_init); + +/* + * wcd_cpe_cmi_lsm_callback: callback called from cpe services + * to notify command response for lsm + * service + * @param: param containing the response code and status + * + * This callback is registered with cpe services while registering + * the LSM service + */ +static void wcd_cpe_cmi_lsm_callback(const struct cmi_api_notification *param) +{ + struct cmi_hdr *hdr; + struct cpe_lsm_session *lsm_session; + u8 session_id; + + if (!param) { + pr_err("%s: param is null\n", __func__); + return; + } + + if (param->event != CMI_API_MSG) { + pr_err("%s: unhandled event 0x%x\n", __func__, param->event); + return; + } + + hdr = (struct cmi_hdr *) param->message; + session_id = CMI_HDR_GET_SESSION_ID(hdr); + + if (session_id > WCD_CPE_LSM_MAX_SESSIONS) { + pr_err("%s: invalid lsm session id = %d\n", + __func__, session_id); + return; + } + + lsm_session = lsm_sessions[session_id]; + + if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) { + + u8 *payload = ((u8 *)param->message) + (sizeof(struct cmi_hdr)); + u8 result = payload[0]; + + lsm_session->cmd_err_code = result; + complete(&lsm_session->cmd_comp); + + } else if (hdr->opcode == CPE_LSM_SESSION_CMDRSP_SHARED_MEM_ALLOC) { + + struct cpe_cmdrsp_shmem_alloc *cmdrsp_shmem_alloc = + (struct cpe_cmdrsp_shmem_alloc *) param->message; + + if (cmdrsp_shmem_alloc->addr == 0) { + pr_err("%s: Failed LSM shared mem alloc\n", __func__); + lsm_session->cmd_err_code = CMI_SHMEM_ALLOC_FAILED; + + } else { + + pr_debug("%s LSM shared mem addr = 0x%x\n", + __func__, cmdrsp_shmem_alloc->addr); + lsm_session->lsm_mem_handle = cmdrsp_shmem_alloc->addr; + lsm_session->cmd_err_code = 0; + } + + complete(&lsm_session->cmd_comp); + + } else if (hdr->opcode == CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2) { + + struct cpe_lsm_event_detect_v2 *event_detect_v2 = + (struct cpe_lsm_event_detect_v2 *) param->message; + + if (!lsm_session->priv_d) { + pr_err("%s: private data is not present\n", + __func__); + return; + } + + pr_debug("%s: event payload, status = %u, size = %u\n", + __func__, event_detect_v2->detection_status, + event_detect_v2->size); + + if (lsm_session->event_cb) + lsm_session->event_cb( + lsm_session->priv_d, + event_detect_v2->detection_status, + event_detect_v2->size, + event_detect_v2->payload); + } +} + +/* + * wcd_cpe_cmi_send_lsm_msg: send a message to lsm service + * @core: handle to cpe core + * @session: session on which to send the message + * @message: actual message containing header and payload + * + * Sends message to lsm service for specified session and wait + * for response back on the message. + * should be called after acquiring session specific mutex + */ +static int wcd_cpe_cmi_send_lsm_msg( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *message) +{ + int ret = 0; + struct cmi_hdr *hdr = message; + + pr_debug("%s: sending message with opcode 0x%x\n", + __func__, hdr->opcode); + + if (unlikely(!wcd_cpe_is_online_state(core))) { + dev_err(core->dev, + "%s: MSG not sent, CPE offline\n", + __func__); + goto done; + } + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, true); + + reinit_completion(&session->cmd_comp); + ret = cmi_send_msg(message); + if (ret) { + pr_err("%s: msg opcode (0x%x) send failed (%d)\n", + __func__, hdr->opcode, ret); + goto rel_bus_vote; + } + + ret = wait_for_completion_timeout(&session->cmd_comp, + CMI_CMD_TIMEOUT); + if (ret > 0) { + pr_debug("%s: command 0x%x, received response 0x%x\n", + __func__, hdr->opcode, session->cmd_err_code); + if (session->cmd_err_code == CMI_SHMEM_ALLOC_FAILED) + session->cmd_err_code = CPE_ENOMEMORY; + if (session->cmd_err_code > 0) + pr_err("%s: CPE returned error[%s]\n", + __func__, cpe_err_get_err_str( + session->cmd_err_code)); + ret = cpe_err_get_lnx_err_code(session->cmd_err_code); + goto rel_bus_vote; + } else { + pr_err("%s: command (0x%x) send timed out\n", + __func__, hdr->opcode); + ret = -ETIMEDOUT; + goto rel_bus_vote; + } + + +rel_bus_vote: + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, false); + +done: + return ret; +} + + +/* + * fill_cmi_header: fill the cmi header with specified values + * + * @hdr: header to be updated with values + * @session_id: session id of the header, + * in case of AFE service it is port_id + * @service_id: afe/lsm, etc + * @version: update the version field in header + * @payload_size: size of the payload following after header + * @opcode: opcode of the message + * @obm_flag: indicates if this header is for obm message + * + */ +static int fill_cmi_header(struct cmi_hdr *hdr, + u8 session_id, u8 service_id, + bool version, u8 payload_size, + u16 opcode, bool obm_flag) +{ + /* sanitize the data */ + if (!IS_VALID_SESSION_ID(session_id) || + !IS_VALID_SERVICE_ID(service_id) || + !IS_VALID_PLD_SIZE(payload_size)) { + pr_err("Invalid header creation request\n"); + return -EINVAL; + } + + CMI_HDR_SET_SESSION(hdr, session_id); + CMI_HDR_SET_SERVICE(hdr, service_id); + if (version) + CMI_HDR_SET_VERSION(hdr, 1); + else + CMI_HDR_SET_VERSION(hdr, 0); + + CMI_HDR_SET_PAYLOAD_SIZE(hdr, payload_size); + + hdr->opcode = opcode; + + if (obm_flag) + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_OUT_BAND); + else + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND); + + return 0; +} + +/* + * fill_lsm_cmd_header_v0_inband: + * Given the header, fill the header with information + * for lsm service, version 0 and inband message + * @hdr: the cmi header to be filled. + * @session_id: ID for the lsm session + * @payload_size: size for cmi message payload + * @opcode: opcode for cmi message + */ +static int fill_lsm_cmd_header_v0_inband(struct cmi_hdr *hdr, + u8 session_id, u8 payload_size, u16 opcode) +{ + return fill_cmi_header(hdr, session_id, + CMI_CPE_LSM_SERVICE_ID, false, + payload_size, opcode, false); +} + +/* + * wcd_cpe_is_valid_lsm_session: + * Check session parameters to identify validity for the sesion + * @core: handle to cpe core + * @session: handle to the lsm session + * @func: invoking function to be printed in error logs + */ +static int wcd_cpe_is_valid_lsm_session(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + const char *func) +{ + if (unlikely(IS_ERR_OR_NULL(core))) { + pr_err("%s: invalid handle to core\n", + func); + return -EINVAL; + } + + if (unlikely(IS_ERR_OR_NULL(session))) { + dev_err(core->dev, "%s: invalid session\n", + func); + return -EINVAL; + } + + if (session->id > WCD_CPE_LSM_MAX_SESSIONS) { + dev_err(core->dev, "%s: invalid session id (%u)\n", + func, session->id); + return -EINVAL; + } + + dev_dbg(core->dev, "%s: session_id = %u\n", + func, session->id); + return 0; +} + +static int wcd_cpe_cmd_lsm_open_tx_v2( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session) +{ + struct cpe_lsm_cmd_open_tx_v2 cmd_open_tx_v2; + struct cal_block_data *top_cal = NULL; + struct audio_cal_info_lsm_top *lsm_top; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + if (core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID] == NULL) { + dev_err(core->dev, + "%s: LSM_TOPOLOGY cal not allocated!\n", + __func__); + return -EINVAL; + } + + mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]->lock); + top_cal = cal_utils_get_only_cal_block( + core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]); + if (!top_cal) { + dev_err(core->dev, + "%s: Failed to get LSM TOPOLOGY cal block\n", + __func__); + ret = -EINVAL; + goto unlock_cal_mutex; + } + + lsm_top = (struct audio_cal_info_lsm_top *) + top_cal->cal_info; + + if (!lsm_top) { + dev_err(core->dev, + "%s: cal_info for LSM_TOPOLOGY not found\n", + __func__); + ret = -EINVAL; + goto unlock_cal_mutex; + } + + dev_dbg(core->dev, + "%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) { + dev_err(core->dev, + "%s: topology id not sent for app_type 0x%x\n", + __func__, lsm_top->app_type); + ret = -EINVAL; + goto unlock_cal_mutex; + } + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_open_tx_v2, 0, sizeof(struct cpe_lsm_cmd_open_tx_v2)); + if (fill_lsm_cmd_header_v0_inband(&cmd_open_tx_v2.hdr, + session->id, OPEN_V2_CMD_PAYLOAD_SIZE, + CPE_LSM_SESSION_CMD_OPEN_TX_V2)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_open_tx_v2.topology_id = lsm_top->topology; + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_open_tx_v2); + if (ret) + dev_err(core->dev, + "%s: failed to send open_tx_v2 cmd, err = %d\n", + __func__, ret); + else + session->is_topology_used = true; +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + +unlock_cal_mutex: + mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]->lock); + return ret; +} + +/* + * wcd_cpe_cmd_lsm_open_tx: compose and send lsm open command + * @core_handle: handle to cpe core + * @session: session for which the command needs to be sent + * @app_id: application id part of the command + * @sample_rate: sample rate for this session + */ +static int wcd_cpe_cmd_lsm_open_tx(void *core_handle, + struct cpe_lsm_session *session, + u16 app_id, u16 sample_rate) +{ + struct cpe_lsm_cmd_open_tx cmd_open_tx; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + /* Try to open with topology first */ + ret = wcd_cpe_cmd_lsm_open_tx_v2(core, session); + if (!ret) + goto done; + + dev_dbg(core->dev, "%s: Try open_tx without topology\n", + __func__); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_open_tx, 0, sizeof(struct cpe_lsm_cmd_open_tx)); + if (fill_lsm_cmd_header_v0_inband(&cmd_open_tx.hdr, + session->id, OPEN_CMD_PAYLOAD_SIZE, + CPE_LSM_SESSION_CMD_OPEN_TX)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_open_tx.app_id = app_id; + cmd_open_tx.sampling_rate = sample_rate; + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_open_tx); + if (ret) + dev_err(core->dev, + "%s: failed to send open_tx cmd, err = %d\n", + __func__, ret); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +done: + return ret; +} + +/* + * wcd_cpe_cmd_close_tx: compose and send lsm close command + * @core_handle: handle to cpe core + * @session: session for which the command needs to be sent + */ +static int wcd_cpe_cmd_lsm_close_tx(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_close_tx; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_close_tx, 0, sizeof(cmd_close_tx)); + if (fill_lsm_cmd_header_v0_inband(&cmd_close_tx, session->id, + 0, CPE_LSM_SESSION_CMD_CLOSE_TX)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_close_tx); + if (ret) + dev_err(core->dev, + "%s: lsm close_tx cmd failed, err = %d\n", + __func__, ret); + else + session->is_topology_used = false; +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_cmd_shmem_alloc: compose and send lsm shared + * memory allocation command + * @core_handle: handle to cpe core + * @session: session for which the command needs to be sent + * @size: size of memory to be allocated + */ +static int wcd_cpe_cmd_lsm_shmem_alloc(void *core_handle, + struct cpe_lsm_session *session, + u32 size) +{ + struct cpe_cmd_shmem_alloc cmd_shmem_alloc; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_shmem_alloc, 0, sizeof(cmd_shmem_alloc)); + if (fill_lsm_cmd_header_v0_inband(&cmd_shmem_alloc.hdr, session->id, + SHMEM_ALLOC_CMD_PLD_SIZE, + CPE_LSM_SESSION_CMD_SHARED_MEM_ALLOC)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_shmem_alloc.size = size; + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_shmem_alloc); + if (ret) + dev_err(core->dev, + "%s: lsm_shmem_alloc cmd send fail, %d\n", + __func__, ret); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_cmd_lsm_shmem_dealloc: deallocate the shared memory + * for the specified session + * @core_handle: handle to cpe core + * @session: session for which memory needs to be deallocated. + */ +static int wcd_cpe_cmd_lsm_shmem_dealloc(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cpe_cmd_shmem_dealloc cmd_dealloc; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_dealloc, 0, sizeof(cmd_dealloc)); + if (fill_lsm_cmd_header_v0_inband(&cmd_dealloc.hdr, session->id, + SHMEM_DEALLOC_CMD_PLD_SIZE, + CPE_LSM_SESSION_CMD_SHARED_MEM_DEALLOC)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_dealloc.addr = session->lsm_mem_handle; + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_dealloc); + if (ret) { + dev_err(core->dev, + "%s: lsm_shmem_dealloc cmd failed, rc %d\n", + __func__, ret); + goto end_ret; + } + + memset(&session->lsm_mem_handle, 0, + sizeof(session->lsm_mem_handle)); + +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_send_lsm_cal: send the calibration for lsm service + * from acdb to the cpe + * @core: handle to cpe core + * @session: session for which the calibration needs to be set. + */ +static int wcd_cpe_send_lsm_cal( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session) +{ + + u8 *msg_pld; + struct cmi_hdr *hdr; + struct cal_block_data *lsm_cal = NULL; + void *inb_msg; + int rc = 0; + + if (core->cal_data[WCD_CPE_LSM_CAL_LSM] == NULL) { + pr_err("%s: LSM cal not allocated!\n", __func__); + return -EINVAL; + } + + mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_LSM]->lock); + lsm_cal = cal_utils_get_only_cal_block( + core->cal_data[WCD_CPE_LSM_CAL_LSM]); + if (!lsm_cal) { + pr_err("%s: failed to get lsm cal block\n", __func__); + rc = -EINVAL; + goto unlock_cal_mutex; + } + + if (lsm_cal->cal_data.size == 0) { + dev_dbg(core->dev, "%s: No LSM cal to send\n", + __func__); + rc = 0; + goto unlock_cal_mutex; + } + + inb_msg = kzalloc(sizeof(struct cmi_hdr) + lsm_cal->cal_data.size, + GFP_KERNEL); + if (!inb_msg) { + rc = -ENOMEM; + goto unlock_cal_mutex; + } + + hdr = (struct cmi_hdr *) inb_msg; + + rc = fill_lsm_cmd_header_v0_inband(hdr, session->id, + lsm_cal->cal_data.size, + CPE_LSM_SESSION_CMD_SET_PARAMS); + if (rc) { + pr_err("%s: invalid params for header, err = %d\n", + __func__, rc); + goto free_msg; + } + + msg_pld = ((u8 *) inb_msg) + sizeof(struct cmi_hdr); + memcpy(msg_pld, lsm_cal->cal_data.kvaddr, + lsm_cal->cal_data.size); + + rc = wcd_cpe_cmi_send_lsm_msg(core, session, inb_msg); + if (rc) + pr_err("%s: acdb lsm_params send failed, err = %d\n", + __func__, rc); + +free_msg: + kfree(inb_msg); + +unlock_cal_mutex: + mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_LSM]->lock); + return rc; + +} + +static void wcd_cpe_set_param_data(struct cpe_param_data *param_d, + struct cpe_lsm_ids *ids, u32 p_size, + u32 set_param_cmd) +{ + param_d->module_id = ids->module_id; + param_d->param_id = ids->param_id; + + switch (set_param_cmd) { + case CPE_LSM_SESSION_CMD_SET_PARAMS_V2: + param_d->p_size.param_size = p_size; + break; + case CPE_LSM_SESSION_CMD_SET_PARAMS: + default: + param_d->p_size.sr.param_size = + (u16) p_size; + param_d->p_size.sr.reserved = 0; + break; + } +} + +static int wcd_cpe_send_param_epd_thres(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids) +{ + struct snd_lsm_ep_det_thres *ep_det_data; + struct cpe_lsm_param_epd_thres epd_cmd; + struct cmi_hdr *msg_hdr = &epd_cmd.hdr; + struct cpe_param_data *param_d = + &epd_cmd.param; + int rc; + + memset(&epd_cmd, 0, sizeof(epd_cmd)); + ep_det_data = (struct snd_lsm_ep_det_thres *) data; + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_EPD_THRES_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_EPD_THRES_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + epd_cmd.minor_version = 1; + epd_cmd.epd_begin = ep_det_data->epd_begin; + epd_cmd.epd_end = ep_det_data->epd_end; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &epd_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(EPD Threshold) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_opmode(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids) +{ + struct snd_lsm_detect_mode *opmode_d; + struct cpe_lsm_param_opmode opmode_cmd; + struct cmi_hdr *msg_hdr = &opmode_cmd.hdr; + struct cpe_param_data *param_d = + &opmode_cmd.param; + int rc; + + memset(&opmode_cmd, 0, sizeof(opmode_cmd)); + opmode_d = (struct snd_lsm_detect_mode *) data; + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_OPMODE_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_OPMODE_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + opmode_cmd.minor_version = 1; + if (opmode_d->mode == LSM_MODE_KEYWORD_ONLY_DETECTION) + opmode_cmd.mode = 1; + else + opmode_cmd.mode = 3; + + if (opmode_d->detect_failure) + opmode_cmd.mode |= 0x04; + + opmode_cmd.reserved = 0; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &opmode_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(operation_mode) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_gain(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids) +{ + struct snd_lsm_gain *gain_d; + struct cpe_lsm_param_gain gain_cmd; + struct cmi_hdr *msg_hdr = &gain_cmd.hdr; + struct cpe_param_data *param_d = + &gain_cmd.param; + int rc; + + memset(&gain_cmd, 0, sizeof(gain_cmd)); + gain_d = (struct snd_lsm_gain *) data; + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_GAIN_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_GAIN_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + gain_cmd.minor_version = 1; + gain_cmd.gain = gain_d->gain; + gain_cmd.reserved = 0; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &gain_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(lsm_gain) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_connectport(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, struct cpe_lsm_ids *ids, u16 port_id) +{ + struct cpe_lsm_param_connectport con_port_cmd; + struct cmi_hdr *msg_hdr = &con_port_cmd.hdr; + struct cpe_param_data *param_d = + &con_port_cmd.param; + int rc; + + memset(&con_port_cmd, 0, sizeof(con_port_cmd)); + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_CMD_CONNECTPORT_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + wcd_cpe_set_param_data(param_d, ids, + CPE_CONNECTPORT_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + con_port_cmd.minor_version = 1; + con_port_cmd.afe_port_id = port_id; + con_port_cmd.reserved = 0; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, &con_port_cmd); + if (unlikely(rc)) + dev_err(core->dev, + "%s: set_param(connect_port) failed, rc %dn", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + return rc; +} + +static int wcd_cpe_send_param_conf_levels( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + struct cpe_lsm_ids *ids) +{ + struct cpe_lsm_conf_level conf_level_data; + struct cmi_hdr *hdr = &(conf_level_data.hdr); + struct cpe_param_data *param_d = &(conf_level_data.param); + u8 pld_size = 0; + u8 pad_bytes = 0; + void *message; + int ret = 0; + + memset(&conf_level_data, 0, sizeof(conf_level_data)); + + pld_size = (sizeof(struct cpe_lsm_conf_level) - sizeof(struct cmi_hdr)); + pld_size += session->num_confidence_levels; + pad_bytes = ((4 - (pld_size % 4)) % 4); + pld_size += pad_bytes; + + fill_cmi_header(hdr, session->id, CMI_CPE_LSM_SERVICE_ID, + false, pld_size, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2, false); + + wcd_cpe_set_param_data(param_d, ids, + pld_size - sizeof(struct cpe_param_data), + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + conf_level_data.num_active_models = session->num_confidence_levels; + + message = kzalloc(sizeof(struct cpe_lsm_conf_level) + + conf_level_data.num_active_models + pad_bytes, + GFP_KERNEL); + if (!message) { + pr_err("%s: no memory for conf_level\n", __func__); + return -ENOMEM; + } + + memcpy(message, &conf_level_data, + sizeof(struct cpe_lsm_conf_level)); + memcpy(((u8 *) message) + sizeof(struct cpe_lsm_conf_level), + session->conf_levels, conf_level_data.num_active_models); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, message); + if (ret) + pr_err("%s: lsm_set_conf_levels failed, err = %d\n", + __func__, ret); + kfree(message); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +static int wcd_cpe_send_param_snd_model(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, struct cpe_lsm_ids *ids) +{ + int ret = 0; + struct cmi_obm_msg obm_msg; + struct cpe_param_data *param_d; + + + ret = fill_cmi_header(&obm_msg.hdr, session->id, + CMI_CPE_LSM_SERVICE_ID, 0, 20, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2, true); + if (ret) { + dev_err(core->dev, + "%s: Invalid parameters, rc = %d\n", + __func__, ret); + goto err_ret; + } + + obm_msg.pld.version = 0; + obm_msg.pld.size = session->snd_model_size; + obm_msg.pld.data_ptr.kvaddr = session->snd_model_data; + obm_msg.pld.mem_handle = session->lsm_mem_handle; + + param_d = (struct cpe_param_data *) session->snd_model_data; + wcd_cpe_set_param_data(param_d, ids, + (session->snd_model_size - sizeof(*param_d)), + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &obm_msg); + if (ret) + dev_err(core->dev, + "%s: snd_model_register failed, %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + +err_ret: + return ret; +} + +static int wcd_cpe_send_param_dereg_model( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + struct cpe_lsm_ids *ids) +{ + struct cmi_hdr *hdr; + struct cpe_param_data *param_d; + u8 *message; + u32 pld_size; + int rc = 0; + + pld_size = sizeof(*hdr) + sizeof(*param_d); + + message = kzalloc(pld_size, GFP_KERNEL); + if (!message) + return -ENOMEM; + + hdr = (struct cmi_hdr *) message; + param_d = (struct cpe_param_data *) + (((u8 *) message) + sizeof(*hdr)); + + if (fill_lsm_cmd_header_v0_inband(hdr, + session->id, + sizeof(*param_d), + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + wcd_cpe_set_param_data(param_d, ids, 0, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, message); + if (rc) + dev_err(core->dev, + "%s: snd_model_deregister failed, %d\n", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + kfree(message); + return rc; +} + +static int wcd_cpe_send_custom_param( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + void *data, u32 msg_size) +{ + u8 *msg; + struct cmi_hdr *hdr; + u8 *msg_pld; + int rc; + + if (msg_size > CMI_INBAND_MESSAGE_SIZE) { + dev_err(core->dev, + "%s: out of band custom params not supported\n", + __func__); + return -EINVAL; + } + + msg = kzalloc(sizeof(*hdr) + msg_size, GFP_KERNEL); + if (!msg) + return -ENOMEM; + + hdr = (struct cmi_hdr *) msg; + msg_pld = msg + sizeof(struct cmi_hdr); + + if (fill_lsm_cmd_header_v0_inband(hdr, + session->id, + msg_size, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + rc = -EINVAL; + goto err_ret; + } + + memcpy(msg_pld, data, msg_size); + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + rc = wcd_cpe_cmi_send_lsm_msg(core, session, msg); + if (rc) + dev_err(core->dev, + "%s: custom params send failed, err = %d\n", + __func__, rc); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +err_ret: + kfree(msg); + return rc; +} + +static int wcd_cpe_set_one_param(void *core_handle, + struct cpe_lsm_session *session, struct lsm_params_info *p_info, + void *data, uint32_t param_type) +{ + struct wcd_cpe_core *core = core_handle; + int rc = 0; + struct cpe_lsm_ids ids; + + memset(&ids, 0, sizeof(ids)); + ids.module_id = p_info->module_id; + ids.param_id = p_info->param_id; + + switch (param_type) { + case LSM_ENDPOINT_DETECT_THRESHOLD: + rc = wcd_cpe_send_param_epd_thres(core, session, + data, &ids); + break; + case LSM_OPERATION_MODE: + rc = wcd_cpe_send_param_opmode(core, session, data, &ids); + break; + case LSM_GAIN: + rc = wcd_cpe_send_param_gain(core, session, data, &ids); + break; + case LSM_MIN_CONFIDENCE_LEVELS: + rc = wcd_cpe_send_param_conf_levels(core, session, &ids); + break; + case LSM_REG_SND_MODEL: + rc = wcd_cpe_send_param_snd_model(core, session, &ids); + break; + case LSM_DEREG_SND_MODEL: + rc = wcd_cpe_send_param_dereg_model(core, session, &ids); + break; + case LSM_CUSTOM_PARAMS: + rc = wcd_cpe_send_custom_param(core, session, + data, p_info->param_size); + break; + default: + pr_err("%s: wrong param_type 0x%x\n", + __func__, param_type); + } + + if (rc) + dev_err(core->dev, + "%s: send_param(%d) failed, err %d\n", + __func__, param_type, rc); + return rc; +} + +/* + * wcd_cpe_lsm_set_params: set the parameters for lsm service + * @core: handle to cpe core + * @session: session for which the parameters are to be set + * @detect_mode: mode for detection + * @detect_failure: flag indicating failure detection enabled/disabled + * + */ +static int wcd_cpe_lsm_set_params(struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + enum lsm_detection_mode detect_mode, bool detect_failure) +{ + struct cpe_lsm_ids ids; + struct snd_lsm_detect_mode det_mode; + + int ret = 0; + + /* Send lsm calibration */ + ret = wcd_cpe_send_lsm_cal(core, session); + if (ret) { + pr_err("%s: fail to sent acdb cal, err = %d", + __func__, ret); + goto err_ret; + } + + /* Send operation mode */ + ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP; + ids.param_id = CPE_LSM_PARAM_ID_OPERATION_MODE; + det_mode.mode = detect_mode; + det_mode.detect_failure = detect_failure; + ret = wcd_cpe_send_param_opmode(core, session, + &det_mode, &ids); + if (ret) + dev_err(core->dev, + "%s: Failed to set opmode, err=%d\n", + __func__, ret); + +err_ret: + return ret; +} + +static int wcd_cpe_lsm_set_data(void *core_handle, + struct cpe_lsm_session *session, + enum lsm_detection_mode detect_mode, + bool detect_failure) +{ + struct wcd_cpe_core *core = core_handle; + struct cpe_lsm_ids ids; + int ret = 0; + + if (session->num_confidence_levels > 0) { + ret = wcd_cpe_lsm_set_params(core, session, detect_mode, + detect_failure); + if (ret) { + dev_err(core->dev, + "%s: lsm set params failed, rc = %d\n", + __func__, ret); + goto err_ret; + } + + ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP; + ids.param_id = CPE_LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS; + ret = wcd_cpe_send_param_conf_levels(core, session, &ids); + if (ret) { + dev_err(core->dev, + "%s: lsm confidence levels failed, rc = %d\n", + __func__, ret); + goto err_ret; + } + } else { + dev_dbg(core->dev, + "%s: no conf levels to set\n", + __func__); + } + +err_ret: + return ret; +} + +/* + * wcd_cpe_lsm_reg_snd_model: register the sound model for listen + * @session: session for which to register the sound model + * @detect_mode: detection mode, user dependent/independent + * @detect_failure: flag to indicate if failure detection is enabled + * + * The memory required for sound model should be pre-allocated on CPE + * before this function is invoked. + */ +static int wcd_cpe_lsm_reg_snd_model(void *core_handle, + struct cpe_lsm_session *session, + enum lsm_detection_mode detect_mode, + bool detect_failure) +{ + int ret = 0; + struct cmi_obm_msg obm_msg; + struct wcd_cpe_core *core = core_handle; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + ret = wcd_cpe_lsm_set_data(core_handle, session, + detect_mode, detect_failure); + if (ret) { + dev_err(core->dev, + "%s: fail to set lsm data, err = %d\n", + __func__, ret); + return ret; + } + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + ret = fill_cmi_header(&obm_msg.hdr, session->id, + CMI_CPE_LSM_SERVICE_ID, 0, 20, + CPE_LSM_SESSION_CMD_REGISTER_SOUND_MODEL, true); + if (ret) { + dev_err(core->dev, + "%s: Invalid parameters, rc = %d\n", + __func__, ret); + goto err_ret; + } + + obm_msg.pld.version = 0; + obm_msg.pld.size = session->snd_model_size; + obm_msg.pld.data_ptr.kvaddr = session->snd_model_data; + obm_msg.pld.mem_handle = session->lsm_mem_handle; + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &obm_msg); + if (ret) + dev_err(core->dev, + "%s: snd_model_register failed, %d\n", + __func__, ret); +err_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_lsm_dereg_snd_model: deregister the sound model for listen + * @core_handle: handle to cpe core + * @session: session for which to deregister the sound model + * + */ +static int wcd_cpe_lsm_dereg_snd_model(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_dereg_snd_model; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_dereg_snd_model, 0, sizeof(cmd_dereg_snd_model)); + if (fill_lsm_cmd_header_v0_inband(&cmd_dereg_snd_model, session->id, + 0, CPE_LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_dereg_snd_model); + if (ret) + dev_err(core->dev, + "%s: failed to send dereg_snd_model cmd\n", + __func__); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_lsm_get_afe_out_port_id: get afe output port id + * @core_handle: handle to the CPE core + * @session: session for which port id needs to get + */ +static int wcd_cpe_lsm_get_afe_out_port_id(void *core_handle, + struct cpe_lsm_session *session) +{ + struct wcd_cpe_core *core = core_handle; + struct snd_soc_codec *codec; + int rc = 0; + + if (!core || !core->codec) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!core) ? "core" : "codec"); + rc = -EINVAL; + goto done; + } + + if (!session) { + dev_err(core->dev, "%s: Invalid session\n", + __func__); + rc = -EINVAL; + goto done; + } + + if (!core->cpe_cdc_cb || + !core->cpe_cdc_cb->get_afe_out_port_id) { + session->afe_out_port_id = WCD_CPE_AFE_OUT_PORT_2; + dev_dbg(core->dev, + "%s: callback not defined, default port_id = %d\n", + __func__, session->afe_out_port_id); + goto done; + } + + codec = core->codec; + rc = core->cpe_cdc_cb->get_afe_out_port_id(codec, + &session->afe_out_port_id); + if (rc) { + dev_err(core->dev, + "%s: failed to get port id, err = %d\n", + __func__, rc); + goto done; + } + dev_dbg(core->dev, "%s: port_id: %d\n", __func__, + session->afe_out_port_id); + +done: + return rc; +} + +/* + * wcd_cpe_cmd_lsm_start: send the start command to lsm + * @core_handle: handle to the CPE core + * @session: session for which start command to be sent + * + */ +static int wcd_cpe_cmd_lsm_start(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_lsm_start; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_lsm_start, 0, sizeof(struct cmi_hdr)); + if (fill_lsm_cmd_header_v0_inband(&cmd_lsm_start, session->id, 0, + CPE_LSM_SESSION_CMD_START)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_lsm_start); + if (ret) + dev_err(core->dev, "failed to send lsm_start cmd\n"); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_cmd_lsm_stop: send the stop command for LSM service + * @core_handle: handle to the cpe core + * @session: session for which stop command to be sent + * + */ +static int wcd_cpe_cmd_lsm_stop(void *core_handle, + struct cpe_lsm_session *session) +{ + struct cmi_hdr cmd_lsm_stop; + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + ret = wcd_cpe_is_valid_lsm_session(core, session, + __func__); + if (ret) + return ret; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&cmd_lsm_stop, 0, sizeof(struct cmi_hdr)); + if (fill_lsm_cmd_header_v0_inband(&cmd_lsm_stop, session->id, 0, + CPE_LSM_SESSION_CMD_STOP)) { + ret = -EINVAL; + goto end_ret; + } + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_lsm_stop); + if (ret) + dev_err(core->dev, + "%s: failed to send lsm_stop cmd\n", + __func__); +end_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; + +} + +/* + * wcd_cpe_alloc_lsm_session: allocate a lsm session + * @core: handle to wcd_cpe_core + * @lsm_priv_d: lsm private data + */ +static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( + void *core_handle, void *client_data, + void (*event_cb)(void *, u8, u8, u8 *)) +{ + struct cpe_lsm_session *session; + int i, session_id = -1; + struct wcd_cpe_core *core = core_handle; + bool afe_register_service = false; + int ret = 0; + + /* + * Even if multiple listen sessions can be + * allocated, the AFE service registration + * should be done only once as CPE can only + * have one instance of AFE service. + * + * If this is the first session to be allocated, + * only then register the afe service. + */ + if (!wcd_cpe_lsm_session_active()) + afe_register_service = true; + + for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++) { + if (!lsm_sessions[i]) { + session_id = i; + break; + } + } + + if (session_id < 0) { + dev_err(core->dev, + "%s: max allowed sessions already allocated\n", + __func__); + return NULL; + } + + ret = wcd_cpe_vote(core, true); + if (ret) { + dev_err(core->dev, + "%s: Failed to enable cpe, err = %d\n", + __func__, ret); + return NULL; + } + + session = kzalloc(sizeof(struct cpe_lsm_session), GFP_KERNEL); + if (!session) + goto err_session_alloc; + + session->id = session_id; + session->event_cb = event_cb; + session->cmi_reg_handle = cmi_register(wcd_cpe_cmi_lsm_callback, + CMI_CPE_LSM_SERVICE_ID); + if (!session->cmi_reg_handle) { + dev_err(core->dev, + "%s: Failed to register LSM service with CMI\n", + __func__); + goto err_ret; + } + session->priv_d = client_data; + mutex_init(&session->lsm_lock); + if (afe_register_service) { + /* Register for AFE Service */ + core->cmi_afe_handle = cmi_register(wcd_cpe_cmi_afe_cb, + CMI_CPE_AFE_SERVICE_ID); + wcd_cpe_initialize_afe_port_data(); + if (!core->cmi_afe_handle) { + dev_err(core->dev, + "%s: Failed to register AFE service with CMI\n", + __func__); + goto err_afe_svc_reg; + } + + /* Once AFE service is registered, send the mode command */ + ret = wcd_cpe_afe_svc_cmd_mode(core, + AFE_SVC_EXPLICIT_PORT_START); + if (ret) + goto err_afe_mode_cmd; + } + + session->lsm_mem_handle = 0; + init_completion(&session->cmd_comp); + + lsm_sessions[session_id] = session; + return session; + +err_afe_mode_cmd: + cmi_deregister(core->cmi_afe_handle); + +err_afe_svc_reg: + cmi_deregister(session->cmi_reg_handle); + mutex_destroy(&session->lsm_lock); + +err_ret: + kfree(session); + +err_session_alloc: + wcd_cpe_vote(core, false); + return NULL; +} + +/* + * wcd_cpe_lsm_config_lab_latency: send lab latency value + * @core: handle to wcd_cpe_core + * @session: lsm session + * @latency: the value of latency for lab setup in msec + */ +static int wcd_cpe_lsm_config_lab_latency( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session, + u32 latency) +{ + int ret = 0, pld_size = CPE_PARAM_LSM_LAB_LATENCY_SIZE; + struct cpe_lsm_lab_latency_config cpe_lab_latency; + struct cpe_lsm_lab_config *lab_lat = &cpe_lab_latency.latency_cfg; + struct cpe_param_data *param_d = &lab_lat->param; + struct cpe_lsm_ids ids; + + if (fill_lsm_cmd_header_v0_inband(&cpe_lab_latency.hdr, session->id, + (u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + pr_err("%s: Failed to create header\n", __func__); + return -EINVAL; + } + if (latency == 0x00 || latency > WCD_CPE_LAB_MAX_LATENCY) { + pr_err("%s: Invalid latency %u\n", + __func__, latency); + return -EINVAL; + } + + lab_lat->latency = latency; + lab_lat->minor_ver = 1; + ids.module_id = CPE_LSM_MODULE_ID_LAB; + ids.param_id = CPE_LSM_PARAM_ID_LAB_CONFIG; + wcd_cpe_set_param_data(param_d, &ids, + PARAM_SIZE_LSM_LATENCY_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + pr_debug("%s: Module 0x%x Param 0x%x size %zu pld_size 0x%x\n", + __func__, lab_lat->param.module_id, + lab_lat->param.param_id, PARAM_SIZE_LSM_LATENCY_SIZE, + pld_size); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cpe_lab_latency); + if (ret != 0) + pr_err("%s: lsm_set_params failed, error = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + return ret; +} + +/* + * wcd_cpe_lsm_lab_control: enable/disable lab + * @core: handle to wcd_cpe_core + * @session: lsm session + * @enable: Indicates whether to enable / disable lab + */ +static int wcd_cpe_lsm_lab_control( + void *core_handle, + struct cpe_lsm_session *session, + bool enable) +{ + struct wcd_cpe_core *core = core_handle; + int ret = 0, pld_size = CPE_PARAM_SIZE_LSM_LAB_CONTROL; + struct cpe_lsm_control_lab cpe_lab_enable; + struct cpe_lsm_lab_enable *lab_enable = &cpe_lab_enable.lab_enable; + struct cpe_param_data *param_d = &lab_enable->param; + struct cpe_lsm_ids ids; + + pr_debug("%s: enter payload_size = %d Enable %d\n", + __func__, pld_size, enable); + + memset(&cpe_lab_enable, 0, sizeof(cpe_lab_enable)); + + if (fill_lsm_cmd_header_v0_inband(&cpe_lab_enable.hdr, session->id, + (u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + return -EINVAL; + } + if (enable == true) + lab_enable->enable = 1; + else + lab_enable->enable = 0; + + ids.module_id = CPE_LSM_MODULE_ID_LAB; + ids.param_id = CPE_LSM_PARAM_ID_LAB_ENABLE; + wcd_cpe_set_param_data(param_d, &ids, + PARAM_SIZE_LSM_CONTROL_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + pr_debug("%s: Module 0x%x, Param 0x%x size %zu pld_size 0x%x\n", + __func__, lab_enable->param.module_id, + lab_enable->param.param_id, PARAM_SIZE_LSM_CONTROL_SIZE, + pld_size); + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cpe_lab_enable); + if (ret != 0) { + pr_err("%s: lsm_set_params failed, error = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + goto done; + } + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + + if (lab_enable->enable) + ret = wcd_cpe_lsm_config_lab_latency(core, session, + WCD_CPE_LAB_MAX_LATENCY); +done: + return ret; +} + +/* + * wcd_cpe_lsm_eob: stop lab + * @core: handle to wcd_cpe_core + * @session: lsm session to be deallocated + */ +static int wcd_cpe_lsm_eob( + struct wcd_cpe_core *core, + struct cpe_lsm_session *session) +{ + int ret = 0; + struct cmi_hdr lab_eob; + + if (fill_lsm_cmd_header_v0_inband(&lab_eob, session->id, + 0, CPE_LSM_SESSION_CMD_EOB)) { + return -EINVAL; + } + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &lab_eob); + if (ret != 0) + pr_err("%s: lsm_set_params failed\n", __func__); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); + + return ret; +} + +/* + * wcd_cpe_dealloc_lsm_session: deallocate lsm session + * @core: handle to wcd_cpe_core + * @session: lsm session to be deallocated + */ +static int wcd_cpe_dealloc_lsm_session(void *core_handle, + struct cpe_lsm_session *session) +{ + struct wcd_cpe_core *core = core_handle; + int ret = 0; + + if (!session) { + dev_err(core->dev, + "%s: Invalid lsm session\n", __func__); + return -EINVAL; + } + + dev_dbg(core->dev, "%s: session %d being deallocated\n", + __func__, session->id); + if (session->id > WCD_CPE_LSM_MAX_SESSIONS) { + dev_err(core->dev, + "%s: Wrong session id %d max allowed = %d\n", + __func__, session->id, + WCD_CPE_LSM_MAX_SESSIONS); + return -EINVAL; + } + + cmi_deregister(session->cmi_reg_handle); + mutex_destroy(&session->lsm_lock); + lsm_sessions[session->id] = NULL; + kfree(session); + + if (!wcd_cpe_lsm_session_active()) { + cmi_deregister(core->cmi_afe_handle); + core->cmi_afe_handle = NULL; + wcd_cpe_deinitialize_afe_port_data(); + } + + ret = wcd_cpe_vote(core, false); + if (ret) + dev_dbg(core->dev, + "%s: Failed to un-vote cpe, err = %d\n", + __func__, ret); + + return ret; +} + +static int wcd_cpe_lab_ch_setup(void *core_handle, + struct cpe_lsm_session *session, + enum wcd_cpe_event event) +{ + struct wcd_cpe_core *core = core_handle; + struct snd_soc_codec *codec; + int rc = 0; + u8 cpe_intr_bits; + + if (!core || !core->codec) { + pr_err("%s: Invalid handle to %s\n", + __func__, + (!core) ? "core" : "codec"); + rc = EINVAL; + goto done; + } + + if (!core->cpe_cdc_cb || + !core->cpe_cdc_cb->cdc_ext_clk || + !core->cpe_cdc_cb->lab_cdc_ch_ctl) { + dev_err(core->dev, + "%s: Invalid codec callbacks\n", + __func__); + rc = -EINVAL; + goto done; + } + + codec = core->codec; + dev_dbg(core->dev, + "%s: event = 0x%x\n", + __func__, event); + + switch (event) { + case WCD_CPE_PRE_ENABLE: + rc = core->cpe_cdc_cb->cdc_ext_clk(codec, true, false); + if (rc) { + dev_err(core->dev, + "%s: failed to enable cdc clk, err = %d\n", + __func__, rc); + goto done; + } + + rc = core->cpe_cdc_cb->lab_cdc_ch_ctl(codec, + true); + if (rc) { + dev_err(core->dev, + "%s: failed to enable cdc port, err = %d\n", + __func__, rc); + rc = core->cpe_cdc_cb->cdc_ext_clk(codec, false, false); + goto done; + } + + break; + + case WCD_CPE_POST_ENABLE: + rc = cpe_svc_toggle_lab(core->cpe_handle, true); + if (rc) + dev_err(core->dev, + "%s: Failed to enable lab\n", __func__); + break; + + case WCD_CPE_PRE_DISABLE: + /* + * Mask the non-fatal interrupts in CPE as they will + * be generated during lab teardown and may flood. + */ + cpe_intr_bits = ~(core->irq_info.cpe_fatal_irqs & 0xFF); + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_MASK, + &cpe_intr_bits); + + rc = core->cpe_cdc_cb->lab_cdc_ch_ctl(codec, + false); + if (rc) + dev_err(core->dev, + "%s: failed to disable cdc port, err = %d\n", + __func__, rc); + break; + + case WCD_CPE_POST_DISABLE: + rc = wcd_cpe_lsm_eob(core, session); + if (rc) + dev_err(core->dev, + "%s: eob send failed, err = %d\n", + __func__, rc); + + /* Continue teardown even if eob failed */ + rc = cpe_svc_toggle_lab(core->cpe_handle, false); + if (rc) + dev_err(core->dev, + "%s: Failed to disable lab\n", __func__); + + /* Continue with disabling even if toggle lab fails */ + rc = core->cpe_cdc_cb->cdc_ext_clk(codec, false, false); + if (rc) + dev_err(core->dev, + "%s: failed to disable cdc clk, err = %d\n", + __func__, rc); + + /* Unmask non-fatal CPE interrupts */ + cpe_intr_bits = ~(core->irq_info.cpe_fatal_irqs & 0xFF); + if (CPE_ERR_IRQ_CB(core)) + core->cpe_cdc_cb->cpe_err_irq_control( + core->codec, + CPE_ERR_IRQ_UNMASK, + &cpe_intr_bits); + break; + + default: + dev_err(core->dev, + "%s: Invalid event 0x%x\n", + __func__, event); + rc = -EINVAL; + break; + } + +done: + return rc; +} + +static int wcd_cpe_lsm_set_fmt_cfg(void *core_handle, + struct cpe_lsm_session *session) +{ + int ret; + struct cpe_lsm_output_format_cfg out_fmt_cfg; + struct wcd_cpe_core *core = core_handle; + + ret = wcd_cpe_is_valid_lsm_session(core, session, __func__); + if (ret) + goto done; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + + memset(&out_fmt_cfg, 0, sizeof(out_fmt_cfg)); + if (fill_lsm_cmd_header_v0_inband(&out_fmt_cfg.hdr, + session->id, OUT_FMT_CFG_CMD_PAYLOAD_SIZE, + CPE_LSM_SESSION_CMD_TX_BUFF_OUTPUT_CONFIG)) { + ret = -EINVAL; + goto err_ret; + } + + out_fmt_cfg.format = session->out_fmt_cfg.format; + out_fmt_cfg.packing = session->out_fmt_cfg.pack_mode; + out_fmt_cfg.data_path_events = session->out_fmt_cfg.data_path_events; + + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &out_fmt_cfg); + if (ret) + dev_err(core->dev, + "%s: lsm_set_output_format_cfg failed, err = %d\n", + __func__, ret); + +err_ret: + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +done: + return ret; +} + +static void wcd_cpe_snd_model_offset(void *core_handle, + struct cpe_lsm_session *session, size_t *offset) +{ + *offset = sizeof(struct cpe_param_data); +} + +static int wcd_cpe_lsm_set_media_fmt_params(void *core_handle, + struct cpe_lsm_session *session, + struct lsm_hw_params *param) +{ + struct cpe_lsm_media_fmt_param media_fmt; + struct cmi_hdr *msg_hdr = &media_fmt.hdr; + struct wcd_cpe_core *core = core_handle; + struct cpe_param_data *param_d = &media_fmt.param; + struct cpe_lsm_ids ids; + int ret; + + memset(&media_fmt, 0, sizeof(media_fmt)); + if (fill_lsm_cmd_header_v0_inband(msg_hdr, + session->id, + CPE_MEDIA_FMT_PLD_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) { + ret = -EINVAL; + goto done; + } + + memset(&ids, 0, sizeof(ids)); + ids.module_id = CPE_LSM_MODULE_FRAMEWORK; + ids.param_id = CPE_LSM_PARAM_ID_MEDIA_FMT; + + wcd_cpe_set_param_data(param_d, &ids, CPE_MEDIA_FMT_PARAM_SIZE, + CPE_LSM_SESSION_CMD_SET_PARAMS_V2); + + media_fmt.minor_version = 1; + media_fmt.sample_rate = param->sample_rate; + media_fmt.num_channels = param->num_chs; + media_fmt.bit_width = param->bit_width; + + WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm"); + ret = wcd_cpe_cmi_send_lsm_msg(core, session, &media_fmt); + if (ret) + dev_err(core->dev, + "%s: Set_param(media_format) failed, err=%d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm"); +done: + return ret; +} + +static int wcd_cpe_lsm_set_port(void *core_handle, + struct cpe_lsm_session *session, void *data) +{ + u32 port_id; + int ret; + struct cpe_lsm_ids ids; + struct wcd_cpe_core *core = core_handle; + + ret = wcd_cpe_is_valid_lsm_session(core, session, __func__); + if (ret) + goto done; + + if (!data) { + dev_err(core->dev, "%s: data is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + port_id = *(u32 *)data; + dev_dbg(core->dev, "%s: port_id: %d\n", __func__, port_id); + + memset(&ids, 0, sizeof(ids)); + ids.module_id = LSM_MODULE_ID_FRAMEWORK; + ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT; + + ret = wcd_cpe_send_param_connectport(core, session, NULL, + &ids, port_id); + if (ret) + dev_err(core->dev, + "%s: send_param_connectport failed, err %d\n", + __func__, ret); +done: + return ret; +} + +/* + * wcd_cpe_get_lsm_ops: register lsm driver to codec + * @lsm_ops: structure with lsm callbacks + * @codec: codec to which this lsm driver is registered to + */ +int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops) +{ + lsm_ops->lsm_alloc_session = wcd_cpe_alloc_lsm_session; + lsm_ops->lsm_dealloc_session = wcd_cpe_dealloc_lsm_session; + lsm_ops->lsm_open_tx = wcd_cpe_cmd_lsm_open_tx; + lsm_ops->lsm_close_tx = wcd_cpe_cmd_lsm_close_tx; + lsm_ops->lsm_shmem_alloc = wcd_cpe_cmd_lsm_shmem_alloc; + lsm_ops->lsm_shmem_dealloc = wcd_cpe_cmd_lsm_shmem_dealloc; + lsm_ops->lsm_register_snd_model = wcd_cpe_lsm_reg_snd_model; + lsm_ops->lsm_deregister_snd_model = wcd_cpe_lsm_dereg_snd_model; + lsm_ops->lsm_get_afe_out_port_id = wcd_cpe_lsm_get_afe_out_port_id; + lsm_ops->lsm_start = wcd_cpe_cmd_lsm_start; + lsm_ops->lsm_stop = wcd_cpe_cmd_lsm_stop; + lsm_ops->lsm_lab_control = wcd_cpe_lsm_lab_control; + lsm_ops->lab_ch_setup = wcd_cpe_lab_ch_setup; + lsm_ops->lsm_set_data = wcd_cpe_lsm_set_data; + lsm_ops->lsm_set_fmt_cfg = wcd_cpe_lsm_set_fmt_cfg; + lsm_ops->lsm_set_one_param = wcd_cpe_set_one_param; + lsm_ops->lsm_get_snd_model_offset = wcd_cpe_snd_model_offset; + lsm_ops->lsm_set_media_fmt_params = wcd_cpe_lsm_set_media_fmt_params; + lsm_ops->lsm_set_port = wcd_cpe_lsm_set_port; + + return 0; +} +EXPORT_SYMBOL(wcd_cpe_get_lsm_ops); + +static int fill_afe_cmd_header(struct cmi_hdr *hdr, u8 port_id, + u16 opcode, u8 pld_size, + bool obm_flag) +{ + CMI_HDR_SET_SESSION(hdr, port_id); + CMI_HDR_SET_SERVICE(hdr, CMI_CPE_AFE_SERVICE_ID); + + CMI_HDR_SET_PAYLOAD_SIZE(hdr, pld_size); + + hdr->opcode = opcode; + + if (obm_flag) + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_OUT_BAND); + else + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND); + + return 0; +} + +/* + * wcd_cpe_cmi_send_afe_msg: send message to AFE service + * @core: wcd cpe core handle + * @port_cfg: configuration data for the afe port + * for which this message is to be sent + * @message: actual message with header and payload + * + * Port specific lock needs to be acquired before this + * function can be invoked + */ +static int wcd_cpe_cmi_send_afe_msg( + struct wcd_cpe_core *core, + struct wcd_cmi_afe_port_data *port_d, + void *message) +{ + int ret = 0; + struct cmi_hdr *hdr = message; + + pr_debug("%s: sending message with opcode 0x%x\n", + __func__, hdr->opcode); + + if (unlikely(!wcd_cpe_is_online_state(core))) { + dev_err(core->dev, "%s: CPE offline\n", __func__); + return 0; + } + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, true); + + ret = cmi_send_msg(message); + if (ret) { + pr_err("%s: cmd 0x%x send failed, err = %d\n", + __func__, hdr->opcode, ret); + goto rel_bus_vote; + } + + ret = wait_for_completion_timeout(&port_d->afe_cmd_complete, + CMI_CMD_TIMEOUT); + if (ret > 0) { + pr_debug("%s: command 0x%x, received response 0x%x\n", + __func__, hdr->opcode, port_d->cmd_result); + if (port_d->cmd_result == CMI_SHMEM_ALLOC_FAILED) + port_d->cmd_result = CPE_ENOMEMORY; + if (port_d->cmd_result > 0) + pr_err("%s: CPE returned error[%s]\n", + __func__, cpe_err_get_err_str( + port_d->cmd_result)); + ret = cpe_err_get_lnx_err_code(port_d->cmd_result); + goto rel_bus_vote; + } else { + pr_err("%s: command 0x%x send timed out\n", + __func__, hdr->opcode); + ret = -ETIMEDOUT; + goto rel_bus_vote; + } + +rel_bus_vote: + reinit_completion(&port_d->afe_cmd_complete); + + if (CMI_HDR_GET_OBM_FLAG(hdr)) + wcd_cpe_bus_vote_max_bw(core, false); + + return ret; +} + + + +/* + * wcd_cpe_afe_shmem_alloc: allocate the cpe memory for afe service + * @core: handle to cpe core + * @port_cfg: configuration data for the port which needs + * memory to be allocated on CPE + * @size: size of the memory to be allocated + */ +static int wcd_cpe_afe_shmem_alloc( + struct wcd_cpe_core *core, + struct wcd_cmi_afe_port_data *port_d, + u32 size) +{ + struct cpe_cmd_shmem_alloc cmd_shmem_alloc; + int ret = 0; + + pr_debug("%s: enter: size = %d\n", __func__, size); + + memset(&cmd_shmem_alloc, 0, sizeof(cmd_shmem_alloc)); + if (fill_afe_cmd_header(&cmd_shmem_alloc.hdr, port_d->port_id, + CPE_AFE_PORT_CMD_SHARED_MEM_ALLOC, + SHMEM_ALLOC_CMD_PLD_SIZE, false)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_shmem_alloc.size = size; + + ret = wcd_cpe_cmi_send_afe_msg(core, port_d, &cmd_shmem_alloc); + if (ret) { + pr_err("%s: afe_shmem_alloc fail,ret = %d\n", + __func__, ret); + goto end_ret; + } + + pr_debug("%s: completed %s, mem_handle = 0x%x\n", + __func__, "CPE_AFE_CMD_SHARED_MEM_ALLOC", + port_d->mem_handle); + +end_ret: + return ret; +} + +/* + * wcd_cpe_afe_shmem_dealloc: deallocate the cpe memory for + * afe service + * @core: handle to cpe core + * @port_d: configuration data for the port which needs + * memory to be deallocated on CPE + * The memory handle to be de-allocated is saved in the + * port configuration data + */ +static int wcd_cpe_afe_shmem_dealloc( + struct wcd_cpe_core *core, + struct wcd_cmi_afe_port_data *port_d) +{ + struct cpe_cmd_shmem_dealloc cmd_dealloc; + int ret = 0; + + pr_debug("%s: enter, port_id = %d\n", + __func__, port_d->port_id); + + memset(&cmd_dealloc, 0, sizeof(cmd_dealloc)); + if (fill_afe_cmd_header(&cmd_dealloc.hdr, port_d->port_id, + CPE_AFE_PORT_CMD_SHARED_MEM_DEALLOC, + SHMEM_DEALLOC_CMD_PLD_SIZE, false)) { + ret = -EINVAL; + goto end_ret; + } + + cmd_dealloc.addr = port_d->mem_handle; + ret = wcd_cpe_cmi_send_afe_msg(core, port_d, &cmd_dealloc); + if (ret) { + pr_err("failed to send shmem_dealloc cmd\n"); + goto end_ret; + } + memset(&port_d->mem_handle, 0, + sizeof(port_d->mem_handle)); + +end_ret: + return ret; +} + +/* + * wcd_cpe_send_afe_cal: send the acdb calibration to AFE port + * @core: handle to cpe core + * @port_d: configuration data for the port for which the + * calibration needs to be appplied + */ +static int wcd_cpe_send_afe_cal(void *core_handle, + struct wcd_cmi_afe_port_data *port_d) +{ + + struct cal_block_data *afe_cal = NULL; + struct wcd_cpe_core *core = core_handle; + struct cmi_obm_msg obm_msg; + void *inb_msg = NULL; + void *msg; + int rc = 0; + bool is_obm_msg; + + if (core->cal_data[WCD_CPE_LSM_CAL_AFE] == NULL) { + pr_err("%s: LSM cal not allocated!\n", + __func__); + rc = -EINVAL; + goto rel_cal_mutex; + } + + mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_AFE]->lock); + afe_cal = cal_utils_get_only_cal_block( + core->cal_data[WCD_CPE_LSM_CAL_AFE]); + if (!afe_cal) { + pr_err("%s: failed to get afe cal block\n", + __func__); + rc = -EINVAL; + goto rel_cal_mutex; + } + + if (afe_cal->cal_data.size == 0) { + dev_dbg(core->dev, "%s: No AFE cal to send\n", + __func__); + rc = 0; + goto rel_cal_mutex; + } + + is_obm_msg = (afe_cal->cal_data.size > + CMI_INBAND_MESSAGE_SIZE) ? true : false; + + if (is_obm_msg) { + struct cmi_hdr *hdr = &(obm_msg.hdr); + struct cmi_obm *pld = &(obm_msg.pld); + + rc = wcd_cpe_afe_shmem_alloc(core, port_d, + afe_cal->cal_data.size); + if (rc) { + dev_err(core->dev, + "%s: AFE shmem alloc fail %d\n", + __func__, rc); + goto rel_cal_mutex; + } + + rc = fill_afe_cmd_header(hdr, port_d->port_id, + CPE_AFE_CMD_SET_PARAM, + CPE_AFE_PARAM_PAYLOAD_SIZE, + true); + if (rc) { + dev_err(core->dev, + "%s: invalid params for header, err = %d\n", + __func__, rc); + wcd_cpe_afe_shmem_dealloc(core, port_d); + goto rel_cal_mutex; + } + + pld->version = 0; + pld->size = afe_cal->cal_data.size; + pld->data_ptr.kvaddr = afe_cal->cal_data.kvaddr; + pld->mem_handle = port_d->mem_handle; + msg = &obm_msg; + + } else { + u8 *msg_pld; + struct cmi_hdr *hdr; + + inb_msg = kzalloc(sizeof(struct cmi_hdr) + + afe_cal->cal_data.size, + GFP_KERNEL); + if (!inb_msg) { + dev_err(core->dev, + "%s: no memory for afe cal inband\n", + __func__); + rc = -ENOMEM; + goto rel_cal_mutex; + } + + hdr = (struct cmi_hdr *) inb_msg; + + rc = fill_afe_cmd_header(hdr, port_d->port_id, + CPE_AFE_CMD_SET_PARAM, + CPE_AFE_PARAM_PAYLOAD_SIZE, + false); + if (rc) { + dev_err(core->dev, + "%s: invalid params for header, err = %d\n", + __func__, rc); + kfree(inb_msg); + inb_msg = NULL; + goto rel_cal_mutex; + } + + msg_pld = ((u8 *) inb_msg) + sizeof(struct cmi_hdr); + memcpy(msg_pld, afe_cal->cal_data.kvaddr, + afe_cal->cal_data.size); + + msg = inb_msg; + } + + rc = wcd_cpe_cmi_send_afe_msg(core, port_d, msg); + if (rc) + pr_err("%s: afe cal for listen failed, rc = %d\n", + __func__, rc); + + if (is_obm_msg) { + wcd_cpe_afe_shmem_dealloc(core, port_d); + port_d->mem_handle = 0; + } else { + kfree(inb_msg); + inb_msg = NULL; + } + +rel_cal_mutex: + mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_AFE]->lock); + return rc; +} + +/* + * wcd_cpe_is_valid_port: check validity of afe port id + * @core: handle to core to check for validity + * @afe_cfg: client provided afe configuration + * @func: function name invoking this validity check, + * used for logging purpose only. + */ +static int wcd_cpe_is_valid_port(struct wcd_cpe_core *core, + struct wcd_cpe_afe_port_cfg *afe_cfg, + const char *func) +{ + if (unlikely(IS_ERR_OR_NULL(core))) { + pr_err("%s: Invalid core handle\n", func); + return -EINVAL; + } + + if (afe_cfg->port_id > WCD_CPE_AFE_MAX_PORTS) { + dev_err(core->dev, + "%s: invalid afe port (%u)\n", + func, afe_cfg->port_id); + return -EINVAL; + } + + dev_dbg(core->dev, + "%s: port_id = %u\n", + func, afe_cfg->port_id); + + return 0; +} + +static int wcd_cpe_afe_svc_cmd_mode(void *core_handle, + u8 mode) +{ + struct cpe_afe_svc_cmd_mode afe_mode; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret; + + afe_port_d = &afe_ports[0]; + /* + * AFE SVC mode command is for the service and not port + * specific, hence use AFE port as 0 so the command will + * be applied to all AFE ports on CPE. + */ + afe_port_d->port_id = 0; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + memset(&afe_mode, 0, sizeof(afe_mode)); + if (fill_afe_cmd_header(&afe_mode.hdr, afe_port_d->port_id, + CPE_AFE_SVC_CMD_LAB_MODE, + CPE_AFE_CMD_MODE_PAYLOAD_SIZE, + false)) { + ret = -EINVAL; + goto err_ret; + } + + afe_mode.mode = mode; + + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &afe_mode); + if (ret) + dev_err(core->dev, + "%s: afe_svc_mode cmd failed, err = %d\n", + __func__, ret); + +err_ret: + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +static int wcd_cpe_afe_cmd_port_cfg(void *core_handle, + struct wcd_cpe_afe_port_cfg *afe_cfg) +{ + struct cpe_afe_cmd_port_cfg port_cfg_cmd; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret; + + ret = wcd_cpe_is_valid_port(core, afe_cfg, __func__); + if (ret) + goto done; + + afe_port_d = &afe_ports[afe_cfg->port_id]; + afe_port_d->port_id = afe_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + memset(&port_cfg_cmd, 0, sizeof(port_cfg_cmd)); + if (fill_afe_cmd_header(&port_cfg_cmd.hdr, + afe_cfg->port_id, + CPE_AFE_PORT_CMD_GENERIC_CONFIG, + CPE_AFE_CMD_PORT_CFG_PAYLOAD_SIZE, + false)) { + ret = -EINVAL; + goto err_ret; + } + + port_cfg_cmd.bit_width = afe_cfg->bit_width; + port_cfg_cmd.num_channels = afe_cfg->num_channels; + port_cfg_cmd.sample_rate = afe_cfg->sample_rate; + + if (afe_port_d->port_id == CPE_AFE_PORT_3_TX) + port_cfg_cmd.buffer_size = WCD_CPE_EC_PP_BUF_SIZE; + else + port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width, + afe_cfg->sample_rate); + + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &port_cfg_cmd); + if (ret) + dev_err(core->dev, + "%s: afe_port_config failed, err = %d\n", + __func__, ret); + +err_ret: + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); +done: + return ret; +} + +/* + * wcd_cpe_afe_set_params: set the parameters for afe port + * @afe_cfg: configuration data for the port for which the + * parameters are to be set + */ +static int wcd_cpe_afe_set_params(void *core_handle, + struct wcd_cpe_afe_port_cfg *afe_cfg, bool afe_mad_ctl) +{ + struct cpe_afe_params afe_params; + struct cpe_afe_hw_mad_ctrl *hw_mad_ctrl = &afe_params.hw_mad_ctrl; + struct cpe_afe_port_cfg *port_cfg = &afe_params.port_cfg; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0, pld_size = 0; + + ret = wcd_cpe_is_valid_port(core, afe_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[afe_cfg->port_id]; + afe_port_d->port_id = afe_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + ret = wcd_cpe_send_afe_cal(core, afe_port_d); + if (ret) { + dev_err(core->dev, + "%s: afe acdb cal send failed, err = %d\n", + __func__, ret); + goto err_ret; + } + + pld_size = CPE_AFE_PARAM_PAYLOAD_SIZE; + memset(&afe_params, 0, sizeof(afe_params)); + + if (fill_afe_cmd_header(&afe_params.hdr, + afe_cfg->port_id, + CPE_AFE_CMD_SET_PARAM, + (u8) pld_size, false)) { + ret = -EINVAL; + goto err_ret; + } + + hw_mad_ctrl->param.module_id = CPE_AFE_MODULE_HW_MAD; + hw_mad_ctrl->param.param_id = CPE_AFE_PARAM_ID_HW_MAD_CTL; + hw_mad_ctrl->param.p_size.sr.param_size = PARAM_SIZE_AFE_HW_MAD_CTRL; + hw_mad_ctrl->param.p_size.sr.reserved = 0; + hw_mad_ctrl->minor_version = 1; + hw_mad_ctrl->mad_type = MAD_TYPE_AUDIO; + hw_mad_ctrl->mad_enable = afe_mad_ctl; + + port_cfg->param.module_id = CPE_AFE_MODULE_AUDIO_DEV_INTERFACE; + port_cfg->param.param_id = CPE_AFE_PARAM_ID_GENERIC_PORT_CONFIG; + port_cfg->param.p_size.sr.param_size = PARAM_SIZE_AFE_PORT_CFG; + port_cfg->param.p_size.sr.reserved = 0; + port_cfg->minor_version = 1; + port_cfg->bit_width = afe_cfg->bit_width; + port_cfg->num_channels = afe_cfg->num_channels; + port_cfg->sample_rate = afe_cfg->sample_rate; + + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &afe_params); + if (ret) + dev_err(core->dev, + "%s: afe_port_config failed, err = %d\n", + __func__, ret); +err_ret: + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_start: send the start command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be started. + */ +static int wcd_cpe_afe_port_start(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(struct cmi_hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_START, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_port_start cmd failed, err = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_stop: send stop command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be stopped. + */ +static int wcd_cpe_afe_port_stop(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_STOP, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_stop cmd failed, err = %d\n", + __func__, ret); + + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_suspend: send suspend command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be suspended. + */ +static int wcd_cpe_afe_port_suspend(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(struct cmi_hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_SUSPEND, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_suspend cmd failed, err = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; +} + +/* + * wcd_cpe_afe_port_resume: send the resume command to afe service + * @core_handle: handle to the cpe core + * @port_cfg: configuration data for the afe port which needs + * to be resumed. + */ +static int wcd_cpe_afe_port_resume(void *core_handle, + struct wcd_cpe_afe_port_cfg *port_cfg) +{ + struct cmi_hdr hdr; + struct wcd_cpe_core *core = core_handle; + struct wcd_cmi_afe_port_data *afe_port_d; + int ret = 0; + + ret = wcd_cpe_is_valid_port(core, port_cfg, __func__); + if (ret) + return ret; + + afe_port_d = &afe_ports[port_cfg->port_id]; + afe_port_d->port_id = port_cfg->port_id; + + WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe"); + + memset(&hdr, 0, sizeof(hdr)); + fill_afe_cmd_header(&hdr, port_cfg->port_id, + CPE_AFE_PORT_CMD_RESUME, + 0, false); + ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr); + if (ret) + dev_err(core->dev, + "%s: afe_resume cmd failed, err = %d\n", + __func__, ret); + WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe"); + return ret; + +} + +/* + * wcd_cpe_register_afe_driver: register lsm driver to codec + * @cpe_ops: structure with lsm callbacks + * @codec: codec to which this lsm driver is registered to + */ +int wcd_cpe_get_afe_ops(struct wcd_cpe_afe_ops *afe_ops) +{ + afe_ops->afe_set_params = wcd_cpe_afe_set_params; + afe_ops->afe_port_start = wcd_cpe_afe_port_start; + afe_ops->afe_port_stop = wcd_cpe_afe_port_stop; + afe_ops->afe_port_suspend = wcd_cpe_afe_port_suspend; + afe_ops->afe_port_resume = wcd_cpe_afe_port_resume; + afe_ops->afe_port_cmd_cfg = wcd_cpe_afe_cmd_port_cfg; + + return 0; +} +EXPORT_SYMBOL(wcd_cpe_get_afe_ops); + +MODULE_DESCRIPTION("WCD CPE Core"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd_cpe_core.h b/sound/soc/codecs/wcd_cpe_core.h new file mode 100644 index 000000000000..3d672b860ff4 --- /dev/null +++ b/sound/soc/codecs/wcd_cpe_core.h @@ -0,0 +1,226 @@ +/* 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. + */ + +#ifndef WCD_CPE_CORE_H +#define WCD_CPE_CORE_H + +#include +#include +#include "wcd_cpe_services.h" + +#define WCD_CPE_LAB_MAX_LATENCY 250 +#define WCD_CPE_MAD_SLIM_CHANNEL 140 + +/* Indicates CPE block is ready for image re-download */ +#define WCD_CPE_BLK_READY (1 << 0) +/* Indicates the underlying bus is ready */ +#define WCD_CPE_BUS_READY (1 << 1) + +/* + * only when the underlying bus and CPE block both are ready, + * the state will be ready to download + */ +#define WCD_CPE_READY_TO_DLOAD \ + (WCD_CPE_BLK_READY | WCD_CPE_BUS_READY) + +#define WCD_CPE_LOAD_IMEM (1 << 0) +#define WCD_CPE_LOAD_DATA (1 << 1) +#define WCD_CPE_LOAD_ALL \ + (WCD_CPE_LOAD_IMEM | WCD_CPE_LOAD_DATA) + +#define WCD_CPE_IMAGE_FNAME_MAX 64 + +#define WCD_CPE_AFE_OUT_PORT_2 2 +#define WCD_CPE_AFE_OUT_PORT_4 4 + +enum { + WCD_CPE_LSM_CAL_AFE = 0, + WCD_CPE_LSM_CAL_LSM, + WCD_CPE_LSM_CAL_TOPOLOGY_ID, + WCD_CPE_LSM_CAL_MAX, +}; + +enum cpe_err_irq_cntl_type { + CPE_ERR_IRQ_MASK = 0, + CPE_ERR_IRQ_UNMASK, + CPE_ERR_IRQ_CLEAR, + CPE_ERR_IRQ_STATUS, +}; + +struct wcd_cpe_cdc_cb { + /* codec provided callback to enable RCO */ + int (*cdc_clk_en)(struct snd_soc_codec *, bool); + + /* callback for FLL setup for codec */ + int (*cpe_clk_en)(struct snd_soc_codec *, bool); + int (*cdc_ext_clk)(struct snd_soc_codec *codec, int enable, bool dapm); + int (*lab_cdc_ch_ctl)(struct snd_soc_codec *codec, u8 event); + int (*get_afe_out_port_id)(struct snd_soc_codec *codec, u16 *port_id); + int (*bus_vote_bw)(struct snd_soc_codec *codec, + bool vote); + + /* Callback to control the cpe error interrupt mask/status/clear */ + int (*cpe_err_irq_control)(struct snd_soc_codec *codec, + enum cpe_err_irq_cntl_type cntl_type, + u8 *status); +}; + +enum wcd_cpe_ssr_state_event { + /* Indicates CPE is initialized */ + WCD_CPE_INITIALIZED = 0, + /* Indicates that IMEM is downloaded to CPE */ + WCD_CPE_IMEM_DOWNLOADED, + /* Indicates CPE is enabled */ + WCD_CPE_ENABLED, + /* Indicates that CPE is currently active */ + WCD_CPE_ACTIVE, + /* Event from underlying bus notifying bus is down */ + WCD_CPE_BUS_DOWN_EVENT, + /* Event from CPE block, notifying CPE is down */ + WCD_CPE_SSR_EVENT, + /* Event from underlying bus notifying bus is up */ + WCD_CPE_BUS_UP_EVENT, +}; + +struct wcd_cpe_ssr_entry { + int offline; + u32 offline_change; + wait_queue_head_t offline_poll_wait; + struct snd_info_entry *entry; +}; + +struct wcd_cpe_irq_info { + int cpe_engine_irq; + int cpe_err_irq; + u8 cpe_fatal_irqs; +}; + +struct wcd_cpe_hw_info { + u32 dram_offset; + size_t dram_size; + u32 iram_offset; + size_t iram_size; +}; + +struct wcd_cpe_core { + /* handle to cpe services */ + void *cpe_handle; + + /* registration handle to cpe services */ + void *cpe_reg_handle; + + /* cmi registration handle for afe service */ + void *cmi_afe_handle; + + /* handle to codec */ + struct snd_soc_codec *codec; + + /* codec device */ + struct device *dev; + + /* firmware image file name */ + char fname[WCD_CPE_IMAGE_FNAME_MAX]; + + /* firmware image file name from sysfs */ + char dyn_fname[WCD_CPE_IMAGE_FNAME_MAX]; + + /* codec information needed by cpe services */ + struct cpe_svc_codec_info_v1 cdc_info; + + /* work to perform image download */ + struct work_struct load_fw_work; + + /* flag to indicate mode in which cpe needs to be booted */ + int cpe_debug_mode; + + /* callbacks for codec specific implementation */ + const struct wcd_cpe_cdc_cb *cpe_cdc_cb; + + /* work to handle CPE SSR*/ + struct work_struct ssr_work; + + /* PM handle for suspend mode during SSR */ + struct pm_qos_request pm_qos_req; + + /* completion event indicating CPE OFFLINE */ + struct completion offline_compl; + + /* entry into snd card procfs indicating cpe status */ + struct wcd_cpe_ssr_entry ssr_entry; + + /* + * completion event to signal CPE is + * ready for image re-download + */ + struct completion ready_compl; + + /* maintains the status for cpe ssr */ + u8 ready_status; + + /* Indicate SSR type */ + enum wcd_cpe_ssr_state_event ssr_type; + + /* mutex to protect cpe ssr status variables */ + struct mutex ssr_lock; + + /* Store the calibration data needed for cpe */ + struct cal_type_data *cal_data[WCD_CPE_LSM_CAL_MAX]; + + /* completion event to signal CPE is online */ + struct completion online_compl; + + /* reference counter for cpe usage */ + u8 cpe_users; + + /* Ramdump support */ + void *cpe_ramdump_dev; + struct ramdump_segment cpe_ramdump_seg; + dma_addr_t cpe_dump_addr; + void *cpe_dump_v_addr; + + /* SFR support */ + u32 sfr_buf_addr; + size_t sfr_buf_size; + + /* IRQ information for CPE interrupts */ + struct wcd_cpe_irq_info irq_info; + + /* Kobject for sysfs entry */ + struct kobject cpe_kobj; + + /* Reference count for cpe clk*/ + int cpe_clk_ref; + + /* codec based hardware info */ + struct wcd_cpe_hw_info hw_info; +}; + +struct wcd_cpe_params { + struct snd_soc_codec *codec; + struct wcd_cpe_core * (*get_cpe_core)( + struct snd_soc_codec *); + const struct wcd_cpe_cdc_cb *cdc_cb; + int dbg_mode; + u16 cdc_major_ver; + u16 cdc_minor_ver; + u32 cdc_id; + + struct wcd_cpe_irq_info cdc_irq_info; + + struct cpe_svc_init_param *cpe_svc_params; +}; + +int wcd_cpe_ssr_event(void *core_handle, + enum wcd_cpe_ssr_state_event event); +struct wcd_cpe_core *wcd_cpe_init(const char *img_fname, +struct snd_soc_codec *codec, struct wcd_cpe_params *params); +#endif diff --git a/sound/soc/codecs/wcd_cpe_services.c b/sound/soc/codecs/wcd_cpe_services.c new file mode 100644 index 000000000000..0028ebc08d5f --- /dev/null +++ b/sound/soc/codecs/wcd_cpe_services.c @@ -0,0 +1,2990 @@ +/* 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 "wcd_cpe_services.h" +#include "wcd_cmi_api.h" + +#define CPE_MSG_BUFFER_SIZE 132 +#define CPE_NO_SERVICE 0 + +#define CMI_DRIVER_SUPPORTED_VERSION 0 +#define CMI_API_SUCCESS 0 +#define CMI_MSG_TRANSPORT (0x0002) +#define CPE_SVC_INACTIVE_STATE_RETRIES_MAX 10 + +#define TOMTOM_A_SVASS_SPE_DRAM_OFFSET 0x50000 +#define TOMTOM_A_SVASS_SPE_DRAM_SIZE 0x30000 +#define TOMTOM_A_SVASS_SPE_IRAM_OFFSET 0x80000 +#define TOMTOM_A_SVASS_SPE_IRAM_SIZE 0xC000 +#define TOMTOM_A_SVASS_SPE_INBOX_SIZE 12 +#define TOMTOM_A_SVASS_SPE_OUTBOX_SIZE 12 + +#define MEM_ACCESS_NONE_VAL 0x0 +#define MEM_ACCESS_IRAM_VAL 0x1 +#define MEM_ACCESS_DRAM_VAL 0x2 +#define LISTEN_CTL_SPE_VAL 0x0 +#define LISTEN_CTL_MSM_VAL 0x1 + +#define TOMTOM_A_SVASS_SPE_INBOX(N) (TOMTOM_A_SVASS_SPE_INBOX_0 + (N)) +#define TOMTOM_A_SVASS_SPE_OUTBOX(N) (TOMTOM_A_SVASS_SPE_OUTBOX_0 + (N)) + +#define WCD9335_CPE_SS_SPE_DRAM_OFFSET 0x48000 +#define WCD9335_CPE_SS_SPE_DRAM_SIZE 0x34000 +#define WCD9335_CPE_SS_SPE_IRAM_OFFSET 0x80000 +#define WCD9335_CPE_SS_SPE_IRAM_SIZE 0x20000 + +#define WCD9335_CPE_SS_SPE_INBOX_SIZE 16 +#define WCD9335_CPE_SS_SPE_OUTBOX_SIZE 16 +#define WCD9335_CPE_SS_SPE_MEM_BANK_SIZ 16 + +#define WCD9335_CPE_SS_SPE_INBOX1(N) (WCD9335_CPE_SS_INBOX1_0 + (N)) +#define WCD9335_CPE_SS_SPE_OUTBOX1(N) (WCD9335_CPE_SS_OUTBOX1_0 + (N)) +#define WCD9335_CPE_SS_MEM_BANK(N) (WCD9335_CPE_SS_MEM_BANK_0 + (N)) + +#define CHUNK_SIZE 16 + +#define CPE_SVC_GRAB_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock acquire\n", \ + __func__, name); \ + mutex_lock(lock); \ +} + +#define CPE_SVC_REL_LOCK(lock, name) \ +{ \ + pr_debug("%s: %s lock release\n", \ + __func__, name); \ + mutex_unlock(lock); \ +} + +static const struct cpe_svc_hw_cfg cpe_svc_tomtom_info = { + TOMTOM_A_SVASS_SPE_DRAM_SIZE, + TOMTOM_A_SVASS_SPE_DRAM_OFFSET, + TOMTOM_A_SVASS_SPE_IRAM_SIZE, + TOMTOM_A_SVASS_SPE_IRAM_OFFSET, + TOMTOM_A_SVASS_SPE_INBOX_SIZE, + TOMTOM_A_SVASS_SPE_OUTBOX_SIZE +}; + +static const struct cpe_svc_hw_cfg cpe_svc_wcd9335_info = { + WCD9335_CPE_SS_SPE_DRAM_SIZE, + WCD9335_CPE_SS_SPE_DRAM_OFFSET, + WCD9335_CPE_SS_SPE_IRAM_SIZE, + WCD9335_CPE_SS_SPE_IRAM_OFFSET, + WCD9335_CPE_SS_SPE_INBOX_SIZE, + WCD9335_CPE_SS_SPE_OUTBOX_SIZE +}; + +enum cpe_state { + CPE_STATE_UNINITIALIZED = 0, + CPE_STATE_INITIALIZED, + CPE_STATE_IDLE, + CPE_STATE_DOWNLOADING, + CPE_STATE_BOOTING, + CPE_STATE_SENDING_MSG, + CPE_STATE_OFFLINE, + CPE_STATE_BUFFERING, + CPE_STATE_BUFFERING_CANCELLED +}; + +enum cpe_substate { + CPE_SS_IDLE = 0, + CPE_SS_MSG_REQUEST_ACCESS, + CPE_SS_MSG_SEND_INBOX, + CPE_SS_MSG_SENT, + CPE_SS_DL_DOWNLOADING, + CPE_SS_DL_COMPLETED, + CPE_SS_BOOT, + CPE_SS_BOOT_INIT, + CPE_SS_ONLINE +}; + +enum cpe_command { + CPE_CMD_KILL_THREAD = 0, + CPE_CMD_BOOT, + CPE_CMD_BOOT_INITIALIZE, + CPE_CMD_BOOT_COMPLETE, + CPE_CMD_SEND_MSG, + CPE_CMD_SEND_TRANS_MSG, + CPE_CMD_SEND_MSG_COMPLETE, + CPE_CMD_PROCESS_IRQ, + CPE_CMD_RAMDUMP, + CPE_CMD_DL_SEGMENT, + CPE_CMD_SHUTDOWN, + CPE_CMD_RESET, + CPE_CMD_DEINITIALIZE, + CPE_CMD_READ, + CPE_CMD_ENABLE_LAB, + CPE_CMD_DISABLE_LAB, + CPE_CMD_SWAP_BUFFER, + CPE_LAB_CFG_SB, + CPE_CMD_CANCEL_MEMACCESS, + CPE_CMD_PROC_INCOMING_MSG, + CPE_CMD_FTM_TEST, +}; + +enum cpe_process_result { + CPE_PROC_SUCCESS = 0, + CPE_PROC_FAILED, + CPE_PROC_KILLED, + CPE_PROC_QUEUED, +}; + +struct cpe_command_node { + enum cpe_command command; + enum cpe_svc_result result; + void *data; + struct list_head list; +}; + +struct cpe_info { + struct list_head main_queue; + struct completion cmd_complete; + struct completion thread_comp; + void *thread_handler; + bool stop_thread; + struct mutex msg_lock; + enum cpe_state state; + enum cpe_substate substate; + struct list_head client_list; + enum cpe_process_result (*cpe_process_command) + (struct cpe_command_node *command_node); + enum cpe_svc_result (*cpe_cmd_validate) + (const struct cpe_info *i, + enum cpe_command command); + enum cpe_svc_result (*cpe_start_notification) + (struct cpe_info *i); + u32 initialized; + struct cpe_svc_tgt_abstraction *tgt; + void *pending; + void *data; + void *client_context; + u32 codec_id; + struct work_struct clk_plan_work; + struct completion core_svc_cmd_compl; +}; + +struct cpe_tgt_waiti_info { + u8 tgt_waiti_size; + u8 *tgt_waiti_data; +}; + +struct cpe_svc_tgt_abstraction { + enum cpe_svc_result (*tgt_boot)(int debug_mode); + + u32 (*tgt_cpar_init_done)(void); + + u32 (*tgt_is_active)(void); + + enum cpe_svc_result (*tgt_reset)(void); + + enum cpe_svc_result (*tgt_stop)(void); + + enum cpe_svc_result (*tgt_read_mailbox) + (u8 *buffer, size_t size); + + enum cpe_svc_result (*tgt_write_mailbox) + (u8 *buffer, size_t size); + + enum cpe_svc_result (*tgt_read_ram) + (struct cpe_info *c, + struct cpe_svc_mem_segment *data); + + enum cpe_svc_result (*tgt_write_ram) + (struct cpe_info *c, + const struct cpe_svc_mem_segment *data); + + enum cpe_svc_result (*tgt_route_notification) + (enum cpe_svc_module module, + enum cpe_svc_route_dest dest); + + enum cpe_svc_result (*tgt_set_debug_mode)(u32 enable); + const struct cpe_svc_hw_cfg *(*tgt_get_cpe_info)(void); + enum cpe_svc_result (*tgt_deinit) + (struct cpe_svc_tgt_abstraction *param); + enum cpe_svc_result (*tgt_voice_tx_lab) + (bool); + u8 *inbox; + u8 *outbox; + struct cpe_tgt_waiti_info *tgt_waiti_info; +}; + +static enum cpe_svc_result cpe_tgt_tomtom_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param); + +static enum cpe_svc_result cpe_tgt_wcd9335_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param); + +struct cpe_send_msg { + u8 *payload; + u32 isobm; + u32 address; + size_t size; +}; + +struct cpe_read_handle { + void *registration; + struct cpe_info t_info; + struct list_head buffers; + void *config; +}; + +struct generic_notification { + void (*notification) + (const struct cpe_svc_notification *parameter); + void (*cmi_notification) + (const struct cmi_api_notification *parameter); +}; + +struct cpe_notif_node { + struct generic_notification notif; + u32 mask; + u32 service; + const struct cpe_info *context; + const char *name; + u32 disabled; + struct list_head list; +}; + +struct cpe_priv { + struct cpe_info *cpe_default_handle; + void (*cpe_irq_control_callback)(u32 enable); + void (*cpe_query_freq_plans_cb) + (void *cdc_priv, + struct cpe_svc_cfg_clk_plan *clk_freq); + void (*cpe_change_freq_plan_cb)(void *cdc_priv, + u32 clk_freq); + u32 cpe_msg_buffer; + void *cpe_cmi_handle; + struct mutex cpe_api_mutex; + struct mutex cpe_svc_lock; + struct cpe_svc_boot_event cpe_debug_vector; + void *cdc_priv; +}; + +static struct cpe_priv cpe_d; + +static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle); + +static enum cpe_svc_result cpe_is_command_valid( + const struct cpe_info *t_info, + enum cpe_command command); + +static int cpe_register_read(u32 reg, u8 *val) +{ + *(val) = snd_soc_read(cpe_d.cdc_priv, reg); + return 0; +} + +static enum cpe_svc_result cpe_update_bits(u32 reg, + u32 mask, u32 value) +{ + int ret = 0; + + ret = snd_soc_update_bits(cpe_d.cdc_priv, reg, + mask, value); + if (ret < 0) + return CPE_SVC_FAILED; + + return CPE_SVC_SUCCESS; +} + +static int cpe_register_write(u32 reg, u32 val) +{ + int ret = 0; + + if (reg != TOMTOM_A_SVASS_MEM_BANK && + reg != WCD9335_CPE_SS_MEM_BANK_0) + pr_debug("%s: reg = 0x%x, value = 0x%x\n", + __func__, reg, val); + + ret = snd_soc_write(cpe_d.cdc_priv, reg, val); + if (ret < 0) + return CPE_SVC_FAILED; + + return CPE_SVC_SUCCESS; +} + +static int cpe_register_write_repeat(u32 reg, u8 *ptr, u32 to_write) +{ + struct snd_soc_codec *codec = cpe_d.cdc_priv; + struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent); + int ret = 0; + + ret = wcd9xxx_slim_write_repeat(wcd9xxx, reg, to_write, ptr); + if (ret != 0) + pr_err("%s: slim_write_repeat failed\n", __func__); + + if (ret < 0) + return CPE_SVC_FAILED; + + return CPE_SVC_SUCCESS; +} + +static bool cpe_register_read_autoinc_supported(void) +{ + return true; +} + + +/* Called under msgq locked context */ +static void cpe_cmd_received(struct cpe_info *t_info) +{ + struct cpe_command_node *node = NULL; + enum cpe_process_result proc_rc = CPE_PROC_SUCCESS; + + if (!t_info) { + pr_err("%s: Invalid thread info\n", + __func__); + return; + } + + while (!list_empty(&t_info->main_queue)) { + if (proc_rc != CPE_PROC_SUCCESS) + break; + node = list_first_entry(&t_info->main_queue, + struct cpe_command_node, list); + if (!node) + break; + list_del(&node->list); + proc_rc = t_info->cpe_process_command(node); + pr_debug("%s: process command return %d\n", + __func__, proc_rc); + + switch (proc_rc) { + case CPE_PROC_SUCCESS: + kfree(node); + break; + case CPE_PROC_FAILED: + kfree(node); + pr_err("%s: cmd failed\n", __func__); + break; + case CPE_PROC_KILLED: + break; + default: + list_add(&node->list, &(t_info->main_queue)); + + } + } +} + +static int cpe_worker_thread(void *context) +{ + struct cpe_info *t_info = (struct cpe_info *)context; + + /* + * Thread will run until requested to stop explicitly + * by setting the t_info->stop_thread flag + */ + while (1) { + /* Wait for command to be processed */ + wait_for_completion(&t_info->cmd_complete); + + CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock"); + cpe_cmd_received(t_info); + reinit_completion(&t_info->cmd_complete); + /* Check if thread needs to be stopped */ + if (t_info->stop_thread) + goto unlock_and_exit; + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + }; + +unlock_and_exit: + pr_debug("%s: thread stopped\n", __func__); + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + complete_and_exit(&t_info->thread_comp, 0); +} + +static void cpe_create_worker_thread(struct cpe_info *t_info) +{ + INIT_LIST_HEAD(&t_info->main_queue); + init_completion(&t_info->cmd_complete); + init_completion(&t_info->thread_comp); + t_info->stop_thread = false; + t_info->thread_handler = kthread_run(cpe_worker_thread, + (void *)t_info, "cpe-worker-thread"); + pr_debug("%s: Created new worker thread\n", + __func__); +} + +static void cpe_cleanup_worker_thread(struct cpe_info *t_info) +{ + if (!t_info->thread_handler) { + pr_err("%s: thread not created\n", __func__); + return; + } + + /* + * Wake up the command handler in case + * it is waiting for an command to be processed. + */ + CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock"); + t_info->stop_thread = true; + complete(&t_info->cmd_complete); + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + + /* Wait for the thread to exit */ + wait_for_completion(&t_info->thread_comp); + t_info->thread_handler = NULL; + + pr_debug("%s: Thread cleaned up successfully\n", + __func__); +} + +static enum cpe_svc_result +cpe_send_cmd_to_thread(struct cpe_info *t_info, + enum cpe_command command, void *data, + bool high_prio) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_command_node *cmd = NULL; + + rc = cpe_is_command_valid(t_info, command); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Invalid command %d\n", + __func__, command); + return rc; + } + + cmd = kzalloc(sizeof(struct cpe_command_node), + GFP_ATOMIC); + if (!cmd) + return CPE_SVC_NO_MEMORY; + + cmd->command = command; + cmd->data = data; + + CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock"); + if (high_prio) + list_add(&(cmd->list), + &(t_info->main_queue)); + else + list_add_tail(&(cmd->list), + &(t_info->main_queue)); + complete(&t_info->cmd_complete); + CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock"); + + return rc; +} + +static enum cpe_svc_result cpe_change_state( + struct cpe_info *t_info, + enum cpe_state state, enum cpe_substate ss) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + t_info->state = state; + t_info->substate = ss; + + pr_debug("%s: current state: %d,%d, new_state: %d,%d\n", + __func__, t_info->state, t_info->substate, + state, ss); + + return rc; +} + +static enum cpe_svc_result +cpe_is_command_valid(const struct cpe_info *t_info, + enum cpe_command command) +{ + enum cpe_svc_result rc = CPE_SVC_INVALID_HANDLE; + + if (t_info && t_info->cpe_cmd_validate) + rc = t_info->cpe_cmd_validate(t_info, command); + else + pr_err("%s: invalid handle or callback\n", + __func__); + return rc; +} + +static void cpe_notify_client(struct cpe_notif_node *client, + struct cpe_svc_notification *payload) +{ + if (!client || !payload) { + pr_err("%s: invalid client or payload\n", + __func__); + return; + } + + if (!(client->mask & payload->event)) { + pr_debug("%s: client mask 0x%x not registered for event 0x%x\n", + __func__, client->mask, payload->event); + return; + } + + if (client->notif.notification && !client->disabled) + client->notif.notification(payload); + + if ((client->mask & CPE_SVC_CMI_MSG) && + client->notif.cmi_notification) + client->notif.cmi_notification( + (const struct cmi_api_notification *)payload); +} + +static void cpe_broadcast_notification(const struct cpe_info *t_info, + struct cpe_svc_notification *payload) +{ + struct cpe_notif_node *n = NULL; + + if (!t_info || !payload) { + pr_err("%s: invalid handle\n", __func__); + return; + } + + pr_debug("%s: notify clients, event = %d\n", + __func__, payload->event); + payload->private_data = cpe_d.cdc_priv; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + list_for_each_entry(n, &t_info->client_list, list) { + if (!(n->mask & CPE_SVC_CMI_MSG)) + cpe_notify_client(n, payload); + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); +} + +static void *cpe_register_generic(struct cpe_info *t_info, + void notification_callback( + const struct cpe_svc_notification *parameter), + void cmi_callback( + const struct cmi_api_notification *parameter), + u32 mask, u32 service, const char *name) +{ + struct cpe_notif_node *n = NULL; + + n = kzalloc(sizeof(struct cpe_notif_node), + GFP_KERNEL); + if (!n) + return NULL; + n->mask = mask; + n->service = service; + n->notif.notification = notification_callback; + n->notif.cmi_notification = cmi_callback; + n->context = t_info; + n->disabled = false; + n->name = name; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + /* Make sure CPE core service is first */ + if (service == CMI_CPE_CORE_SERVICE_ID) + list_add(&n->list, &t_info->client_list); + else + list_add_tail(&n->list, &t_info->client_list); + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + + return n; +} + +static enum cpe_svc_result cpe_deregister_generic(struct cpe_info *t_info, + void *reg_handle) +{ + struct cpe_notif_node *n = (struct cpe_notif_node *)reg_handle; + + if (!t_info || !reg_handle) { + pr_err("%s: invalid handle\n", __func__); + return CPE_SVC_INVALID_HANDLE; + } + + list_del(&(n->list)); + kfree(reg_handle); + + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result cpe_svc_tgt_init(struct cpe_svc_codec_info_v1 *i, + struct cpe_svc_tgt_abstraction *abs) +{ + if (!i || !abs) { + pr_err("%s: Incorrect information provided\n", + __func__); + return CPE_SVC_FAILED; + } + + switch (i->id) { + case CPE_SVC_CODEC_TOMTOM: + return cpe_tgt_tomtom_init(i, abs); + case CPE_SVC_CODEC_WCD9335: + return cpe_tgt_wcd9335_init(i, abs); + default: + pr_err("%s: Codec type %d not supported\n", + __func__, i->id); + return CPE_SVC_FAILED; + } + + return CPE_SVC_SUCCESS; +} + +static void cpe_notify_cmi_client(struct cpe_info *t_info, u8 *payload, + enum cpe_svc_result result) +{ + struct cpe_notif_node *n = NULL; + struct cmi_api_notification notif; + struct cmi_hdr *hdr; + u8 service = 0; + + if (!t_info || !payload) { + pr_err("%s: invalid payload/handle\n", + __func__); + return; + } + + hdr = CMI_GET_HEADER(payload); + service = CMI_HDR_GET_SERVICE(hdr); + + notif.event = CPE_SVC_CMI_MSG; + notif.result = result; + notif.message = payload; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + list_for_each_entry(n, &t_info->client_list, list) { + + if ((n->mask & CPE_SVC_CMI_MSG) && + n->service == service && + n->notif.cmi_notification) { + n->notif.cmi_notification(¬if); + break; + } + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); +} + +static void cpe_toggle_irq_notification(struct cpe_info *t_info, u32 value) +{ + if (cpe_d.cpe_irq_control_callback) + cpe_d.cpe_irq_control_callback(value); +} + +static void cpe_command_cleanup(struct cpe_command_node *command_node) +{ + switch (command_node->command) { + case CPE_CMD_SEND_MSG: + case CPE_CMD_SEND_TRANS_MSG: + case CPE_CMD_SEND_MSG_COMPLETE: + case CPE_CMD_SHUTDOWN: + case CPE_CMD_READ: + kfree(command_node->data); + command_node->data = NULL; + break; + default: + pr_err("%s: unhandled command\n", + __func__); + break; + } +} + +static enum cpe_svc_result cpe_send_msg_to_inbox( + struct cpe_info *t_info, u32 opcode, + struct cpe_send_msg *msg) +{ + size_t bytes = 0; + size_t inbox_size = + t_info->tgt->tgt_get_cpe_info()->inbox_size; + struct cmi_hdr *hdr; + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + memset(t_info->tgt->inbox, 0, inbox_size); + hdr = CMI_GET_HEADER(t_info->tgt->inbox); + CMI_HDR_SET_SESSION(hdr, 1); + CMI_HDR_SET_SERVICE(hdr, CMI_CPE_CORE_SERVICE_ID); + CMI_HDR_SET_VERSION(hdr, CMI_DRIVER_SUPPORTED_VERSION); + CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND); + + switch (opcode) { + case CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC: { + struct cmi_core_svc_cmd_shared_mem_alloc *m; + + CMI_HDR_SET_OPCODE(hdr, + CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, + sizeof(struct cmi_core_svc_cmd_shared_mem_alloc)); + m = (struct cmi_core_svc_cmd_shared_mem_alloc *) + CMI_GET_PAYLOAD(t_info->tgt->inbox); + m->size = CPE_MSG_BUFFER_SIZE; + pr_debug("send shared mem alloc msg to cpe inbox\n"); + } + break; + case CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ: + CMI_HDR_SET_OPCODE(hdr, + CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, 0); + pr_debug("%s: Creating DRAM acces request msg\n", + __func__); + break; + + case CPE_CMI_BASIC_RSP_OPCODE: { + struct cmi_basic_rsp_result *rsp; + + CMI_HDR_SET_OPCODE(hdr, + CPE_CMI_BASIC_RSP_OPCODE); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, + sizeof(struct cmi_basic_rsp_result)); + rsp = (struct cmi_basic_rsp_result *) + CMI_GET_PAYLOAD(t_info->tgt->inbox); + rsp->status = 0; + pr_debug("%s: send basic response\n", __func__); + } + break; + + default: + if (msg->address != 0) { + struct cmi_msg_transport *m = NULL; + struct cpe_svc_mem_segment mem_seg; + + mem_seg.type = CPE_SVC_DATA_MEM; + if (msg->isobm) { + struct cmi_obm *obm = (struct cmi_obm *) + + CMI_GET_PAYLOAD(msg->payload); + mem_seg.cpe_addr = obm->mem_handle; + mem_seg.data = (u8 *)obm->data_ptr.kvaddr; + mem_seg.size = obm->size; + t_info->tgt->tgt_write_ram(t_info, &mem_seg); + } + + mem_seg.cpe_addr = msg->address; + mem_seg.data = msg->payload; + mem_seg.size = msg->size; + t_info->tgt->tgt_write_ram(t_info, &mem_seg); + + hdr = CMI_GET_HEADER(t_info->tgt->inbox); + CMI_HDR_SET_OPCODE(hdr, CMI_MSG_TRANSPORT); + m = (struct cmi_msg_transport *) + CMI_GET_PAYLOAD(t_info->tgt->inbox); + m->addr = msg->address; + m->size = msg->size; + CMI_HDR_SET_PAYLOAD_SIZE(hdr, + sizeof(struct cmi_msg_transport)); + } else { + memcpy(t_info->tgt->inbox, msg->payload, + msg->size); + } + + break; + } + + pr_debug("%s: sending message to cpe inbox\n", + __func__); + bytes = sizeof(struct cmi_hdr); + hdr = CMI_GET_HEADER(t_info->tgt->inbox); + bytes += CMI_HDR_GET_PAYLOAD_SIZE(hdr); + rc = t_info->tgt->tgt_write_mailbox(t_info->tgt->inbox, bytes); + + return rc; +} + +static bool cpe_is_cmd_clk_req(void *cmd) +{ + struct cmi_hdr *hdr; + + hdr = CMI_GET_HEADER(cmd); + + if ((CMI_HDR_GET_SERVICE(hdr) == + CMI_CPE_CORE_SERVICE_ID)) { + if (CMI_GET_OPCODE(cmd) == + CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST) + return true; + } + + return false; +} + +static enum cpe_svc_result cpe_process_clk_change_req( + struct cpe_info *t_info) +{ + struct cmi_core_svc_cmd_clk_freq_request *req; + + req = (struct cmi_core_svc_cmd_clk_freq_request *) + CMI_GET_PAYLOAD(t_info->tgt->outbox); + + if (!cpe_d.cpe_change_freq_plan_cb) { + pr_err("%s: No support for clk freq change\n", + __func__); + return CPE_SVC_FAILED; + } + + cpe_d.cpe_change_freq_plan_cb(cpe_d.cdc_priv, + req->clk_freq); + + /*send a basic response*/ + cpe_send_msg_to_inbox(t_info, + CPE_CMI_BASIC_RSP_OPCODE, NULL); + + return CPE_SVC_SUCCESS; +} + +static void cpe_process_irq_int(u32 irq, + struct cpe_info *t_info) +{ + struct cpe_command_node temp_node; + struct cpe_send_msg *m; + u8 size = 0; + bool err_irq = false; + struct cmi_hdr *hdr; + + pr_debug("%s: irq = %u\n", __func__, irq); + + if (!t_info) { + pr_err("%s: Invalid handle\n", + __func__); + return; + } + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + switch (irq) { + case CPE_IRQ_OUTBOX_IRQ: + size = t_info->tgt->tgt_get_cpe_info()->outbox_size; + t_info->tgt->tgt_read_mailbox(t_info->tgt->outbox, size); + break; + + case CPE_IRQ_MEM_ACCESS_ERROR: + err_irq = true; + cpe_change_state(t_info, CPE_STATE_OFFLINE, CPE_SS_IDLE); + break; + + case CPE_IRQ_WDOG_BITE: + case CPE_IRQ_RCO_WDOG_INT: + err_irq = true; + __cpe_svc_shutdown(t_info); + break; + + case CPE_IRQ_FLL_LOCK_LOST: + default: + err_irq = true; + break; + } + + if (err_irq) { + pr_err("%s: CPE error IRQ %u occurred\n", + __func__, irq); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return; + } + + switch (t_info->state) { + case CPE_STATE_BOOTING: + + switch (t_info->substate) { + case CPE_SS_BOOT: + temp_node.command = CPE_CMD_BOOT_INITIALIZE; + temp_node.result = CPE_SVC_SUCCESS; + t_info->substate = CPE_SS_BOOT_INIT; + t_info->cpe_process_command(&temp_node); + break; + + case CPE_SS_BOOT_INIT: + temp_node.command = CPE_CMD_BOOT_COMPLETE; + temp_node.result = CPE_SVC_SUCCESS; + t_info->substate = CPE_SS_ONLINE; + t_info->cpe_process_command(&temp_node); + break; + + default: + pr_debug("%s: unhandled substate %d for state %d\n", + __func__, t_info->state, t_info->substate); + break; + } + break; + + case CPE_STATE_SENDING_MSG: + hdr = CMI_GET_HEADER(t_info->tgt->outbox); + if (CMI_GET_OPCODE(t_info->tgt->outbox) == + CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2) { + pr_debug("%s: session_id: %u, state: %d,%d, event received\n", + __func__, CMI_HDR_GET_SESSION_ID(hdr), + t_info->state, t_info->substate); + temp_node.command = CPE_CMD_PROC_INCOMING_MSG; + temp_node.data = NULL; + t_info->cpe_process_command(&temp_node); + break; + } + + m = (struct cpe_send_msg *)t_info->pending; + + switch (t_info->substate) { + case CPE_SS_MSG_REQUEST_ACCESS: + cpe_send_cmd_to_thread(t_info, + CPE_CMD_SEND_TRANS_MSG, m, true); + break; + + case CPE_SS_MSG_SEND_INBOX: + if (cpe_is_cmd_clk_req(t_info->tgt->outbox)) + cpe_process_clk_change_req(t_info); + else + cpe_send_cmd_to_thread(t_info, + CPE_CMD_SEND_MSG_COMPLETE, m, true); + break; + + default: + pr_debug("%s: unhandled substate %d for state %d\n", + __func__, t_info->state, t_info->substate); + break; + } + break; + + case CPE_STATE_IDLE: + pr_debug("%s: Message received, notifying client\n", + __func__); + temp_node.command = CPE_CMD_PROC_INCOMING_MSG; + temp_node.data = NULL; + t_info->cpe_process_command(&temp_node); + break; + + default: + pr_debug("%s: unhandled state %d\n", + __func__, t_info->state); + break; + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); +} + + +static void broacast_boot_failed(void) +{ + struct cpe_info *t_info = cpe_d.cpe_default_handle; + struct cpe_svc_notification payload; + + payload.event = CPE_SVC_BOOT_FAILED; + payload.result = CPE_SVC_FAILED; + payload.payload = NULL; + if (t_info) + payload.private_data = + t_info->client_context; + cpe_broadcast_notification(t_info, &payload); +} + +static enum cpe_svc_result broadcast_boot_event( + struct cpe_info *t_info) +{ + struct cpe_svc_notification payload; + + payload.event = CPE_SVC_ONLINE; + payload.result = CPE_SVC_SUCCESS; + payload.payload = NULL; + if (t_info) + payload.private_data = + t_info->client_context; + cpe_broadcast_notification(t_info, &payload); + + return CPE_SVC_SUCCESS; +} + +static enum cpe_process_result cpe_boot_initialize(struct cpe_info *t_info, + enum cpe_svc_result *cpe_rc) +{ + enum cpe_process_result rc = CPE_SVC_FAILED; + struct cpe_svc_notification payload; + struct cmi_core_svc_event_system_boot *p = NULL; + + if (CMI_GET_OPCODE(t_info->tgt->outbox) != + CPE_CORE_SVC_EVENT_SYSTEM_BOOT) { + broacast_boot_failed(); + return rc; + } + + p = (struct cmi_core_svc_event_system_boot *) + CMI_GET_PAYLOAD(t_info->tgt->outbox); + if (p->status != CPE_BOOT_SUCCESS) { + pr_err("%s: cpe boot failed, status = %d\n", + __func__, p->status); + broacast_boot_failed(); + return rc; + } + + /* boot was successful */ + if (p->version == + CPE_CORE_VERSION_SYSTEM_BOOT_EVENT) { + cpe_d.cpe_debug_vector.debug_address = + p->sfr_buff_address; + cpe_d.cpe_debug_vector.debug_buffer_size = + p->sfr_buff_size; + cpe_d.cpe_debug_vector.status = p->status; + payload.event = CPE_SVC_BOOT; + payload.result = CPE_SVC_SUCCESS; + payload.payload = (void *)&cpe_d.cpe_debug_vector; + payload.private_data = t_info->client_context; + cpe_broadcast_notification(t_info, &payload); + } + cpe_change_state(t_info, CPE_STATE_BOOTING, + CPE_SS_BOOT_INIT); + (*cpe_rc) = cpe_send_msg_to_inbox(t_info, + CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC, NULL); + rc = CPE_PROC_SUCCESS; + return rc; +} + +static void cpe_svc_core_cmi_handler( + const struct cmi_api_notification *parameter) +{ + struct cmi_hdr *hdr; + + if (!parameter) + return; + + pr_debug("%s: event = %d\n", + __func__, parameter->event); + + if (parameter->event != CMI_API_MSG) + return; + + hdr = (struct cmi_hdr *) parameter->message; + + if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) { + struct cmi_basic_rsp_result *result; + + result = (struct cmi_basic_rsp_result *) + ((u8 *)parameter->message) + (sizeof(*hdr)); + if (result->status) + pr_err("%s: error response, error code = %u\n", + __func__, result->status); + complete(&cpe_d.cpe_default_handle->core_svc_cmd_compl); + } +} + +static void cpe_clk_plan_work(struct work_struct *work) +{ + struct cpe_info *t_info = NULL; + size_t size = 0; + struct cpe_svc_cfg_clk_plan plan; + u8 *cmi_msg; + struct cmi_hdr *hdr; + int rc; + + t_info = container_of(work, struct cpe_info, clk_plan_work); + if (!t_info) { + pr_err("%s: Invalid handle for cpe_info\n", + __func__); + return; + } + + /* Register the core service */ + cpe_d.cpe_cmi_handle = cmi_register( + cpe_svc_core_cmi_handler, + CMI_CPE_CORE_SERVICE_ID); + + /* send the clk plan command */ + if (!cpe_d.cpe_query_freq_plans_cb) { + pr_err("%s: No support for querying clk plans\n", + __func__); + return; + } + + cpe_d.cpe_query_freq_plans_cb(cpe_d.cdc_priv, &plan); + size = sizeof(plan.current_clk_feq) + + sizeof(plan.num_clk_freqs); + size += plan.num_clk_freqs * + sizeof(plan.clk_freqs[0]); + cmi_msg = kzalloc(size + sizeof(struct cmi_hdr), + GFP_KERNEL); + if (!cmi_msg) + return; + + hdr = (struct cmi_hdr *) cmi_msg; + CMI_HDR_SET_OPCODE(hdr, + CPE_CORE_SVC_CMD_CFG_CLK_PLAN); + CMI_HDR_SET_SERVICE(hdr, CMI_CPE_CORE_SERVICE_ID); + CMI_HDR_SET_SESSION(hdr, 1); + CMI_HDR_SET_VERSION(hdr, CMI_DRIVER_SUPPORTED_VERSION); + CMI_HDR_SET_PAYLOAD_SIZE(hdr, size); + memcpy(CMI_GET_PAYLOAD(cmi_msg), &plan, + size); + cmi_send_msg(cmi_msg); + + /* Wait for clk plan command to complete */ + rc = wait_for_completion_timeout(&t_info->core_svc_cmd_compl, + (10 * HZ)); + if (!rc) { + pr_err("%s: clk plan cmd timed out\n", + __func__); + goto cmd_fail; + } + + /* clk plan cmd is successful, send start notification */ + if (t_info->cpe_start_notification) + t_info->cpe_start_notification(t_info); + else + pr_err("%s: no start notification\n", + __func__); + +cmd_fail: + kfree(cmi_msg); + cmi_deregister(cpe_d.cpe_cmi_handle); +} + +static enum cpe_process_result cpe_boot_complete( + struct cpe_info *t_info) +{ + struct cmi_core_svc_cmdrsp_shared_mem_alloc *p = NULL; + + if (CMI_GET_OPCODE(t_info->tgt->outbox) != + CPE_CORE_SVC_CMDRSP_SHARED_MEM_ALLOC) { + broacast_boot_failed(); + return CPE_PROC_FAILED; + } + + p = (struct cmi_core_svc_cmdrsp_shared_mem_alloc *) + CMI_GET_PAYLOAD(t_info->tgt->outbox); + cpe_d.cpe_msg_buffer = p->addr; + + if (cpe_d.cpe_msg_buffer == 0) { + pr_err("%s: Invalid cpe buffer for message\n", + __func__); + broacast_boot_failed(); + return CPE_PROC_FAILED; + } + + cpe_change_state(t_info, CPE_STATE_IDLE, CPE_SS_IDLE); + cpe_create_worker_thread(t_info); + + if (t_info->codec_id != CPE_SVC_CODEC_TOMTOM) { + schedule_work(&t_info->clk_plan_work); + } else { + if (t_info->cpe_start_notification) + t_info->cpe_start_notification(t_info); + else + pr_err("%s: no start notification\n", + __func__); + } + + pr_debug("%s: boot complete\n", __func__); + return CPE_SVC_SUCCESS; +} + +static enum cpe_process_result cpe_process_send_msg( + struct cpe_info *t_info, + enum cpe_svc_result *cpe_rc, + struct cpe_command_node *command_node) +{ + enum cpe_process_result rc = CPE_PROC_SUCCESS; + struct cpe_send_msg *m = + (struct cpe_send_msg *)command_node->data; + u32 size = m->size; + + if (t_info->pending) { + pr_debug("%s: message queued\n", __func__); + *cpe_rc = CPE_SVC_SUCCESS; + return CPE_PROC_QUEUED; + } + + pr_debug("%s: Send CMI message, size = %u\n", + __func__, size); + + if (size <= t_info->tgt->tgt_get_cpe_info()->inbox_size) { + pr_debug("%s: Msg fits mailbox, size %u\n", + __func__, size); + cpe_change_state(t_info, CPE_STATE_SENDING_MSG, + CPE_SS_MSG_SEND_INBOX); + t_info->pending = m; + *cpe_rc = cpe_send_msg_to_inbox(t_info, 0, m); + } else if (size < CPE_MSG_BUFFER_SIZE) { + m->address = cpe_d.cpe_msg_buffer; + pr_debug("%s: Message req CMI mem access\n", + __func__); + t_info->pending = m; + cpe_change_state(t_info, CPE_STATE_SENDING_MSG, + CPE_SS_MSG_REQUEST_ACCESS); + *cpe_rc = cpe_send_msg_to_inbox(t_info, + CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ, m); + } else { + pr_debug("%s: Invalid msg size %u\n", + __func__, size); + cpe_command_cleanup(command_node); + rc = CPE_PROC_FAILED; + cpe_change_state(t_info, CPE_STATE_IDLE, + CPE_SS_IDLE); + } + + return rc; +} + +static enum cpe_process_result cpe_process_incoming( + struct cpe_info *t_info) +{ + enum cpe_process_result rc = CPE_PROC_FAILED; + struct cmi_hdr *hdr; + + hdr = CMI_GET_HEADER(t_info->tgt->outbox); + + if (CMI_HDR_GET_SERVICE(hdr) == + CMI_CPE_CORE_SERVICE_ID) { + pr_debug("%s: core service message received\n", + __func__); + + switch (CMI_GET_OPCODE(t_info->tgt->outbox)) { + case CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST: + cpe_process_clk_change_req(t_info); + rc = CPE_PROC_SUCCESS; + break; + case CMI_MSG_TRANSPORT: + pr_debug("%s: transport msg received\n", + __func__); + rc = CPE_PROC_SUCCESS; + break; + case CPE_CMI_BASIC_RSP_OPCODE: + pr_debug("%s: received basic rsp\n", + __func__); + rc = CPE_PROC_SUCCESS; + break; + default: + pr_debug("%s: unknown message received\n", + __func__); + break; + } + } else { + /* if service id if for a CMI client, notify client */ + pr_debug("%s: Message received, notifying client\n", + __func__); + cpe_notify_cmi_client(t_info, + t_info->tgt->outbox, CPE_SVC_SUCCESS); + rc = CPE_PROC_SUCCESS; + } + + return rc; +} + +static enum cpe_process_result cpe_process_kill_thread( + struct cpe_info *t_info, + struct cpe_command_node *command_node) +{ + struct cpe_svc_notification payload; + + cpe_d.cpe_msg_buffer = 0; + payload.result = CPE_SVC_SHUTTING_DOWN; + payload.event = CPE_SVC_OFFLINE; + payload.payload = NULL; + payload.private_data = t_info->client_context; + /* + * Make state as offline before broadcasting + * the message to clients. + */ + cpe_change_state(t_info, CPE_STATE_OFFLINE, + CPE_SS_IDLE); + cpe_broadcast_notification(t_info, &payload); + + return CPE_PROC_KILLED; +} + +static enum cpe_process_result cpe_mt_process_cmd( + struct cpe_command_node *command_node) +{ + struct cpe_info *t_info = cpe_d.cpe_default_handle; + enum cpe_svc_result cpe_rc = CPE_SVC_SUCCESS; + enum cpe_process_result rc = CPE_PROC_SUCCESS; + struct cpe_send_msg *m; + struct cmi_hdr *hdr; + u8 service = 0; + u8 retries = 0; + + if (!t_info || !command_node) { + pr_err("%s: Invalid handle/command node\n", + __func__); + return CPE_PROC_FAILED; + } + + pr_debug("%s: cmd = %u\n", __func__, command_node->command); + + cpe_rc = cpe_is_command_valid(t_info, command_node->command); + + if (cpe_rc != CPE_SVC_SUCCESS) { + pr_err("%s: Invalid command %d, err = %d\n", + __func__, command_node->command, cpe_rc); + return CPE_PROC_FAILED; + } + + switch (command_node->command) { + + case CPE_CMD_BOOT_INITIALIZE: + rc = cpe_boot_initialize(t_info, &cpe_rc); + break; + + case CPE_CMD_BOOT_COMPLETE: + rc = cpe_boot_complete(t_info); + break; + + case CPE_CMD_SEND_MSG: + rc = cpe_process_send_msg(t_info, &cpe_rc, + command_node); + break; + + case CPE_CMD_SEND_TRANS_MSG: + m = (struct cpe_send_msg *)command_node->data; + + while (retries < CPE_SVC_INACTIVE_STATE_RETRIES_MAX) { + if (t_info->tgt->tgt_is_active()) { + ++retries; + /* Wait for CPE to be inactive */ + usleep_range(5000, 5100); + } else { + break; + } + } + + pr_debug("%s: cpe inactive after %d attempts\n", + __func__, retries); + + cpe_change_state(t_info, CPE_STATE_SENDING_MSG, + CPE_SS_MSG_SEND_INBOX); + rc = cpe_send_msg_to_inbox(t_info, 0, m); + break; + + case CPE_CMD_SEND_MSG_COMPLETE: + hdr = CMI_GET_HEADER(t_info->tgt->outbox); + service = CMI_HDR_GET_SERVICE(hdr); + pr_debug("%s: msg send success, notifying clients\n", + __func__); + cpe_command_cleanup(command_node); + t_info->pending = NULL; + cpe_change_state(t_info, + CPE_STATE_IDLE, CPE_SS_IDLE); + cpe_notify_cmi_client(t_info, + t_info->tgt->outbox, CPE_SVC_SUCCESS); + break; + + case CPE_CMD_PROC_INCOMING_MSG: + rc = cpe_process_incoming(t_info); + break; + + case CPE_CMD_KILL_THREAD: + rc = cpe_process_kill_thread(t_info, command_node); + break; + + default: + pr_err("%s: unhandled cpe cmd = %d\n", + __func__, command_node->command); + break; + } + + if (cpe_rc != CPE_SVC_SUCCESS) { + pr_err("%s: failed to execute command\n", __func__); + if (t_info->pending) { + m = (struct cpe_send_msg *)t_info->pending; + cpe_notify_cmi_client(t_info, m->payload, + CPE_SVC_FAILED); + t_info->pending = NULL; + } + + cpe_command_cleanup(command_node); + rc = CPE_PROC_FAILED; + cpe_change_state(t_info, CPE_STATE_IDLE, + CPE_SS_IDLE); + } + + return rc; +} + +static enum cpe_svc_result cpe_mt_validate_cmd( + const struct cpe_info *t_info, + enum cpe_command command) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + if ((t_info == NULL) || t_info->initialized == false) { + pr_err("%s: cpe service is not ready\n", + __func__); + return CPE_SVC_NOT_READY; + } + + switch (t_info->state) { + case CPE_STATE_UNINITIALIZED: + case CPE_STATE_INITIALIZED: + switch (command) { + case CPE_CMD_RESET: + case CPE_CMD_DL_SEGMENT: + case CPE_CMD_RAMDUMP: + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_KILL_THREAD: + case CPE_CMD_DEINITIALIZE: + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_SUCCESS; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + case CPE_STATE_DOWNLOADING: + switch (command) { + case CPE_CMD_RESET: + case CPE_CMD_DL_SEGMENT: + case CPE_CMD_BOOT: + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_SUCCESS; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + case CPE_STATE_BOOTING: + switch (command) { + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_BOOT_INITIALIZE: + case CPE_CMD_BOOT_COMPLETE: + case CPE_CMD_SHUTDOWN: + rc = CPE_SVC_SUCCESS; + break; + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_BUSY; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + case CPE_STATE_IDLE: + switch (command) { + case CPE_CMD_SEND_MSG: + case CPE_CMD_SEND_TRANS_MSG: + case CPE_CMD_SEND_MSG_COMPLETE: + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_RESET: + case CPE_CMD_SHUTDOWN: + case CPE_CMD_KILL_THREAD: + case CPE_CMD_PROC_INCOMING_MSG: + rc = CPE_SVC_SUCCESS; + break; + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_BUSY; + break; + default: + rc = CPE_SVC_FAILED; + break; + } + break; + + case CPE_STATE_SENDING_MSG: + switch (command) { + case CPE_CMD_SEND_MSG: + case CPE_CMD_SEND_TRANS_MSG: + case CPE_CMD_SEND_MSG_COMPLETE: + case CPE_CMD_PROCESS_IRQ: + case CPE_CMD_SHUTDOWN: + case CPE_CMD_KILL_THREAD: + case CPE_CMD_PROC_INCOMING_MSG: + rc = CPE_SVC_SUCCESS; + break; + case CPE_CMD_FTM_TEST: + rc = CPE_SVC_BUSY; + break; + default: + rc = CPE_SVC_FAILED; + break; + } + break; + + case CPE_STATE_OFFLINE: + switch (command) { + case CPE_CMD_RESET: + case CPE_CMD_RAMDUMP: + case CPE_CMD_KILL_THREAD: + rc = CPE_SVC_SUCCESS; + break; + default: + rc = CPE_SVC_NOT_READY; + break; + } + break; + + default: + pr_debug("%s: unhandled state %d\n", + __func__, t_info->state); + break; + } + + if (rc != CPE_SVC_SUCCESS) + pr_err("%s: invalid command %d, state = %d\n", + __func__, command, t_info->state); + return rc; +} + +void *cpe_svc_initialize( + void irq_control_callback(u32 enable), + const void *codec_info, void *context) +{ + struct cpe_info *t_info = NULL; + const struct cpe_svc_hw_cfg *cap = NULL; + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_svc_init_param *init_context = + (struct cpe_svc_init_param *) context; + void *client_context = NULL; + + if (cpe_d.cpe_default_handle && + cpe_d.cpe_default_handle->initialized == true) + return (void *)cpe_d.cpe_default_handle; + cpe_d.cpe_query_freq_plans_cb = NULL; + cpe_d.cpe_change_freq_plan_cb = NULL; + + if (context) { + client_context = init_context->context; + switch (init_context->version) { + case CPE_SVC_INIT_PARAM_V1: + cpe_d.cpe_query_freq_plans_cb = + init_context->query_freq_plans_cb; + cpe_d.cpe_change_freq_plan_cb = + init_context->change_freq_plan_cb; + break; + default: + break; + } + } + + if (!cpe_d.cpe_default_handle) { + cpe_d.cpe_default_handle = kzalloc(sizeof(struct cpe_info), + GFP_KERNEL); + if (!cpe_d.cpe_default_handle) + goto err_register; + + memset(cpe_d.cpe_default_handle, 0, + sizeof(struct cpe_info)); + } + + t_info = cpe_d.cpe_default_handle; + t_info->client_context = client_context; + + INIT_LIST_HEAD(&t_info->client_list); + cpe_d.cdc_priv = client_context; + INIT_WORK(&t_info->clk_plan_work, cpe_clk_plan_work); + init_completion(&t_info->core_svc_cmd_compl); + + t_info->tgt = kzalloc(sizeof(struct cpe_svc_tgt_abstraction), + GFP_KERNEL); + if (!t_info->tgt) + goto err_tgt_alloc; + t_info->codec_id = + ((struct cpe_svc_codec_info_v1 *) codec_info)->id; + + rc = cpe_svc_tgt_init((struct cpe_svc_codec_info_v1 *)codec_info, + t_info->tgt); + + if (rc != CPE_SVC_SUCCESS) + goto err_tgt_init; + + cap = t_info->tgt->tgt_get_cpe_info(); + + memset(t_info->tgt->outbox, 0, cap->outbox_size); + memset(t_info->tgt->inbox, 0, cap->inbox_size); + mutex_init(&t_info->msg_lock); + cpe_d.cpe_irq_control_callback = irq_control_callback; + t_info->cpe_process_command = cpe_mt_process_cmd; + t_info->cpe_cmd_validate = cpe_mt_validate_cmd; + t_info->cpe_start_notification = broadcast_boot_event; + mutex_init(&cpe_d.cpe_api_mutex); + mutex_init(&cpe_d.cpe_svc_lock); + pr_debug("%s: cpe services initialized\n", __func__); + t_info->state = CPE_STATE_INITIALIZED; + t_info->initialized = true; + + return t_info; + +err_tgt_init: + kfree(t_info->tgt); + +err_tgt_alloc: + kfree(cpe_d.cpe_default_handle); + cpe_d.cpe_default_handle = NULL; + +err_register: + return NULL; +} + +enum cpe_svc_result cpe_svc_deinitialize(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_DEINITIALIZE); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Invalid command %d\n", + __func__, CPE_CMD_DEINITIALIZE); + return rc; + } + + if (cpe_d.cpe_default_handle == t_info) + cpe_d.cpe_default_handle = NULL; + + t_info->tgt->tgt_deinit(t_info->tgt); + cpe_change_state(t_info, CPE_STATE_UNINITIALIZED, + CPE_SS_IDLE); + mutex_destroy(&t_info->msg_lock); + kfree(t_info->tgt); + kfree(t_info); + mutex_destroy(&cpe_d.cpe_api_mutex); + mutex_destroy(&cpe_d.cpe_svc_lock); + + return rc; +} + +void *cpe_svc_register(void *cpe_handle, + void (*notification_callback) + (const struct cpe_svc_notification *parameter), + u32 mask, const char *name) +{ + void *reg_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!cpe_d.cpe_default_handle) { + cpe_d.cpe_default_handle = kzalloc(sizeof(struct cpe_info), + GFP_KERNEL); + if (!cpe_d.cpe_default_handle) { + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return NULL; + } + + memset(cpe_d.cpe_default_handle, 0, + sizeof(struct cpe_info)); + } + + if (!cpe_handle) + cpe_handle = cpe_d.cpe_default_handle; + + reg_handle = cpe_register_generic((struct cpe_info *)cpe_handle, + notification_callback, + NULL, + mask, CPE_NO_SERVICE, name); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return reg_handle; +} + +enum cpe_svc_result cpe_svc_deregister(void *cpe_handle, void *reg_handle) +{ + enum cpe_svc_result rc; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!cpe_handle) + cpe_handle = cpe_d.cpe_default_handle; + + rc = cpe_deregister_generic((struct cpe_info *)cpe_handle, + reg_handle); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_download_segment(void *cpe_handle, + const struct cpe_svc_mem_segment *segment) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_DL_SEGMENT); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_DL_SEGMENT); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + cpe_toggle_irq_notification(t_info, false); + t_info->state = CPE_STATE_DOWNLOADING; + t_info->substate = CPE_SS_DL_DOWNLOADING; + rc = t_info->tgt->tgt_write_ram(t_info, segment); + cpe_toggle_irq_notification(t_info, true); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_boot(void *cpe_handle, int debug_mode) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_BOOT); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_BOOT); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + if (rc == CPE_SVC_SUCCESS) { + t_info->tgt->tgt_boot(debug_mode); + t_info->state = CPE_STATE_BOOTING; + t_info->substate = CPE_SS_BOOT; + pr_debug("%s: cpe service booting\n", + __func__); + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cpe_svc_result cpe_svc_process_irq(void *cpe_handle, u32 cpe_irq) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + cpe_toggle_irq_notification(t_info, false); + cpe_process_irq_int(cpe_irq, t_info); + cpe_toggle_irq_notification(t_info, true); + + return rc; +} + +enum cpe_svc_result cpe_svc_route_notification(void *cpe_handle, + enum cpe_svc_module module, enum cpe_svc_route_dest dest) +{ + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + enum cpe_svc_result rc = CPE_SVC_NOT_READY; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + rc = t_info->tgt->tgt_route_notification(module, dest); + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + struct cpe_command_node *n = NULL; + struct cpe_command_node kill_cmd; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_SHUTDOWN); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_SHUTDOWN); + return rc; + } + + while (!list_empty(&t_info->main_queue)) { + n = list_first_entry(&t_info->main_queue, + struct cpe_command_node, list); + + if (n->command == CPE_CMD_SEND_MSG) { + cpe_notify_cmi_client(t_info, (u8 *)n->data, + CPE_SVC_SHUTTING_DOWN); + } + /* + * Since command cannot be processed, + * delete it from the list and perform cleanup + */ + list_del(&n->list); + cpe_command_cleanup(n); + kfree(n); + } + + pr_debug("%s: cpe service OFFLINE state\n", __func__); + + t_info->state = CPE_STATE_OFFLINE; + t_info->substate = CPE_SS_IDLE; + + memset(&kill_cmd, 0, sizeof(kill_cmd)); + kill_cmd.command = CPE_CMD_KILL_THREAD; + + if (t_info->pending) { + struct cpe_send_msg *m = + (struct cpe_send_msg *)t_info->pending; + cpe_notify_cmi_client(t_info, m->payload, + CPE_SVC_SHUTTING_DOWN); + kfree(t_info->pending); + t_info->pending = NULL; + } + + cpe_cleanup_worker_thread(t_info); + t_info->cpe_process_command(&kill_cmd); + + return rc; +} + +enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + rc = __cpe_svc_shutdown(cpe_handle); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cpe_svc_result cpe_svc_reset(void *cpe_handle) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_RESET); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_RESET); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + if (t_info && t_info->tgt) { + rc = t_info->tgt->tgt_reset(); + pr_debug("%s: cpe services in INITIALIZED state\n", + __func__); + t_info->state = CPE_STATE_INITIALIZED; + t_info->substate = CPE_SS_IDLE; + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_ramdump(void *cpe_handle, + struct cpe_svc_mem_segment *buffer) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_RAMDUMP); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_RAMDUMP); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; + } + + if (t_info->tgt) { + rc = t_info->tgt->tgt_read_ram(t_info, buffer); + } else { + pr_err("%s: cpe service not ready\n", __func__); + rc = CPE_SVC_NOT_READY; + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +enum cpe_svc_result cpe_svc_set_debug_mode(void *cpe_handle, u32 mode) +{ + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + enum cpe_svc_result rc = CPE_SVC_INVALID_HANDLE; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + rc = t_info->tgt->tgt_set_debug_mode(mode); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return rc; +} + +const struct cpe_svc_hw_cfg *cpe_svc_get_hw_cfg(void *cpe_handle) +{ + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + return t_info->tgt->tgt_get_cpe_info(); + + return NULL; +} + +void *cmi_register( + void notification_callback( + const struct cmi_api_notification *parameter), + u32 service) +{ + void *reg_handle = NULL; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + reg_handle = cpe_register_generic(cpe_d.cpe_default_handle, + NULL, + notification_callback, + (CPE_SVC_CMI_MSG | CPE_SVC_OFFLINE | + CPE_SVC_ONLINE), + service, + "CMI_CLIENT"); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + + return reg_handle; +} + +enum cmi_api_result cmi_deregister(void *reg_handle) +{ + u32 clients = 0; + struct cpe_notif_node *n = NULL; + enum cmi_api_result rc = CMI_API_SUCCESS; + struct cpe_svc_notification payload; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + rc = (enum cmi_api_result) cpe_deregister_generic( + cpe_d.cpe_default_handle, reg_handle); + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + list_for_each_entry(n, &cpe_d.cpe_default_handle->client_list, list) { + if (n->mask & CPE_SVC_CMI_MSG) + clients++; + } + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); + + if (clients == 0) { + payload.event = CPE_SVC_CMI_CLIENTS_DEREG; + payload.payload = NULL; + payload.result = CPE_SVC_SUCCESS; + cpe_broadcast_notification(cpe_d.cpe_default_handle, &payload); + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cmi_api_result cmi_send_msg(void *message) +{ + enum cmi_api_result rc = CMI_API_SUCCESS; + struct cpe_send_msg *msg = NULL; + struct cmi_hdr *hdr; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + hdr = CMI_GET_HEADER(message); + msg = kzalloc(sizeof(struct cpe_send_msg), + GFP_ATOMIC); + if (!msg) { + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return CPE_SVC_NO_MEMORY; + } + + if (CMI_HDR_GET_OBM_FLAG(hdr) == CMI_OBM_FLAG_OUT_BAND) + msg->isobm = 1; + else + msg->isobm = 0; + + msg->size = sizeof(struct cmi_hdr) + + CMI_HDR_GET_PAYLOAD_SIZE(hdr); + + msg->payload = kzalloc(msg->size, GFP_ATOMIC); + if (!msg->payload) { + kfree(msg); + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return CPE_SVC_NO_MEMORY; + } + + msg->address = 0; + memcpy((void *)msg->payload, message, msg->size); + + rc = (enum cmi_api_result) cpe_send_cmd_to_thread( + cpe_d.cpe_default_handle, + CPE_CMD_SEND_MSG, + (void *)msg, false); + + if (rc != 0) { + pr_err("%s: Failed to queue message\n", __func__); + kfree(msg->payload); + kfree(msg); + } + + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +enum cpe_svc_result cpe_svc_ftm_test(void *cpe_handle, u32 *status) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + struct cpe_svc_mem_segment backup_seg; + struct cpe_svc_mem_segment waiti_seg; + u8 *backup_data = NULL; + + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + rc = cpe_is_command_valid(t_info, CPE_CMD_FTM_TEST); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: cmd validation fail, cmd = %d\n", + __func__, CPE_CMD_FTM_TEST); + goto fail_cmd; + } + + if (t_info && t_info->tgt) { + backup_data = kzalloc( + t_info->tgt->tgt_waiti_info->tgt_waiti_size, + GFP_KERNEL); + + /* CPE reset */ + rc = t_info->tgt->tgt_reset(); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: CPE reset fail! err = %d\n", + __func__, rc); + goto err_return; + } + + /* Back up the 4 byte IRAM data first */ + backup_seg.type = CPE_SVC_INSTRUCTION_MEM; + backup_seg.cpe_addr = + t_info->tgt->tgt_get_cpe_info()->IRAM_offset; + backup_seg.size = t_info->tgt->tgt_waiti_info->tgt_waiti_size; + backup_seg.data = backup_data; + + pr_debug("%s: Backing up IRAM data from CPE\n", + __func__); + + rc = t_info->tgt->tgt_read_ram(t_info, &backup_seg); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to backup CPE IRAM data, err = %d\n", + __func__, rc); + goto err_return; + } + + pr_debug("%s: Complete backing up IRAM data from CPE\n", + __func__); + + /* Write the WAITI instruction data */ + waiti_seg.type = CPE_SVC_INSTRUCTION_MEM; + waiti_seg.cpe_addr = + t_info->tgt->tgt_get_cpe_info()->IRAM_offset; + waiti_seg.size = t_info->tgt->tgt_waiti_info->tgt_waiti_size; + waiti_seg.data = t_info->tgt->tgt_waiti_info->tgt_waiti_data; + + rc = t_info->tgt->tgt_write_ram(t_info, &waiti_seg); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to write the WAITI data, err = %d\n", + __func__, rc); + goto restore_iram; + } + + /* Boot up cpe to execute the WAITI instructions */ + rc = t_info->tgt->tgt_boot(1); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to boot CPE, err = %d\n", + __func__, rc); + goto reset; + } + + /* + * 1ms delay is suggested by the hw team to + * wait for cpe to boot up. + */ + usleep_range(1000, 1100); + + /* Check if the cpe init is done after executing the WAITI */ + *status = t_info->tgt->tgt_cpar_init_done(); + +reset: + /* Set the cpe back to reset state */ + rc = t_info->tgt->tgt_reset(); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: CPE reset fail! err = %d\n", + __func__, rc); + goto restore_iram; + } + +restore_iram: + /* Restore the IRAM 4 bytes data */ + rc = t_info->tgt->tgt_write_ram(t_info, &backup_seg); + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Fail to restore the IRAM data, err = %d\n", + __func__, rc); + goto err_return; + } + } + +err_return: + kfree(backup_data); +fail_cmd: + CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api"); + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_boot(int debug_mode) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + if (!debug_mode) + rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_WDOG_CFG, + 0x3F, 0x31); + else + pr_info("%s: CPE in debug mode, WDOG disabled\n", + __func__); + + rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, + 0x02, 0x00); + rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, + 0x0C, 0x04); + rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_CFG, + 0x01, 0x01); + + return rc; +} + +static u32 cpe_tgt_tomtom_is_cpar_init_done(void) +{ + u8 status = 0; + + cpe_register_read(TOMTOM_A_SVASS_STATUS, &status); + return status & 0x01; +} + +static u32 cpe_tgt_tomtom_is_active(void) +{ + u8 status = 0; + + cpe_register_read(TOMTOM_A_SVASS_STATUS, &status); + return status & 0x04; +} + +static enum cpe_svc_result cpe_tgt_tomtom_reset(void) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_WDOG_CFG, + 0x30, 0x00); + + rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_CFG, + 0x01, 0x00); + rc = cpe_update_bits(TOMTOM_A_MEM_LEAKAGE_CTL, + 0x07, 0x03); + rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, + 0x08, 0x08); + rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, + 0x02, 0x02); + return rc; +} + +enum cpe_svc_result cpe_tgt_tomtom_voicetx(bool enable) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 val = 0; + + if (enable) + val = 0x02; + else + val = 0x00; + rc = cpe_update_bits(TOMTOM_A_SVASS_CFG, + 0x02, val); + val = 0; + cpe_register_read(TOMTOM_A_SVASS_CFG, &val); + return rc; +} + +enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable) +{ + + struct cpe_info *t_info = (struct cpe_info *)cpe_handle; + + if (!t_info) + t_info = cpe_d.cpe_default_handle; + + if (t_info->tgt) + return t_info->tgt->tgt_voice_tx_lab(enable); + else + return CPE_SVC_INVALID_HANDLE; +} + +static enum cpe_svc_result cpe_tgt_tomtom_read_mailbox(u8 *buffer, + size_t size) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 cnt = 0; + + if (size >= TOMTOM_A_SVASS_SPE_OUTBOX_SIZE) + size = TOMTOM_A_SVASS_SPE_OUTBOX_SIZE - 1; + for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) { + rc = cpe_register_read(TOMTOM_A_SVASS_SPE_OUTBOX(cnt), + &(buffer[cnt])); + } + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_write_mailbox(u8 *buffer, + size_t size) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 cnt = 0; + + if (size >= TOMTOM_A_SVASS_SPE_INBOX_SIZE) + size = TOMTOM_A_SVASS_SPE_INBOX_SIZE - 1; + for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) { + rc = cpe_register_write(TOMTOM_A_SVASS_SPE_INBOX(cnt), + buffer[cnt]); + } + + if (rc == CPE_SVC_SUCCESS) + rc = cpe_register_write(TOMTOM_A_SVASS_SPE_INBOX_TRG, 1); + + return rc; +} + +static enum cpe_svc_result cpe_get_mem_addr(struct cpe_info *t_info, + const struct cpe_svc_mem_segment *mem_seg, + u32 *addr, u8 *mem) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 offset, mem_sz, address; + u8 mem_type; + + switch (mem_seg->type) { + + case CPE_SVC_DATA_MEM: + mem_type = MEM_ACCESS_DRAM_VAL; + offset = TOMTOM_A_SVASS_SPE_DRAM_OFFSET; + mem_sz = TOMTOM_A_SVASS_SPE_DRAM_SIZE; + break; + + case CPE_SVC_INSTRUCTION_MEM: + mem_type = MEM_ACCESS_IRAM_VAL; + offset = TOMTOM_A_SVASS_SPE_IRAM_OFFSET; + mem_sz = TOMTOM_A_SVASS_SPE_IRAM_SIZE; + break; + + default: + pr_err("%s: Invalid mem type = %u\n", + __func__, mem_seg->type); + return CPE_SVC_INVALID_HANDLE; + } + + if (mem_seg->cpe_addr < offset) { + pr_err("%s: Invalid addr %x for mem type %u\n", + __func__, mem_seg->cpe_addr, mem_type); + return CPE_SVC_INVALID_HANDLE; + } + + address = mem_seg->cpe_addr - offset; + if (address + mem_seg->size > mem_sz) { + pr_err("%s: wrong size %zu, start address %x, mem_type %u\n", + __func__, mem_seg->size, address, mem_type); + return CPE_SVC_INVALID_HANDLE; + } + + (*addr) = address; + (*mem) = mem_type; + + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_read_RAM(struct cpe_info *t_info, + struct cpe_svc_mem_segment *mem_seg) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 mem_reg_val = 0; + u32 cnt = 0; + bool autoinc; + u8 mem = MEM_ACCESS_NONE_VAL; + u32 addr = 0; + u32 ptr_update = true; + + if (!mem_seg) { + pr_err("%s: Invalid mem segment\n", + __func__); + return CPE_SVC_INVALID_HANDLE; + } + + rc = cpe_get_mem_addr(t_info, mem_seg, &addr, &mem); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Cannot obtain address, mem_type %u\n", + __func__, mem_seg->type); + return rc; + } + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); + autoinc = cpe_register_read_autoinc_supported(); + if (autoinc) + mem_reg_val |= 0x04; + + mem_reg_val |= 0x08; + mem_reg_val |= mem; + + do { + if (!autoinc || ptr_update) { + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR0, + (addr & 0xFF)); + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR1, + ((addr >> 8) & 0xFF)); + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR2, + ((addr >> 16) & 0xFF)); + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, + mem_reg_val); + + ptr_update = false; + } + rc = cpe_register_read(TOMTOM_A_SVASS_MEM_BANK, + &mem_seg->data[cnt]); + + if (!autoinc) + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); + } while (++cnt < mem_seg->size); + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); + + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_write_RAM(struct cpe_info *t_info, + const struct cpe_svc_mem_segment *mem_seg) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 mem_reg_val = 0; + u8 mem = MEM_ACCESS_NONE_VAL; + u32 addr = 0; + u8 *temp_ptr = NULL; + u32 temp_size = 0; + bool autoinc; + + if (!mem_seg) { + pr_err("%s: Invalid mem segment\n", + __func__); + return CPE_SVC_INVALID_HANDLE; + } + + rc = cpe_get_mem_addr(t_info, mem_seg, &addr, &mem); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Cannot obtain address, mem_type %u\n", + __func__, mem_seg->type); + return rc; + } + + autoinc = cpe_register_read_autoinc_supported(); + if (autoinc) + mem_reg_val |= 0x04; + mem_reg_val |= mem; + + rc = cpe_update_bits(TOMTOM_A_SVASS_MEM_CTL, + 0x0F, mem_reg_val); + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR0, + (addr & 0xFF)); + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR1, + ((addr >> 8) & 0xFF)); + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR2, + ((addr >> 16) & 0xFF)); + + temp_size = 0; + temp_ptr = mem_seg->data; + + while (temp_size <= mem_seg->size) { + u32 to_write = (mem_seg->size >= temp_size+CHUNK_SIZE) + ? CHUNK_SIZE : (mem_seg->size-temp_size); + + if (t_info->state == CPE_STATE_OFFLINE) { + pr_err("%s: CPE is offline\n", __func__); + return CPE_SVC_FAILED; + } + + cpe_register_write_repeat(TOMTOM_A_SVASS_MEM_BANK, + temp_ptr, to_write); + temp_size += CHUNK_SIZE; + temp_ptr += CHUNK_SIZE; + } + + rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_route_notification( + enum cpe_svc_module module, + enum cpe_svc_route_dest dest) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 ctl_reg_val = 0; + + switch (module) { + case CPE_SVC_LISTEN_PROC: + switch (dest) { + case CPE_SVC_EXTERNAL: + ctl_reg_val = LISTEN_CTL_MSM_VAL; + break; + case CPE_SVC_INTERNAL: + ctl_reg_val = LISTEN_CTL_SPE_VAL; + break; + default: + pr_err("%s: Invalid dest %d\n", + __func__, dest); + return CPE_SVC_FAILED; + } + + rc = cpe_update_bits(TOMTOM_A_SVASS_CFG, + 0x01, ctl_reg_val); + break; + default: + pr_err("%s: Invalid module %d\n", + __func__, module); + rc = CPE_SVC_FAILED; + break; + } + + return rc; +} + +static enum cpe_svc_result cpe_tgt_tomtom_set_debug_mode(u32 enable) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 dbg_reg_val = 0x00; + + if (enable) + dbg_reg_val = 0x08; + rc = cpe_update_bits(TOMTOM_A_SVASS_DEBUG, + 0x08, dbg_reg_val); + return rc; +} + +static const struct cpe_svc_hw_cfg *cpe_tgt_tomtom_get_cpe_info(void) +{ + return &cpe_svc_tomtom_info; +} + +static enum cpe_svc_result cpe_tgt_tomtom_deinit( + struct cpe_svc_tgt_abstraction *param) +{ + kfree(param->inbox); + param->inbox = NULL; + kfree(param->outbox); + param->outbox = NULL; + memset(param, 0, sizeof(struct cpe_svc_tgt_abstraction)); + return CPE_SVC_SUCCESS; +} + +static u8 cpe_tgt_tomtom_waiti_data[] = {0x00, 0x70, 0x00, 0x00}; + +static struct cpe_tgt_waiti_info cpe_tgt_tomtom_waiti_info = { + .tgt_waiti_size = ARRAY_SIZE(cpe_tgt_tomtom_waiti_data), + .tgt_waiti_data = cpe_tgt_tomtom_waiti_data, +}; + +static enum cpe_svc_result cpe_tgt_tomtom_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param) +{ + if (!codec_info) + return CPE_SVC_INVALID_HANDLE; + if (!param) + return CPE_SVC_INVALID_HANDLE; + + if (codec_info->id == CPE_SVC_CODEC_TOMTOM) { + param->tgt_boot = cpe_tgt_tomtom_boot; + param->tgt_cpar_init_done = cpe_tgt_tomtom_is_cpar_init_done; + param->tgt_is_active = cpe_tgt_tomtom_is_active; + param->tgt_reset = cpe_tgt_tomtom_reset; + param->tgt_read_mailbox = cpe_tgt_tomtom_read_mailbox; + param->tgt_write_mailbox = cpe_tgt_tomtom_write_mailbox; + param->tgt_read_ram = cpe_tgt_tomtom_read_RAM; + param->tgt_write_ram = cpe_tgt_tomtom_write_RAM; + param->tgt_route_notification = + cpe_tgt_tomtom_route_notification; + param->tgt_set_debug_mode = cpe_tgt_tomtom_set_debug_mode; + param->tgt_get_cpe_info = cpe_tgt_tomtom_get_cpe_info; + param->tgt_deinit = cpe_tgt_tomtom_deinit; + param->tgt_voice_tx_lab = cpe_tgt_tomtom_voicetx; + param->tgt_waiti_info = &cpe_tgt_tomtom_waiti_info; + + param->inbox = kzalloc(TOMTOM_A_SVASS_SPE_INBOX_SIZE, + GFP_KERNEL); + if (!param->inbox) + return CPE_SVC_NO_MEMORY; + + param->outbox = kzalloc(TOMTOM_A_SVASS_SPE_OUTBOX_SIZE, + GFP_KERNEL); + if (!param->outbox) { + kfree(param->inbox); + return CPE_SVC_NO_MEMORY; + } + } + + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_boot(int debug_mode) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + if (!debug_mode) + rc |= cpe_update_bits( + WCD9335_CPE_SS_WDOG_CFG, + 0x3f, 0x31); + else + pr_info("%s: CPE in debug mode, WDOG disabled\n", + __func__); + + rc |= cpe_register_write(WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 19); + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x04, 0x00); + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x02, 0x02); + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x01, 0x01); + + if (unlikely(rc)) { + pr_err("%s: Failed to boot, err = %d\n", + __func__, rc); + rc = CPE_SVC_FAILED; + } + + return rc; +} + +static u32 cpe_tgt_wcd9335_is_cpar_init_done(void) +{ + u8 temp = 0; + + cpe_register_read(WCD9335_CPE_SS_STATUS, &temp); + return temp & 0x1; +} + +static u32 cpe_tgt_wcd9335_is_active(void) +{ + u8 temp = 0; + + cpe_register_read(WCD9335_CPE_SS_STATUS, &temp); + return temp & 0x4; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_reset(void) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CFG, 0x01, 0x00); + + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN, 0x00); + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, 0x00); + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1, 0x00); + rc |= cpe_register_write( + WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2, 0x00); + + rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x04, 0x04); + + if (unlikely(rc)) { + pr_err("%s: failed to reset cpe, err = %d\n", + __func__, rc); + rc = CPE_SVC_FAILED; + } + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_read_mailbox(u8 *buffer, + size_t size) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 cnt = 0; + + pr_debug("%s: size=%zd\n", __func__, size); + + if (size > WCD9335_CPE_SS_SPE_OUTBOX_SIZE) + size = WCD9335_CPE_SS_SPE_OUTBOX_SIZE; + + for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) + rc = cpe_register_read(WCD9335_CPE_SS_SPE_OUTBOX1(cnt), + &buffer[cnt]); + + rc = cpe_register_write(WCD9335_CPE_SS_OUTBOX1_ACK, 0x01); + + if (unlikely(rc)) { + pr_err("%s: failed to ACK outbox, err = %d\n", + __func__, rc); + rc = CPE_SVC_FAILED; + } + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_write_mailbox(u8 *buffer, + size_t size) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 cnt = 0; + + pr_debug("%s: size = %zd\n", __func__, size); + if (size > WCD9335_CPE_SS_SPE_INBOX_SIZE) + size = WCD9335_CPE_SS_SPE_INBOX_SIZE; + for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) { + rc |= cpe_register_write(WCD9335_CPE_SS_SPE_INBOX1(cnt), + buffer[cnt]); + } + + if (unlikely(rc)) { + pr_err("%s: Error %d writing mailbox registers\n", + __func__, rc); + return rc; + } + + rc = cpe_register_write(WCD9335_CPE_SS_INBOX1_TRG, 1); + return rc; +} + +static enum cpe_svc_result cpe_wcd9335_get_mem_addr(struct cpe_info *t_info, + const struct cpe_svc_mem_segment *mem_seg, + u32 *addr, u8 *mem) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u32 offset, mem_sz, address; + u8 mem_type; + + switch (mem_seg->type) { + case CPE_SVC_DATA_MEM: + mem_type = MEM_ACCESS_DRAM_VAL; + offset = WCD9335_CPE_SS_SPE_DRAM_OFFSET; + mem_sz = WCD9335_CPE_SS_SPE_DRAM_SIZE; + break; + + case CPE_SVC_INSTRUCTION_MEM: + mem_type = MEM_ACCESS_IRAM_VAL; + offset = WCD9335_CPE_SS_SPE_IRAM_OFFSET; + mem_sz = WCD9335_CPE_SS_SPE_IRAM_SIZE; + break; + + default: + pr_err("%s: Invalid mem type = %u\n", + __func__, mem_seg->type); + return CPE_SVC_INVALID_HANDLE; + } + + if (mem_seg->cpe_addr < offset) { + pr_err("%s: Invalid addr %x for mem type %u\n", + __func__, mem_seg->cpe_addr, mem_type); + return CPE_SVC_INVALID_HANDLE; + } + + address = mem_seg->cpe_addr - offset; + if (address + mem_seg->size > mem_sz) { + pr_err("%s: wrong size %zu, start address %x, mem_type %u\n", + __func__, mem_seg->size, address, mem_type); + return CPE_SVC_INVALID_HANDLE; + } + + (*addr) = address; + (*mem) = mem_type; + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_read_RAM(struct cpe_info *t_info, + struct cpe_svc_mem_segment *mem_seg) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 temp = 0; + u32 cnt = 0; + u8 mem = 0x0; + u32 addr = 0; + u32 lastaddr = 0; + u32 ptr_update = true; + bool autoinc; + + if (!mem_seg) { + pr_err("%s: Invalid buffer\n", __func__); + return CPE_SVC_INVALID_HANDLE; + } + + rc = cpe_wcd9335_get_mem_addr(t_info, mem_seg, &addr, &mem); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Cannot obtain address, mem_type %u\n", + __func__, mem_seg->type); + return rc; + } + + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + autoinc = cpe_register_read_autoinc_supported(); + + if (autoinc) + temp = 0x18; + else + temp = 0x10; + + temp |= mem; + + lastaddr = ~addr; + do { + if (!autoinc || (ptr_update)) { + /* write LSB only if modified */ + if ((lastaddr & 0xFF) != (addr & 0xFF)) + rc |= cpe_register_write( + WCD9335_CPE_SS_MEM_PTR_0, + (addr & 0xFF)); + /* write middle byte only if modified */ + if (((lastaddr >> 8) & 0xFF) != ((addr >> 8) & 0xFF)) + rc |= cpe_register_write( + WCD9335_CPE_SS_MEM_PTR_1, + ((addr>>8) & 0xFF)); + /* write MSB only if modified */ + if (((lastaddr >> 16) & 0xFF) != ((addr >> 16) & 0xFF)) + rc |= cpe_register_write( + WCD9335_CPE_SS_MEM_PTR_2, + ((addr>>16) & 0xFF)); + + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, temp); + lastaddr = addr; + addr++; + ptr_update = false; + } + + rc |= cpe_register_read(WCD9335_CPE_SS_MEM_BANK_0, + &mem_seg->data[cnt]); + + if (!autoinc) + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + } while ((++cnt < mem_seg->size) || + (rc != CPE_SVC_SUCCESS)); + + rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + + if (rc) + pr_err("%s: Failed to read registers, err = %d\n", + __func__, rc); + + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_write_RAM(struct cpe_info *t_info, + const struct cpe_svc_mem_segment *mem_seg) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 mem_reg_val = 0; + u8 mem = MEM_ACCESS_NONE_VAL; + u32 addr = 0; + u8 *temp_ptr = NULL; + u32 temp_size = 0; + bool autoinc; + + if (!mem_seg) { + pr_err("%s: Invalid mem segment\n", + __func__); + return CPE_SVC_INVALID_HANDLE; + } + + rc = cpe_wcd9335_get_mem_addr(t_info, mem_seg, &addr, &mem); + + if (rc != CPE_SVC_SUCCESS) { + pr_err("%s: Cannot obtain address, mem_type %u\n", + __func__, mem_seg->type); + return rc; + } + + autoinc = cpe_register_read_autoinc_supported(); + if (autoinc) + mem_reg_val = 0x18; + else + mem_reg_val = 0x10; + + mem_reg_val |= mem; + + rc = cpe_update_bits(WCD9335_CPE_SS_MEM_CTRL, + 0x0F, mem_reg_val); + + rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_0, + (addr & 0xFF)); + rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_1, + ((addr >> 8) & 0xFF)); + + rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_2, + ((addr >> 16) & 0xFF)); + + temp_size = 0; + temp_ptr = mem_seg->data; + + while (temp_size <= mem_seg->size) { + u32 to_write = (mem_seg->size >= temp_size+CHUNK_SIZE) + ? CHUNK_SIZE : (mem_seg->size - temp_size); + + if (t_info->state == CPE_STATE_OFFLINE) { + pr_err("%s: CPE is offline\n", __func__); + return CPE_SVC_FAILED; + } + + cpe_register_write_repeat(WCD9335_CPE_SS_MEM_BANK_0, + temp_ptr, to_write); + temp_size += CHUNK_SIZE; + temp_ptr += CHUNK_SIZE; + } + + rc = cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0); + + if (rc) + pr_err("%s: Failed to write registers, err = %d\n", + __func__, rc); + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_route_notification( + enum cpe_svc_module module, + enum cpe_svc_route_dest dest) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + pr_debug("%s: Module = %d, Destination = %d\n", + __func__, module, dest); + + switch (module) { + case CPE_SVC_LISTEN_PROC: + switch (dest) { + case CPE_SVC_EXTERNAL: + rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x01, 0x01); + break; + case CPE_SVC_INTERNAL: + rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x01, 0x00); + break; + default: + pr_err("%s: Invalid destination %d\n", + __func__, dest); + return CPE_SVC_FAILED; + } + break; + default: + pr_err("%s: Invalid module %d\n", + __func__, module); + rc = CPE_SVC_FAILED; + break; + } + return rc; +} + +static enum cpe_svc_result cpe_tgt_wcd9335_set_debug_mode(u32 enable) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + + pr_debug("%s: enable = %s\n", __func__, + (enable) ? "true" : "false"); + + return rc; +} + +static const struct cpe_svc_hw_cfg *cpe_tgt_wcd9335_get_cpe_info(void) +{ + return &cpe_svc_wcd9335_info; +} + +static enum cpe_svc_result +cpe_tgt_wcd9335_deinit(struct cpe_svc_tgt_abstraction *param) +{ + kfree(param->inbox); + param->inbox = NULL; + kfree(param->outbox); + param->outbox = NULL; + memset(param, 0, sizeof(struct cpe_svc_tgt_abstraction)); + + return CPE_SVC_SUCCESS; +} + +static enum cpe_svc_result + cpe_tgt_wcd9335_voicetx(bool enable) +{ + enum cpe_svc_result rc = CPE_SVC_SUCCESS; + u8 val = 0; + + pr_debug("%s: enable = %u\n", __func__, enable); + if (enable) + val = 0x02; + else + val = 0x00; + + rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x02, val); + val = 0; + cpe_register_read(WCD9335_CPE_SS_CFG, &val); + + return rc; +} + +static u8 cpe_tgt_wcd9335_waiti_data[] = {0x00, 0x70, 0x00, 0x00}; + +static struct cpe_tgt_waiti_info cpe_tgt_wcd9335_waiti_info = { + .tgt_waiti_size = ARRAY_SIZE(cpe_tgt_wcd9335_waiti_data), + .tgt_waiti_data = cpe_tgt_wcd9335_waiti_data, +}; + +static enum cpe_svc_result cpe_tgt_wcd9335_init( + struct cpe_svc_codec_info_v1 *codec_info, + struct cpe_svc_tgt_abstraction *param) +{ + if (!codec_info) + return CPE_SVC_INVALID_HANDLE; + if (!param) + return CPE_SVC_INVALID_HANDLE; + + if (codec_info->id == CPE_SVC_CODEC_WCD9335) { + param->tgt_boot = cpe_tgt_wcd9335_boot; + param->tgt_cpar_init_done = cpe_tgt_wcd9335_is_cpar_init_done; + param->tgt_is_active = cpe_tgt_wcd9335_is_active; + param->tgt_reset = cpe_tgt_wcd9335_reset; + param->tgt_read_mailbox = cpe_tgt_wcd9335_read_mailbox; + param->tgt_write_mailbox = cpe_tgt_wcd9335_write_mailbox; + param->tgt_read_ram = cpe_tgt_wcd9335_read_RAM; + param->tgt_write_ram = cpe_tgt_wcd9335_write_RAM; + param->tgt_route_notification = + cpe_tgt_wcd9335_route_notification; + param->tgt_set_debug_mode = cpe_tgt_wcd9335_set_debug_mode; + param->tgt_get_cpe_info = cpe_tgt_wcd9335_get_cpe_info; + param->tgt_deinit = cpe_tgt_wcd9335_deinit; + param->tgt_voice_tx_lab = cpe_tgt_wcd9335_voicetx; + param->tgt_waiti_info = &cpe_tgt_wcd9335_waiti_info; + + param->inbox = kzalloc(WCD9335_CPE_SS_SPE_INBOX_SIZE, + GFP_KERNEL); + if (!param->inbox) + return CPE_SVC_NO_MEMORY; + + param->outbox = kzalloc(WCD9335_CPE_SS_SPE_OUTBOX_SIZE, + GFP_KERNEL); + if (!param->outbox) { + kfree(param->inbox); + return CPE_SVC_NO_MEMORY; + } + } + + return CPE_SVC_SUCCESS; +} + +MODULE_DESCRIPTION("WCD CPE Services"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wcd_cpe_services.h b/sound/soc/codecs/wcd_cpe_services.h new file mode 100644 index 000000000000..68eb61996a69 --- /dev/null +++ b/sound/soc/codecs/wcd_cpe_services.h @@ -0,0 +1,179 @@ +/* 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 __CPE_SERVICES__ +#define __CPE_SERVICES__ + +#define CPE_IRQ_OUTBOX_IRQ 0x01 +#define CPE_IRQ_MEM_ACCESS_ERROR 0x02 +#define CPE_IRQ_WDOG_BITE 0x04 +#define CPE_IRQ_BUFFER_OVERFLOW 0x08 +#define CPE_IRQ_LAB_OVFUNF 0x10 +#define CPE_IRQ_FLL_LOCK_LOST 0x20 +#define CPE_IRQ_RCO_WDOG_INT 0x40 + +#define EFAILED (MAX_ERRNO - 1) +#define ENOTREADY (MAX_ERRNO - 2) + +#define MAX_SUPPORTED_CLKFREQ 8 +#define CPE_SVC_INIT_PARAM_V1 1 + +enum cpe_svc_result { + CPE_SVC_SUCCESS = 0, + CPE_SVC_FAILED = -EFAILED, + CPE_SVC_NO_MEMORY = -ENOMEM, + CPE_SVC_INVALID_HANDLE = -EINVAL, + CPE_SVC_NOT_READY = -ENOTREADY, + CPE_SVC_SHUTTING_DOWN = -ESHUTDOWN, + CPE_SVC_BUSY = -EBUSY, +}; + +enum cpe_svc_event { + CPE_SVC_CMI_MSG = 0x01, + CPE_SVC_OFFLINE = 0x02, + CPE_SVC_ONLINE = 0x04, + CPE_SVC_BOOT_FAILED = 0x08, + CPE_SVC_READ_COMPLETE = 0x10, + CPE_SVC_READ_ERROR = 0x20, + CPE_SVC_BOOT = 0x40, + CPE_SVC_CMI_CLIENTS_DEREG = 0x100, + CPE_SVC_EVENT_ANCHOR = 0x7FFF +}; + +enum cpe_svc_module { + CPE_SVC_LISTEN_PROC = 1, + CPE_SVC_MODULE_ANCHOR = 0x7F +}; + +enum cpe_svc_route_dest { + CPE_SVC_EXTERNAL = 1, + CPE_SVC_INTERNAL = 2, + CPE_SVC_ROUTE_ANCHOR = 0x7F +}; + +enum cpe_svc_mem_type { + CPE_SVC_DATA_MEM = 1, + CPE_SVC_INSTRUCTION_MEM = 2, + CPE_SVC_IPC_MEM = 3, + CPE_SVC_MEM_TYPE_ANCHOR = 0x7F +}; + +enum cpe_svc_codec_id { + CPE_SVC_CODEC_TOMTOM = 5, + CPE_SVC_CODEC_WCD9335 = 7, + CPE_SVC_CODEC_WCD9326 = 8, + CPE_SVC_CODEC_ID_ANCHOR = 0x7ffffff +}; + +enum cpe_svc_codec_version { + CPE_SVC_CODEC_V1P0 = 1, + CPE_SVC_CODEC_VERSION_ANCHOR = 0x7fffffff +}; + +struct cpe_svc_codec_info_v1 { + u16 major_version;/*must be 1*/ + u16 minor_version;/*must be 0*/ + u32 id; + u32 version; + /*Add 1.1 version fields after this line*/ +}; + +struct cpe_svc_notification { + enum cpe_svc_event event; + enum cpe_svc_result result; + void *payload; + void *private_data; +}; + +struct cpe_svc_msg_payload { + u8 *cmi_msg; +}; + +struct cpe_svc_read_complete { + u8 *buffer; + size_t size; +}; + +struct cpe_svc_boot_event { + u32 debug_address; + size_t debug_buffer_size; + u32 status; +}; + +struct cpe_svc_mem_segment { + enum cpe_svc_mem_type type; + u32 cpe_addr; + size_t size; + u8 *data; +}; + +struct cpe_svc_hw_cfg { + size_t DRAM_size; + u32 DRAM_offset; + size_t IRAM_size; + u32 IRAM_offset; + u8 inbox_size; + u8 outbox_size; +}; + +struct cpe_svc_cfg_clk_plan { + u32 current_clk_feq; + u32 num_clk_freqs; + u32 clk_freqs[MAX_SUPPORTED_CLKFREQ]; +}; + +struct cpe_svc_init_param { + void *context; + u32 version; + void (*query_freq_plans_cb)(void *cdc_priv, + struct cpe_svc_cfg_clk_plan *clk_freq); + void (*change_freq_plan_cb)(void *cdc_priv, + u32 clk_freq); +}; + + +void *cpe_svc_initialize( + void irq_control_callback(u32 enable), + const void *codec_info, void *context); +enum cpe_svc_result cpe_svc_deinitialize(void *cpe_handle); + +void *cpe_svc_register(void *cpe_handle, + void (*notification_callback)( + const struct cpe_svc_notification *parameter), + u32 mask, const char *name); + +enum cpe_svc_result cpe_svc_deregister(void *cpe_handle, void *reg_handle); + +enum cpe_svc_result cpe_svc_download_segment(void *cpe_handle, + const struct cpe_svc_mem_segment *segment); + +enum cpe_svc_result cpe_svc_boot(void *cpe_handle, int debug_mode); + +enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle); + +enum cpe_svc_result cpe_svc_reset(void *cpe_handle); + +enum cpe_svc_result cpe_svc_process_irq(void *cpe_handle, u32 cpe_irq); + +enum cpe_svc_result +cpe_svc_route_notification(void *cpe_handle, enum cpe_svc_module module, + enum cpe_svc_route_dest dest); + +enum cpe_svc_result cpe_svc_ramdump(void *cpe_handle, + struct cpe_svc_mem_segment *buffer); + +enum cpe_svc_result cpe_svc_set_debug_mode(void *cpe_handle, u32 mode); + +const struct cpe_svc_hw_cfg *cpe_svc_get_hw_cfg(void *cpe_handle); +enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable); +enum cpe_svc_result cpe_svc_ftm_test(void *cpe_handle, u32 *status); +#endif /*__CPE_SERVICES__*/ diff --git a/sound/soc/codecs/wcdcal-hwdep.c b/sound/soc/codecs/wcdcal-hwdep.c new file mode 100644 index 000000000000..31eae69b1f4b --- /dev/null +++ b/sound/soc/codecs/wcdcal-hwdep.c @@ -0,0 +1,226 @@ +/* + * 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 "wcdcal-hwdep.h" + +const int cal_size_info[WCD9XXX_MAX_CAL] = { + [WCD9XXX_ANC_CAL] = 16384, + [WCD9XXX_MBHC_CAL] = 4096, + [WCD9XXX_MAD_CAL] = 4096, + [WCD9XXX_VBAT_CAL] = 72, +}; + +const char *cal_name_info[WCD9XXX_MAX_CAL] = { + [WCD9XXX_ANC_CAL] = "anc", + [WCD9XXX_MBHC_CAL] = "mbhc", + [WCD9XXX_MAD_CAL] = "mad", + [WCD9XXX_VBAT_CAL] = "vbat", +}; + +struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data, + enum wcd_cal_type type) +{ + if (!fw_data) { + pr_err("%s: fw_data is NULL\n", __func__); + return NULL; + } + if (type >= WCD9XXX_MAX_CAL || + type < WCD9XXX_MIN_CAL) { + pr_err("%s: wrong cal type sent %d\n", __func__, type); + return NULL; + } + mutex_lock(&fw_data->lock); + if (!test_bit(WCDCAL_RECIEVED, + &fw_data->wcdcal_state[type])) { + pr_err("%s: cal not sent by userspace %d\n", + __func__, type); + mutex_unlock(&fw_data->lock); + return NULL; + } + mutex_unlock(&fw_data->lock); + return fw_data->fw[type]; +} +EXPORT_SYMBOL(wcdcal_get_fw_cal); + +static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw, + struct wcdcal_ioctl_buffer fw_user) +{ + struct fw_info *fw_data = hw->private_data; + struct firmware_cal **fw = fw_data->fw; + void *data; + + if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) { + pr_err("%s: codec didn't set this %d!!\n", + __func__, fw_user.cal_type); + return -EFAULT; + } + if (fw_user.cal_type >= WCD9XXX_MAX_CAL || + fw_user.cal_type < WCD9XXX_MIN_CAL) { + pr_err("%s: wrong cal type sent %d\n", + __func__, fw_user.cal_type); + return -EFAULT; + } + if (fw_user.size > cal_size_info[fw_user.cal_type] || + fw_user.size <= 0) { + pr_err("%s: incorrect firmware size %d for %s\n", + __func__, fw_user.size, + cal_name_info[fw_user.cal_type]); + return -EFAULT; + } + data = fw[fw_user.cal_type]->data; + if (copy_from_user(data, fw_user.buffer, fw_user.size)) + return -EFAULT; + fw[fw_user.cal_type]->size = fw_user.size; + mutex_lock(&fw_data->lock); + set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]); + mutex_unlock(&fw_data->lock); + return 0; +} + +#ifdef CONFIG_COMPAT +struct wcdcal_ioctl_buffer32 { + u32 size; + compat_uptr_t buffer; + enum wcd_cal_type cal_type; +}; + +enum { + SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 = + _IOW('U', 0x1, struct wcdcal_ioctl_buffer32), +}; + +static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg; + struct wcdcal_ioctl_buffer32 fw_user32; + struct wcdcal_ioctl_buffer fw_user_compat; + + if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) { + pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd); + return -ENOIOCTLCMD; + } + if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) { + pr_err("%s: failed to copy\n", __func__); + return -EFAULT; + } + fw_user_compat.size = fw_user32.size; + fw_user_compat.buffer = compat_ptr(fw_user32.buffer); + fw_user_compat.cal_type = fw_user32.cal_type; + return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat); +} +#else +#define wcdcal_hwdep_ioctl_compat NULL +#endif + +static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg; + struct wcdcal_ioctl_buffer fw_user; + + if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) { + pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd); + return -ENOIOCTLCMD; + } + if (copy_from_user(&fw_user, argp, sizeof(fw_user))) { + pr_err("%s: failed to copy\n", __func__); + return -EFAULT; + } + return wcdcal_hwdep_ioctl_shared(hw, fw_user); +} + +static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file) +{ + struct fw_info *fw_data = hw->private_data; + + mutex_lock(&fw_data->lock); + /* clear all the calibrations */ + memset(fw_data->wcdcal_state, 0, + sizeof(fw_data->wcdcal_state)); + mutex_unlock(&fw_data->lock); + return 0; +} + +int wcd_cal_create_hwdep(void *data, int node, struct snd_soc_codec *codec) +{ + char hwname[40]; + struct snd_hwdep *hwdep; + struct firmware_cal **fw; + struct fw_info *fw_data = data; + int err, cal_bit; + + if (!fw_data || !codec) { + pr_err("%s: wrong arguments passed\n", __func__); + return -EINVAL; + } + + fw = fw_data->fw; + snprintf(hwname, strlen("Codec %s"), "Codec %s", + codec->component.name); + err = snd_hwdep_new(codec->component.card->snd_card, + hwname, node, &hwdep); + if (err < 0) { + dev_err(codec->dev, "%s: new hwdep failed %d\n", + __func__, err); + return err; + } + snprintf(hwdep->name, strlen("Codec %s"), "Codec %s", + codec->component.name); + hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC; + hwdep->private_data = fw_data; + hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat; + hwdep->ops.ioctl = wcdcal_hwdep_ioctl; + hwdep->ops.release = wcdcal_hwdep_release; + mutex_init(&fw_data->lock); + + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + set_bit(WCDCAL_UNINITIALISED, + &fw_data->wcdcal_state[cal_bit]); + fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL); + if (!fw[cal_bit]) { + dev_err(codec->dev, "%s: no memory for %s cal\n", + __func__, cal_name_info[cal_bit]); + goto end; + } + } + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit], + GFP_KERNEL); + if (!fw[cal_bit]->data) + goto exit; + set_bit(WCDCAL_INITIALISED, + &fw_data->wcdcal_state[cal_bit]); + } + return 0; +exit: + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + kfree(fw[cal_bit]->data); + fw[cal_bit]->data = NULL; + } +end: + for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) { + kfree(fw[cal_bit]); + fw[cal_bit] = NULL; + } + return -ENOMEM; +} +EXPORT_SYMBOL(wcd_cal_create_hwdep); diff --git a/sound/soc/codecs/wcdcal-hwdep.h b/sound/soc/codecs/wcdcal-hwdep.h new file mode 100644 index 000000000000..632e2f11f323 --- /dev/null +++ b/sound/soc/codecs/wcdcal-hwdep.h @@ -0,0 +1,40 @@ +/* + * 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 __WCD9XXX_HWDEP_H__ +#define __WCD9XXX_HWDEP_H__ +#include + +enum wcd_cal_states { + WCDCAL_UNINITIALISED, + WCDCAL_INITIALISED, + WCDCAL_RECIEVED +}; + +struct fw_info { + struct firmware_cal *fw[WCD9XXX_MAX_CAL]; + DECLARE_BITMAP(cal_bit, WCD9XXX_MAX_CAL); + /* for calibration tracking */ + unsigned long wcdcal_state[WCD9XXX_MAX_CAL]; + struct mutex lock; +}; + +struct firmware_cal { + u8 *data; + size_t size; +}; + +struct snd_soc_codec; +int wcd_cal_create_hwdep(void *fw, int node, struct snd_soc_codec *codec); +struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data, + enum wcd_cal_type type); +#endif /* __WCD9XXX_HWDEP_H__ */ diff --git a/sound/soc/codecs/wsa881x-analog.c b/sound/soc/codecs/wsa881x-analog.c new file mode 100644 index 000000000000..4de962497765 --- /dev/null +++ b/sound/soc/codecs/wsa881x-analog.c @@ -0,0 +1,1446 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wsa881x-analog.h" +#include "wsa881x-temp-sensor.h" +#include "../msm/msm-audio-pinctrl.h" + +#define SPK_GAIN_12DB 4 +#define WIDGET_NAME_MAX_SIZE 80 + +/* + * Private data Structure for wsa881x. All parameters related to + * WSA881X codec needs to be defined here. + */ +struct wsa881x_pdata { + struct regmap *regmap[2]; + struct i2c_client *client[2]; + struct snd_soc_codec *codec; + + /* track wsa881x status during probe */ + int status; + bool boost_enable; + bool visense_enable; + int spk_pa_gain; + struct i2c_msg xfer_msg[2]; + struct mutex xfer_lock; + bool regmap_flag; + bool wsa_active; + int index; + int (*enable_mclk)(struct snd_soc_card *, bool); + struct wsa881x_tz_priv tz_pdata; + int bg_cnt; + int clk_cnt; + int enable_cnt; + int version; + struct mutex bg_lock; + struct mutex res_lock; + struct delayed_work ocp_ctl_work; +}; + +enum { + WSA881X_STATUS_PROBING, + WSA881X_STATUS_I2C, +}; + +#define WSA881X_OCP_CTL_TIMER_SEC 2 +#define WSA881X_OCP_CTL_TEMP_CELSIUS 25 +#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60 + +static int wsa881x_ocp_poll_timer_sec = WSA881X_OCP_CTL_POLL_TIMER_SEC; +module_param(wsa881x_ocp_poll_timer_sec, int, 0664); +MODULE_PARM_DESC(wsa881x_ocp_poll_timer_sec, "timer for ocp ctl polling"); + +static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, + bool enable); + +const char *wsa_tz_names[] = {"wsa881x.0e", "wsa881x.0f"}; + +struct wsa881x_pdata wsa_pdata[MAX_WSA881X_DEVICE]; + +static bool pinctrl_init; + +static int wsa881x_populate_dt_pdata(struct device *dev); +static int wsa881x_reset(struct wsa881x_pdata *pdata, bool enable); +static int wsa881x_startup(struct wsa881x_pdata *pdata); +static int wsa881x_shutdown(struct wsa881x_pdata *pdata); + +static int delay_array_msec[] = {10, 20, 30, 40, 50}; + +static int wsa881x_i2c_addr = -1; +static int wsa881x_probing_count; +static int wsa881x_presence_count; + +static const char * const wsa881x_spk_pa_gain_text[] = { +"POS_13P5_DB", "POS_12_DB", "POS_10P5_DB", "POS_9_DB", "POS_7P5_DB", +"POS_6_DB", "POS_4P5_DB", "POS_3_DB", "POS_1P5_DB", "POS_0_DB"}; + +static const struct soc_enum wsa881x_spk_pa_gain_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa881x_spk_pa_gain_text), + wsa881x_spk_pa_gain_text), +}; + +static int wsa881x_spk_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->spk_pa_gain; + + dev_dbg(codec->dev, "%s: spk_pa_gain = %ld\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int wsa881x_spk_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.integer.value[0] < 0 || + ucontrol->value.integer.value[0] > 0xC) { + dev_err(codec->dev, "%s: Unsupported gain val %ld\n", + __func__, ucontrol->value.integer.value[0]); + return -EINVAL; + } + wsa881x->spk_pa_gain = ucontrol->value.integer.value[0]; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int get_i2c_wsa881x_device_index(u16 reg) +{ + u16 mask = 0x0f00; + int value = 0; + + value = ((reg & mask) >> 8) & 0x000f; + + switch (value) { + case 0: + return 0; + case 1: + return 1; + default: + break; + } + return -EINVAL; +} + +static int wsa881x_i2c_write_device(struct wsa881x_pdata *wsa881x, + unsigned int reg, unsigned int val) +{ + int i = 0, rc = 0; + int wsa881x_index; + struct i2c_msg *msg; + int ret = 0; + int bytes = 1; + u8 reg_addr = 0; + u8 data[bytes + 1]; + + wsa881x_index = get_i2c_wsa881x_device_index(reg); + if (wsa881x_index < 0) { + pr_err("%s:invalid register to write\n", __func__); + return -EINVAL; + } + if (wsa881x->regmap_flag) { + rc = regmap_write(wsa881x->regmap[wsa881x_index], reg, val); + for (i = 0; rc && i < ARRAY_SIZE(delay_array_msec); i++) { + pr_err("Failed writing reg=%u - retry(%d)\n", reg, i); + /* retry after delay of increasing order */ + msleep(delay_array_msec[i]); + rc = regmap_write(wsa881x->regmap[wsa881x_index], + reg, val); + } + if (rc) + pr_err("Failed writing reg=%u rc=%d\n", reg, rc); + else + pr_err("write success register = %x val = %x\n", + reg, val); + } else { + reg_addr = (u8)reg; + msg = &wsa881x->xfer_msg[0]; + msg->addr = wsa881x->client[wsa881x_index]->addr; + msg->len = bytes + 1; + msg->flags = 0; + data[0] = reg; + data[1] = (u8)val; + msg->buf = data; + ret = i2c_transfer(wsa881x->client[wsa881x_index]->adapter, + wsa881x->xfer_msg, 1); + /* Try again if the write fails */ + if (ret != 1) { + ret = i2c_transfer( + wsa881x->client[wsa881x_index]->adapter, + wsa881x->xfer_msg, 1); + if (ret != 1) { + pr_err("failed to write the device\n"); + return ret; + } + } + pr_debug("write success reg = %x val = %x\n", reg, data[1]); + } + return rc; +} + +static int wsa881x_i2c_read_device(struct wsa881x_pdata *wsa881x, + unsigned int reg) +{ + int wsa881x_index; + int i = 0, rc = 0; + unsigned int val; + struct i2c_msg *msg; + int ret = 0; + u8 reg_addr = 0; + u8 dest[5]; + + wsa881x_index = get_i2c_wsa881x_device_index(reg); + if (wsa881x_index < 0) { + pr_err("%s:invalid register to read\n", __func__); + return -EINVAL; + } + if (wsa881x->regmap_flag) { + rc = regmap_read(wsa881x->regmap[wsa881x_index], reg, &val); + for (i = 0; rc && i < ARRAY_SIZE(delay_array_msec); i++) { + pr_err("Failed reading reg=%u - retry(%d)\n", reg, i); + /* retry after delay of increasing order */ + msleep(delay_array_msec[i]); + rc = regmap_read(wsa881x->regmap[wsa881x_index], + reg, &val); + } + if (rc) { + pr_err("Failed reading reg=%u rc=%d\n", reg, rc); + return rc; + } + pr_debug("read success reg = %x val = %x\n", + reg, val); + } else { + reg_addr = (u8)reg; + msg = &wsa881x->xfer_msg[0]; + msg->addr = wsa881x->client[wsa881x_index]->addr; + msg->len = 1; + msg->flags = 0; + msg->buf = ®_addr; + + msg = &wsa881x->xfer_msg[1]; + msg->addr = wsa881x->client[wsa881x_index]->addr; + msg->len = 1; + msg->flags = I2C_M_RD; + msg->buf = dest; + ret = i2c_transfer(wsa881x->client[wsa881x_index]->adapter, + wsa881x->xfer_msg, 2); + + /* Try again if read fails first time */ + if (ret != 2) { + ret = i2c_transfer( + wsa881x->client[wsa881x_index]->adapter, + wsa881x->xfer_msg, 2); + if (ret != 2) { + pr_err("failed to read wsa register:%d\n", + reg); + return ret; + } + } + val = dest[0]; + } + return val; +} + +static unsigned int wsa881x_i2c_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + struct wsa881x_pdata *wsa881x; + unsigned int val; + int ret; + + if (codec == NULL) { + pr_err("%s: invalid codec\n", __func__); + return -EINVAL; + } + wsa881x = snd_soc_codec_get_drvdata(codec); + if (!wsa881x->wsa_active) { + ret = snd_soc_cache_read(codec, reg, &val); + if (ret >= 0) + return val; + dev_err(codec->dev, + "cache read failed for reg: 0x%x ret: %d\n", + reg, ret); + return ret; + } + return wsa881x_i2c_read_device(wsa881x, reg); +} + +static int wsa881x_i2c_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int val) +{ + struct wsa881x_pdata *wsa881x; + int ret = 0; + + if (codec == NULL) { + pr_err("%s: invalid codec\n", __func__); + return -EINVAL; + } + wsa881x = snd_soc_codec_get_drvdata(codec); + if (!wsa881x->wsa_active) { + ret = snd_soc_cache_write(codec, reg, val); + if (ret != 0) + dev_err(codec->dev, "cache write to %x failed: %d\n", + reg, ret); + return ret; + } + return wsa881x_i2c_write_device(wsa881x, reg, val); +} + +static int wsa881x_i2c_get_client_index(struct i2c_client *client, + int *wsa881x_index) +{ + int ret = 0; + + switch (client->addr) { + case WSA881X_I2C_SPK0_SLAVE0_ADDR: + case WSA881X_I2C_SPK0_SLAVE1_ADDR: + *wsa881x_index = WSA881X_I2C_SPK0_SLAVE0; + break; + case WSA881X_I2C_SPK1_SLAVE0_ADDR: + case WSA881X_I2C_SPK1_SLAVE1_ADDR: + *wsa881x_index = WSA881X_I2C_SPK1_SLAVE0; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int wsa881x_boost_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enable:%d\n", __func__, enable); + if (enable) { + if (!WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_ANA_CTL, + 0x01, 0x01); + snd_soc_update_bits(codec, WSA881X_ANA_CTL, + 0x04, 0x04); + snd_soc_update_bits(codec, WSA881X_BOOST_PS_CTL, + 0x40, 0x00); + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, + 0xF0, 0xB0); + snd_soc_update_bits(codec, WSA881X_BOOST_ZX_CTL, + 0x20, 0x00); + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, + 0x80, 0x80); + } else { + snd_soc_update_bits(codec, WSA881X_BOOST_LOOP_STABILITY, + 0x03, 0x03); + snd_soc_update_bits(codec, WSA881X_BOOST_MISC2_CTL, + 0xFF, 0x14); + snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, + 0x80, 0x80); + snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, + 0x03, 0x00); + snd_soc_update_bits(codec, + WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, + 0x0C, 0x04); + snd_soc_update_bits(codec, + WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, + 0x03, 0x00); + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, + 0xF0, 0x70); + snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x03, 0x01); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, + 0x08, 0x08); + snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x04, 0x04); + snd_soc_update_bits(codec, WSA881X_BOOST_CURRENT_LIMIT, + 0x0F, 0x08); + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, + 0x80, 0x80); + } + /* For WSA8810, start-up time is 1500us as per qcrg sequence */ + usleep_range(1500, 1510); + } else { + /* ENSURE: Class-D amp is shutdown. CLK is still on */ + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x00); + /* boost settle time is 1500us as per qcrg sequence */ + usleep_range(1500, 1510); + } + return 0; +} + +static int wsa881x_visense_txfe_ctrl(struct snd_soc_codec *codec, bool enable, + u8 isense1_gain, u8 isense2_gain, + u8 vsense_gain) +{ + u8 value = 0; + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enable:%d\n", __func__, enable); + + if (enable) { + if (WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_OTP_REG_28, + 0x3F, 0x3A); + snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG1, + 0xFF, 0xB2); + snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG2, + 0xFF, 0x05); + } + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_VSENSE_VCM, + 0x08, 0x00); + if (WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2, + 0x1C, 0x04); + } else { + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2, + 0x08, 0x08); + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2, + 0x02, 0x02); + } + value = ((isense2_gain << 6) | (isense1_gain << 4) | + (vsense_gain << 3)); + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, + 0xF8, value); + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, + 0x01, 0x01); + } else { + if (WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, + WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x10, 0x10); + else + snd_soc_update_bits(codec, + WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x08, 0x08); + /* + * 200us sleep is needed after visense txfe disable as per + * HW requirement. + */ + usleep_range(200, 210); + + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, + 0x01, 0x00); + } + return 0; +} + +static int wsa881x_visense_adc_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enable:%d\n", __func__, enable); + if (enable) { + if (!WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, WSA881X_ADC_SEL_IBIAS, + 0x70, 0x40); + snd_soc_update_bits(codec, WSA881X_ADC_EN_SEL_IBIAS, + 0x07, 0x04); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, 0x80, 0x80); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, 0x80, 0x80); + } else { + /* Ensure: Speaker Protection has been stopped */ + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, 0x80, 0x00); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, 0x80, 0x00); + } + + return 0; +} + +static void wsa881x_bandgap_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: enable:%d, bg_count:%d\n", __func__, + enable, wsa881x->bg_cnt); + mutex_lock(&wsa881x->bg_lock); + if (enable) { + ++wsa881x->bg_cnt; + if (wsa881x->bg_cnt == 1) { + snd_soc_update_bits(codec, WSA881X_TEMP_OP, + 0x08, 0x08); + /* 400usec sleep is needed as per HW requirement */ + usleep_range(400, 410); + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x04); + } + } else { + --wsa881x->bg_cnt; + if (wsa881x->bg_cnt <= 0) { + WARN_ON(wsa881x->bg_cnt < 0); + wsa881x->bg_cnt = 0; + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x00); + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x08, 0x00); + } + } + mutex_unlock(&wsa881x->bg_lock); +} + +static void wsa881x_clk_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s:ss enable:%d, clk_count:%d\n", __func__, + enable, wsa881x->clk_cnt); + mutex_lock(&wsa881x->res_lock); + if (enable) { + ++wsa881x->clk_cnt; + if (wsa881x->clk_cnt == 1) { + snd_soc_write(codec, WSA881X_CDC_RST_CTL, 0x02); + snd_soc_write(codec, WSA881X_CDC_RST_CTL, 0x03); + snd_soc_write(codec, WSA881X_CLOCK_CONFIG, 0x01); + snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x01); + snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x01); + } + } else { + --wsa881x->clk_cnt; + if (wsa881x->clk_cnt <= 0) { + WARN_ON(wsa881x->clk_cnt < 0); + wsa881x->clk_cnt = 0; + snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x00); + snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x00); + if (WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, + WSA881X_CDC_TOP_CLK_CTL, 0x01, 0x00); + } + } + mutex_unlock(&wsa881x->res_lock); +} + +static int wsa881x_rdac_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enable:%d\n", __func__, enable); + if (enable) { + snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x08, 0x00); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0x08, 0x08); + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x40, 0x40); + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x80, 0x80); + if (WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, + 0x01, 0x01); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, + 0x30, 0x30); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, + 0x0C, 0x00); + } + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0xF0, 0x40); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x01, 0x01); + } else { + /* Ensure class-D amp is off */ + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x80, 0x00); + } + return 0; +} + +static int wsa881x_spkr_pa_ctrl(struct snd_soc_codec *codec, bool enable) +{ + int ret = 0; + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enable:%d\n", __func__, enable); + if (enable) { + /* + * Ensure: Boost is enabled and stable, Analog input is up + * and outputting silence + */ + if (!WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_I, + 0xFF, 0x01); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, + 0x02, 0x02); + snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_V, + 0xFF, 0x10); + snd_soc_update_bits(codec, WSA881X_SPKR_PWRSTG_DBG, + 0xA0, 0xA0); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, + 0x80, 0x80); + usleep_range(700, 710); + snd_soc_update_bits(codec, WSA881X_SPKR_PWRSTG_DBG, + 0x00, 0x00); + snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_V, + 0xFF, 0x00); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, + 0x02, 0x00); + snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_I, + 0xFF, 0x00); + } else + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, + 0x80, 0x80); + /* add 1000us delay as per qcrg */ + usleep_range(1000, 1010); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x01, 0x01); + if (WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, + 0x01, 0x00); + usleep_range(1000, 1010); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0xF0, + (wsa881x->spk_pa_gain << 4)); + if (wsa881x->visense_enable) { + ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, + "wsa_vi"); + if (ret) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "wsa_vi"); + return ret; + } + wsa881x_visense_txfe_ctrl(codec, true, + 0x00, 0x01, 0x00); + wsa881x_visense_adc_ctrl(codec, true); + } + } else { + /* + * Ensure: Boost is still on, Stream from Analog input and + * Speaker Protection has been stopped and input is at 0V + */ + if (WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, + 0x01, 0x01); + usleep_range(1000, 1010); + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, + 0x01, 0x00); + msleep(20); + snd_soc_update_bits(codec, WSA881X_ANA_CTL, + 0x03, 0x00); + usleep_range(200, 210); + } + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x80, 0x00); + } + return 0; +} + +static int wsa881x_get_boost(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->boost_enable; + return 0; +} + +static int wsa881x_set_boost(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Boost enable current %d, new %d\n", + __func__, wsa881x->boost_enable, value); + wsa881x->boost_enable = value; + return 0; +} + +static int wsa881x_get_visense(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->visense_enable; + return 0; +} + +static int wsa881x_set_visense(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: VIsense enable current %d, new %d\n", + __func__, wsa881x->visense_enable, value); + wsa881x->visense_enable = value; + return 0; +} + +static const struct snd_kcontrol_new wsa881x_snd_controls[] = { + SOC_SINGLE_EXT("BOOST Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_boost, wsa881x_set_boost), + + SOC_SINGLE_EXT("VISENSE Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_visense, wsa881x_set_visense), + + SOC_ENUM_EXT("WSA_SPK PA Gain", wsa881x_spk_pa_gain_enum[0], + wsa881x_spk_pa_gain_get, wsa881x_spk_pa_gain_put), +}; + +static const char * const rdac_text[] = { + "ZERO", "Switch", +}; + +static const struct soc_enum rdac_enum = + SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(rdac_text), rdac_text); + +static const struct snd_kcontrol_new rdac_mux[] = { + SOC_DAPM_ENUM("RDAC", rdac_enum) +}; + +static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(codec->dev, "%s: %s %d boost %d visense %d\n", + __func__, w->name, event, + wsa881x->boost_enable, wsa881x->visense_enable); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = wsa881x_startup(wsa881x); + if (ret) { + pr_err("%s: wsa startup failed ret: %d", __func__, ret); + return ret; + } + wsa881x_clk_ctrl(codec, true); + snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x02, 0x02); + if (!WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, WSA881X_BIAS_REF_CTRL, + 0x0F, 0x08); + wsa881x_bandgap_ctrl(codec, true); + if (!WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, WSA881X_SPKR_BBM_CTL, + 0x02, 0x02); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06); + if (!WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL2, + 0x04, 0x04); + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_INT, + 0x09, 0x09); + } + snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0xF0, 0x20); + if (WSA881X_IS_2_0(wsa881x->version)) + snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, + 0x0E, 0x0E); + if (wsa881x->boost_enable) + wsa881x_boost_ctrl(codec, true); + break; + case SND_SOC_DAPM_POST_PMU: + wsa881x_rdac_ctrl(codec, true); + break; + case SND_SOC_DAPM_PRE_PMD: + wsa881x_rdac_ctrl(codec, false); + if (wsa881x->visense_enable) { + wsa881x_visense_adc_ctrl(codec, false); + wsa881x_visense_txfe_ctrl(codec, false, + 0x00, 0x01, 0x00); + ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, + "wsa_vi"); + if (ret) { + pr_err("%s: gpio set cannot be suspended %s\n", + __func__, "wsa_vi"); + return ret; + } + } + break; + case SND_SOC_DAPM_POST_PMD: + if (wsa881x->boost_enable) + wsa881x_boost_ctrl(codec, false); + wsa881x_clk_ctrl(codec, false); + wsa881x_bandgap_ctrl(codec, false); + ret = wsa881x_shutdown(wsa881x); + if (ret < 0) { + pr_err("%s: wsa shutdown failed ret: %d", + __func__, ret); + return ret; + } + break; + default: + pr_err("%s: invalid event:%d\n", __func__, event); + return -EINVAL; + } + return 0; +} + +static void wsa881x_ocp_ctl_work(struct work_struct *work) +{ + struct wsa881x_pdata *wsa881x; + struct delayed_work *dwork; + struct snd_soc_codec *codec; + unsigned long temp_val; + + dwork = to_delayed_work(work); + wsa881x = container_of(dwork, struct wsa881x_pdata, ocp_ctl_work); + + if (!wsa881x) + return; + + codec = wsa881x->codec; + wsa881x_get_temp(wsa881x->tz_pdata.tz_dev, &temp_val); + dev_dbg(codec->dev, " temp = %ld\n", temp_val); + + if (temp_val <= WSA881X_OCP_CTL_TEMP_CELSIUS) + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x00); + else + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); + + schedule_delayed_work(&wsa881x->ocp_ctl_work, + msecs_to_jiffies(wsa881x_ocp_poll_timer_sec * 1000)); +} + +static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: %s %d\n", __func__, w->name, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x80); + break; + case SND_SOC_DAPM_POST_PMU: + wsa881x_spkr_pa_ctrl(codec, true); + schedule_delayed_work(&wsa881x->ocp_ctl_work, + msecs_to_jiffies(WSA881X_OCP_CTL_TIMER_SEC * 1000)); + break; + case SND_SOC_DAPM_PRE_PMD: + wsa881x_spkr_pa_ctrl(codec, false); + break; + case SND_SOC_DAPM_POST_PMD: + cancel_delayed_work_sync(&wsa881x->ocp_ctl_work); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); + break; + default: + pr_err("%s: invalid event:%d\n", __func__, event); + return -EINVAL; + } + return 0; +} + + +static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("WSA_IN"), + + SND_SOC_DAPM_DAC_E("RDAC Analog", NULL, SND_SOC_NOPM, 0, 0, + wsa881x_rdac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("WSA_RDAC", SND_SOC_NOPM, 0, 0, + rdac_mux), + + SND_SOC_DAPM_PGA_S("WSA_SPKR PGA", 1, SND_SOC_NOPM, 0, 0, + wsa881x_spkr_pa_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("WSA_SPKR"), +}; + +static const struct snd_soc_dapm_route wsa881x_audio_map[] = { + {"WSA_RDAC", "Switch", "WSA_IN"}, + {"RDAC Analog", NULL, "WSA_RDAC"}, + {"WSA_SPKR PGA", NULL, "RDAC Analog"}, + {"WSA_SPKR", NULL, "WSA_SPKR PGA"}, +}; + + +static int wsa881x_startup(struct wsa881x_pdata *pdata) +{ + int ret = 0; + struct snd_soc_codec *codec = pdata->codec; + struct snd_soc_card *card = codec->component.card; + + pr_debug("%s(): wsa startup, enable_cnt:%d\n", __func__, + pdata->enable_cnt); + + if (pdata->enable_cnt++ > 0) + return 0; + ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_clk"); + if (ret) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "wsa_clk"); + return ret; + } + if (pdata->enable_mclk) { + ret = pdata->enable_mclk(card, true); + if (ret < 0) { + dev_err_ratelimited(codec->dev, + "%s: mclk enable failed %d\n", + __func__, ret); + return ret; + } + } + ret = wsa881x_reset(pdata, true); + return ret; +} + +static int wsa881x_shutdown(struct wsa881x_pdata *pdata) +{ + int ret = 0, reg; + struct snd_soc_codec *codec = pdata->codec; + struct snd_soc_card *card = codec->component.card; + + pr_debug("%s(): wsa shutdown, enable_cnt:%d\n", __func__, + pdata->enable_cnt); + if (--pdata->enable_cnt > 0) + return 0; + ret = wsa881x_reset(pdata, false); + if (ret) { + pr_err("%s: wsa reset failed suspend %d\n", + __func__, ret); + return ret; + } + + if (pdata->enable_mclk) { + ret = pdata->enable_mclk(card, false); + if (ret < 0) { + pr_err("%s: mclk disable failed %d\n", + __func__, ret); + return ret; + } + } + + ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_clk"); + if (ret) { + pr_err("%s: gpio set cannot be suspended %s\n", + __func__, "wsa_clk"); + return ret; + } + if (pdata->codec) { + /* restore defaults to cache */ + for (reg = 0; reg < ARRAY_SIZE(wsa881x_ana_reg_defaults); + reg++) { + if (wsa881x_ana_reg_readable[reg]) + snd_soc_cache_write(pdata->codec, + wsa881x_ana_reg_defaults[reg].reg, + wsa881x_ana_reg_defaults[reg].def); + } + } + return 0; +} + +static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, + bool enable) +{ + int ret = 0; + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + if (enable) { + ret = wsa881x_startup(wsa881x); + if (ret < 0) { + dev_err_ratelimited(codec->dev, + "%s: failed to startup\n", __func__); + return ret; + } + } + wsa881x_clk_ctrl(codec, enable); + wsa881x_bandgap_ctrl(codec, enable); + if (!enable) { + ret = wsa881x_shutdown(wsa881x); + if (ret < 0) + dev_err_ratelimited(codec->dev, + "%s: failed to shutdown\n", __func__); + } + return ret; +} + +static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec, + struct wsa_temp_register *wsa_temp_reg) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + if (!wsa881x) { + dev_err(codec->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + ret = wsa881x_resource_acquire(codec, true); + if (ret) { + dev_err_ratelimited(codec->dev, + "%s: resource acquire fail\n", __func__); + return ret; + } + + if (WSA881X_IS_2_0(wsa881x->version)) { + snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00); + wsa_temp_reg->dmeas_msb = snd_soc_read(codec, WSA881X_TEMP_MSB); + wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, WSA881X_TEMP_LSB); + snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x01); + } else { + wsa_temp_reg->dmeas_msb = snd_soc_read(codec, + WSA881X_TEMP_DOUT_MSB); + wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, + WSA881X_TEMP_DOUT_LSB); + } + wsa_temp_reg->d1_msb = snd_soc_read(codec, WSA881X_OTP_REG_1); + wsa_temp_reg->d1_lsb = snd_soc_read(codec, WSA881X_OTP_REG_2); + wsa_temp_reg->d2_msb = snd_soc_read(codec, WSA881X_OTP_REG_3); + wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4); + + ret = wsa881x_resource_acquire(codec, false); + if (ret) + dev_err_ratelimited(codec->dev, + "%s: resource release fail\n", __func__); + + return ret; +} + +static int wsa881x_probe(struct snd_soc_codec *codec) +{ + struct i2c_client *client; + int ret = 0; + int wsa881x_index = 0; + struct snd_soc_dapm_context *dapm = &codec->dapm; + char *widget_name = NULL; + struct snd_soc_card *card = codec->component.card; + struct snd_soc_codec_conf *codec_conf = card->codec_conf; + + client = dev_get_drvdata(codec->dev); + ret = wsa881x_i2c_get_client_index(client, &wsa881x_index); + if (ret != 0) { + dev_err(&client->dev, "%s: I2C get codec I2C\n" + "client failed\n", __func__); + return ret; + } + mutex_init(&wsa_pdata[wsa881x_index].bg_lock); + mutex_init(&wsa_pdata[wsa881x_index].res_lock); + snprintf(wsa_pdata[wsa881x_index].tz_pdata.name, 100, "%s", + wsa_tz_names[wsa881x_index]); + wsa_pdata[wsa881x_index].codec = codec; + wsa_pdata[wsa881x_index].spk_pa_gain = SPK_GAIN_12DB; + wsa_pdata[wsa881x_index].codec = codec; + wsa_pdata[wsa881x_index].tz_pdata.codec = codec; + wsa_pdata[wsa881x_index].tz_pdata.wsa_temp_reg_read = + wsa881x_temp_reg_read; + snd_soc_codec_set_drvdata(codec, &wsa_pdata[wsa881x_index]); + wsa881x_init_thermal(&wsa_pdata[wsa881x_index].tz_pdata); + INIT_DELAYED_WORK(&wsa_pdata[wsa881x_index].ocp_ctl_work, + wsa881x_ocp_ctl_work); + + if (codec_conf->name_prefix) { + widget_name = kcalloc(WIDGET_NAME_MAX_SIZE, sizeof(char), + GFP_KERNEL); + if (!widget_name) + return -ENOMEM; + + snprintf(widget_name, WIDGET_NAME_MAX_SIZE, + "%s WSA_SPKR", codec_conf->name_prefix); + snd_soc_dapm_ignore_suspend(dapm, widget_name); + snprintf(widget_name, WIDGET_NAME_MAX_SIZE, + "%s WSA_IN", codec_conf->name_prefix); + snd_soc_dapm_ignore_suspend(dapm, widget_name); + kfree(widget_name); + } else { + snd_soc_dapm_ignore_suspend(dapm, "WSA_SPKR"); + snd_soc_dapm_ignore_suspend(dapm, "WSA_IN"); + } + + snd_soc_dapm_sync(dapm); + return 0; +} + +static int wsa881x_remove(struct snd_soc_codec *codec) +{ + struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); + + if (wsa881x->tz_pdata.tz_dev) + wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev); + + mutex_destroy(&wsa881x->bg_lock); + mutex_destroy(&wsa881x->res_lock); + return 0; +} + +static struct snd_soc_codec_driver soc_codec_dev_wsa881x = { + .probe = wsa881x_probe, + .remove = wsa881x_remove, + + .read = wsa881x_i2c_read, + .write = wsa881x_i2c_write, + + .reg_cache_size = WSA881X_CACHE_SIZE, + .reg_cache_default = wsa881x_ana_reg_defaults, + .reg_word_size = 1, + + .component_driver = { + .controls = wsa881x_snd_controls, + .num_controls = ARRAY_SIZE(wsa881x_snd_controls), + .dapm_widgets = wsa881x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets), + .dapm_routes = wsa881x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map), + }, +}; + +static int wsa881x_reset(struct wsa881x_pdata *pdata, bool enable) +{ + int ret = 0; + + /* + * shutdown the GPIOs WSA_EN, WSA_MCLK, regulators + * and restore defaults in soc cache when shutdown. + * Enable regulators, GPIOs WSA_MCLK, WSA_EN when powerup. + */ + if (enable) { + if (pdata->wsa_active) + return 0; + ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_reset"); + if (ret) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "wsa_reset"); + return ret; + } + ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_reset"); + if (ret) { + pr_err("%s: gpio set cannot be suspended(powerup) %s\n", + __func__, "wsa_reset"); + return ret; + } + ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_reset"); + if (ret) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "wsa_reset"); + return ret; + } + pdata->wsa_active = true; + } else { + if (!pdata->wsa_active) + return 0; + ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_reset"); + if (ret) { + pr_err("%s: gpio set cannot be suspended %s\n", + __func__, "wsa_reset"); + return ret; + } + pdata->wsa_active = false; + } + return ret; +} + +int wsa881x_get_client_index(void) +{ + return wsa881x_i2c_addr; +} +EXPORT_SYMBOL(wsa881x_get_client_index); + +int wsa881x_get_probing_count(void) +{ + return wsa881x_probing_count; +} +EXPORT_SYMBOL(wsa881x_get_probing_count); + +int wsa881x_get_presence_count(void) +{ + return wsa881x_presence_count; +} +EXPORT_SYMBOL(wsa881x_get_presence_count); + +int wsa881x_set_mclk_callback( + int (*enable_mclk_callback)(struct snd_soc_card *, bool)) +{ + int i; + + for (i = 0; i < MAX_WSA881X_DEVICE; i++) { + if (wsa_pdata[i].status == WSA881X_STATUS_I2C) + wsa_pdata[i].enable_mclk = enable_mclk_callback; + } + return 0; +} +EXPORT_SYMBOL(wsa881x_set_mclk_callback); + +static int check_wsa881x_presence(struct i2c_client *client) +{ + int ret = 0; + int wsa881x_index = 0; + + ret = wsa881x_i2c_get_client_index(client, &wsa881x_index); + if (ret != 0) { + dev_err(&client->dev, "%s: I2C get codec I2C\n" + "client failed\n", __func__); + return ret; + } + ret = wsa881x_i2c_read_device(&wsa_pdata[wsa881x_index], + WSA881X_CDC_RST_CTL); + if (ret < 0) { + dev_err(&client->dev, "failed to read wsa881x with addr %x\n", + client->addr); + return ret; + } + ret = wsa881x_i2c_write_device(&wsa_pdata[wsa881x_index], + WSA881X_CDC_RST_CTL, 0x01); + if (ret < 0) { + dev_err(&client->dev, "failed write addr %x reg:0x5 val:0x1\n", + client->addr); + return ret; + } + /* allow 20ms before trigger next write to verify WSA881x presence */ + msleep(20); + ret = wsa881x_i2c_write_device(&wsa_pdata[wsa881x_index], + WSA881X_CDC_RST_CTL, 0x00); + if (ret < 0) { + dev_err(&client->dev, "failed write addr %x reg:0x5 val:0x0\n", + client->addr); + return ret; + } + return ret; +} + +static int wsa881x_populate_dt_pdata(struct device *dev) +{ + int ret = 0; + + /* reading the gpio configurations from dtsi file */ + if (!pinctrl_init) { + ret = msm_gpioset_initialize(CLIENT_WSA_BONGO_1, dev); + if (ret < 0) { + dev_err(dev, + "%s: error reading dtsi files%d\n", __func__, ret); + goto err; + } + pinctrl_init = true; + } +err: + return ret; +} + +static int wsa881x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + int wsa881x_index = 0; + struct wsa881x_pdata *pdata = NULL; + + ret = wsa881x_i2c_get_client_index(client, &wsa881x_index); + if (ret != 0) { + dev_err(&client->dev, "%s: I2C get codec I2C\n" + "client failed\n", __func__); + return ret; + } + + pdata = &wsa_pdata[wsa881x_index]; + + if ((client->addr == WSA881X_I2C_SPK0_SLAVE1_ADDR || + client->addr == WSA881X_I2C_SPK1_SLAVE1_ADDR) && + (pdata->status == WSA881X_STATUS_PROBING)) + return ret; + + if (pdata->status == WSA881X_STATUS_I2C) { + dev_dbg(&client->dev, "%s:probe for other slaves\n" + "devices of codec I2C slave Addr = %x\n", + __func__, client->addr); + + dev_dbg(&client->dev, "%s:wsa_idx = %d SLAVE = %d\n", + __func__, wsa881x_index, WSA881X_ANALOG_SLAVE); + pdata->regmap[WSA881X_ANALOG_SLAVE] = + devm_regmap_init_i2c( + client, + &wsa881x_ana_regmap_config[WSA881X_ANALOG_SLAVE]); + regcache_cache_bypass(pdata->regmap[WSA881X_ANALOG_SLAVE], + true); + if (IS_ERR(pdata->regmap[WSA881X_ANALOG_SLAVE])) { + ret = PTR_ERR(pdata->regmap[WSA881X_ANALOG_SLAVE]); + dev_err(&client->dev, + "%s: regmap_init failed %d\n", + __func__, ret); + } + client->dev.platform_data = pdata; + i2c_set_clientdata(client, pdata); + pdata->client[WSA881X_ANALOG_SLAVE] = client; + if (pdata->version == WSA881X_2_0) + wsa881x_update_regmap_2_0( + pdata->regmap[WSA881X_ANALOG_SLAVE], + WSA881X_ANALOG_SLAVE); + + return ret; + } else if (pdata->status == WSA881X_STATUS_PROBING) { + pdata->index = wsa881x_index; + if (client->dev.of_node) { + dev_dbg(&client->dev, "%s:Platform data\n" + "from device tree\n", __func__); + ret = wsa881x_populate_dt_pdata(&client->dev); + if (ret < 0) { + dev_err(&client->dev, + "%s: Fail to obtain pdata from device tree\n", + __func__); + ret = -EINVAL; + goto err; + } + client->dev.platform_data = pdata; + } else { + dev_dbg(&client->dev, "%s:Platform data from\n" + "board file\n", __func__); + pdata = client->dev.platform_data; + } + if (!pdata) { + dev_dbg(&client->dev, "no platform data?\n"); + ret = -EINVAL; + goto err; + } + i2c_set_clientdata(client, pdata); + dev_set_drvdata(&client->dev, client); + + pdata->regmap[WSA881X_DIGITAL_SLAVE] = + devm_regmap_init_i2c( + client, + &wsa881x_ana_regmap_config[WSA881X_DIGITAL_SLAVE]); + regcache_cache_bypass(pdata->regmap[WSA881X_DIGITAL_SLAVE], + true); + if (IS_ERR(pdata->regmap[WSA881X_DIGITAL_SLAVE])) { + ret = PTR_ERR(pdata->regmap[WSA881X_DIGITAL_SLAVE]); + dev_err(&client->dev, "%s: regmap_init failed %d\n", + __func__, ret); + goto err; + } + /* bus reset sequence */ + ret = wsa881x_reset(pdata, true); + if (ret < 0) { + dev_err(&client->dev, "%s: WSA enable Failed %d\n", + __func__, ret); + goto err; + } + pdata->client[WSA881X_DIGITAL_SLAVE] = client; + pdata->regmap_flag = true; + ret = check_wsa881x_presence(client); + if (ret < 0) { + dev_err(&client->dev, + "failed to ping wsa with addr:%x, ret = %d\n", + client->addr, ret); + wsa881x_probing_count++; + goto err1; + } + pdata->version = wsa881x_i2c_read_device(pdata, + WSA881X_CHIP_ID1); + pr_debug("%s: wsa881x version: %d\n", __func__, pdata->version); + if (pdata->version == WSA881X_2_0) { + wsa881x_update_reg_defaults_2_0(); + wsa881x_update_regmap_2_0( + pdata->regmap[WSA881X_DIGITAL_SLAVE], + WSA881X_DIGITAL_SLAVE); + } + wsa881x_presence_count++; + wsa881x_probing_count++; + ret = snd_soc_register_codec(&client->dev, + &soc_codec_dev_wsa881x, + NULL, 0); + if (ret < 0) + goto err1; + pdata->status = WSA881X_STATUS_I2C; + } +err1: + wsa881x_reset(pdata, false); +err: + return 0; +} + +static int wsa881x_i2c_remove(struct i2c_client *client) +{ + struct wsa881x_pdata *wsa881x = i2c_get_clientdata(client); + + snd_soc_unregister_codec(&client->dev); + i2c_set_clientdata(client, NULL); + kfree(wsa881x); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int wsa881x_i2c_suspend(struct device *dev) +{ + pr_debug("%s: system suspend\n", __func__); + return 0; +} + +static int wsa881x_i2c_resume(struct device *dev) +{ + pr_debug("%s: system resume\n", __func__); + return 0; +} + +static const struct dev_pm_ops wsa881x_i2c_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(wsa881x_i2c_suspend, wsa881x_i2c_resume) +}; +#endif /* CONFIG_PM_SLEEP */ + +static const struct i2c_device_id wsa881x_i2c_id[] = { + {"wsa881x-i2c-dev", WSA881X_I2C_SPK0_SLAVE0_ADDR}, + {"wsa881x-i2c-dev", WSA881X_I2C_SPK0_SLAVE1_ADDR}, + {"wsa881x-i2c-dev", WSA881X_I2C_SPK1_SLAVE0_ADDR}, + {"wsa881x-i2c-dev", WSA881X_I2C_SPK1_SLAVE1_ADDR}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, wsa881x_i2c_id); + + +static const struct of_device_id msm_match_table[] = { + {.compatible = "qcom,wsa881x-i2c-codec"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_match_table); + +static struct i2c_driver wsa881x_codec_driver = { + .driver = { + .name = "wsa881x-i2c-codec", + .owner = THIS_MODULE, +#ifdef CONFIG_PM_SLEEP + .pm = &wsa881x_i2c_pm_ops, +#endif + .of_match_table = msm_match_table, + }, + .id_table = wsa881x_i2c_id, + .probe = wsa881x_i2c_probe, + .remove = wsa881x_i2c_remove, +}; + +static int __init wsa881x_codec_init(void) +{ + int i = 0; + + for (i = 0; i < MAX_WSA881X_DEVICE; i++) + wsa_pdata[i].status = WSA881X_STATUS_PROBING; + return i2c_add_driver(&wsa881x_codec_driver); +} +module_init(wsa881x_codec_init); + +static void __exit wsa881x_codec_exit(void) +{ + i2c_del_driver(&wsa881x_codec_driver); +} + +module_exit(wsa881x_codec_exit); + +MODULE_DESCRIPTION("WSA881x Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wsa881x-analog.h b/sound/soc/codecs/wsa881x-analog.h new file mode 100644 index 000000000000..a2ef2a284c23 --- /dev/null +++ b/sound/soc/codecs/wsa881x-analog.h @@ -0,0 +1,50 @@ +/* Copyright (c) 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 _WSA881X_H +#define _WSA881X_H + +#include +#include "wsa881x-registers-analog.h" +#include + +#define WSA881X_I2C_SPK0_SLAVE0_ADDR 0x0E +#define WSA881X_I2C_SPK0_SLAVE1_ADDR 0x44 +#define WSA881X_I2C_SPK1_SLAVE0_ADDR 0x0F +#define WSA881X_I2C_SPK1_SLAVE1_ADDR 0x45 + +#define WSA881X_I2C_SPK0_SLAVE0 0 +#define WSA881X_I2C_SPK1_SLAVE0 1 +#define MAX_WSA881X_DEVICE 2 +#define WSA881X_DIGITAL_SLAVE 0 +#define WSA881X_ANALOG_SLAVE 1 + +enum { + WSA881X_1_X = 0, + WSA881X_2_0, +}; + +#define WSA881X_IS_2_0(ver) \ + ((ver == WSA881X_2_0) ? 1 : 0) + +extern const u8 wsa881x_ana_reg_readable[WSA881X_CACHE_SIZE]; +extern struct reg_default wsa881x_ana_reg_defaults[WSA881X_CACHE_SIZE]; +extern struct regmap_config wsa881x_ana_regmap_config[2]; +int wsa881x_get_client_index(void); +int wsa881x_get_probing_count(void); +int wsa881x_get_presence_count(void); +int wsa881x_set_mclk_callback( + int (*enable_mclk_callback)(struct snd_soc_card *, bool)); +void wsa881x_update_reg_defaults_2_0(void); +void wsa881x_update_regmap_2_0(struct regmap *regmap, int flag); + +#endif /* _WSA881X_H */ diff --git a/sound/soc/codecs/wsa881x-irq.c b/sound/soc/codecs/wsa881x-irq.c new file mode 100644 index 000000000000..9afbd92b8f72 --- /dev/null +++ b/sound/soc/codecs/wsa881x-irq.c @@ -0,0 +1,610 @@ +/* Copyright (c) 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wsa881x-irq.h" +#include "wsa881x-registers-analog.h" + +#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE)) +#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) + + +#define WSA_MAX_NUM_IRQS 8 + +#ifndef NO_IRQ +#define NO_IRQ (-1) +#endif + +static int virq_to_phyirq( + struct wsa_resource *wsa_res, int virq); +static int phyirq_to_virq( + struct wsa_resource *wsa_res, int irq); +static unsigned int wsa_irq_get_upstream_irq( + struct wsa_resource *wsa_res); +static void wsa_irq_put_upstream_irq( + struct wsa_resource *wsa_res); +static int wsa_map_irq( + struct wsa_resource *wsa_res, int irq); + +static struct snd_soc_codec *ptr_codec; + +/** + * wsa_set_codec() - to update codec pointer + * @codec: codec pointer. + * + * To update the codec pointer, which is used to read/write + * wsa register. + * + * Return: void. + */ +void wsa_set_codec(struct snd_soc_codec *codec) +{ + if (codec == NULL) { + pr_err("%s: codec pointer is NULL\n", __func__); + ptr_codec = NULL; + return; + } + ptr_codec = codec; + /* Initialize interrupt mask and level registers */ + snd_soc_write(codec, WSA881X_INTR_LEVEL, 0x8F); + snd_soc_write(codec, WSA881X_INTR_MASK, 0x8F); +} + +static void wsa_irq_lock(struct irq_data *data) +{ + struct wsa_resource *wsa_res = + irq_data_get_irq_chip_data(data); + + if (wsa_res == NULL) { + pr_err("%s: wsa_res pointer is NULL\n", __func__); + return; + } + mutex_lock(&wsa_res->irq_lock); +} + +static void wsa_irq_sync_unlock(struct irq_data *data) +{ + struct wsa_resource *wsa_res = + irq_data_get_irq_chip_data(data); + + if (wsa_res == NULL) { + pr_err("%s: wsa_res pointer is NULL\n", __func__); + return; + } + if (wsa_res->codec == NULL) { + pr_err("%s: codec pointer not registered\n", __func__); + if (ptr_codec == NULL) { + pr_err("%s: did not receive valid codec pointer\n", + __func__); + goto unlock; + } else { + wsa_res->codec = ptr_codec; + } + } + + /* + * If there's been a change in the mask write it back + * to the hardware. + */ + if (wsa_res->irq_masks_cur != + wsa_res->irq_masks_cache) { + + wsa_res->irq_masks_cache = + wsa_res->irq_masks_cur; + snd_soc_write(wsa_res->codec, + WSA881X_INTR_MASK, + wsa_res->irq_masks_cur); + } +unlock: + mutex_unlock(&wsa_res->irq_lock); +} + +static void wsa_irq_enable(struct irq_data *data) +{ + struct wsa_resource *wsa_res = + irq_data_get_irq_chip_data(data); + int wsa_irq; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res pointer is NULL\n", __func__); + return; + } + wsa_irq = virq_to_phyirq(wsa_res, data->irq); + pr_debug("%s: wsa_irq = %d\n", __func__, wsa_irq); + wsa_res->irq_masks_cur &= + ~(BYTE_BIT_MASK(wsa_irq)); +} + +static void wsa_irq_disable(struct irq_data *data) +{ + struct wsa_resource *wsa_res = + irq_data_get_irq_chip_data(data); + int wsa_irq; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res pointer is NULL\n", __func__); + return; + } + wsa_irq = virq_to_phyirq(wsa_res, data->irq); + pr_debug("%s: wsa_irq = %d\n", __func__, wsa_irq); + wsa_res->irq_masks_cur + |= BYTE_BIT_MASK(wsa_irq); +} + +static void wsa_irq_ack(struct irq_data *data) +{ + int wsa_irq = 0; + struct wsa_resource *wsa_res = + irq_data_get_irq_chip_data(data); + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + wsa_irq = virq_to_phyirq(wsa_res, data->irq); + pr_debug("%s: IRQ_ACK called for WCD9XXX IRQ: %d\n", + __func__, wsa_irq); +} + +static void wsa_irq_mask(struct irq_data *d) +{ + /* do nothing but required as linux calls irq_mask without NULL check */ +} + +static struct irq_chip wsa_irq_chip = { + .name = "wsa", + .irq_bus_lock = wsa_irq_lock, + .irq_bus_sync_unlock = wsa_irq_sync_unlock, + .irq_disable = wsa_irq_disable, + .irq_enable = wsa_irq_enable, + .irq_mask = wsa_irq_mask, + .irq_ack = wsa_irq_ack, +}; + +static irqreturn_t wsa_irq_thread(int irq, void *data) +{ + struct wsa_resource *wsa_res = data; + int i; + u8 status; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return IRQ_HANDLED; + } + if (wsa_res->codec == NULL) { + pr_err("%s: codec pointer not registered\n", __func__); + if (ptr_codec == NULL) { + pr_err("%s: did not receive valid codec pointer\n", + __func__); + return IRQ_HANDLED; + } + wsa_res->codec = ptr_codec; + } + status = snd_soc_read(wsa_res->codec, WSA881X_INTR_STATUS); + /* Apply masking */ + status &= ~wsa_res->irq_masks_cur; + + for (i = 0; i < wsa_res->num_irqs; i++) { + if (status & BYTE_BIT_MASK(i)) { + mutex_lock(&wsa_res->nested_irq_lock); + handle_nested_irq(phyirq_to_virq(wsa_res, i)); + mutex_unlock(&wsa_res->nested_irq_lock); + } + } + + return IRQ_HANDLED; +} + +/** + * wsa_free_irq() - to free an interrupt + * @irq: interrupt number. + * @data: pointer to wsa resource. + * + * To free already requested interrupt. + * + * Return: void. + */ +void wsa_free_irq(int irq, void *data) +{ + struct wsa_resource *wsa_res = data; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + free_irq(phyirq_to_virq(wsa_res, irq), data); +} + +/** + * wsa_enable_irq() - to enable an interrupt + * @wsa_res: pointer to wsa resource. + * @irq: interrupt number. + * + * This function is to enable an interrupt. + * + * Return: void. + */ +void wsa_enable_irq(struct wsa_resource *wsa_res, int irq) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + enable_irq(phyirq_to_virq(wsa_res, irq)); +} + +/** + * wsa_disable_irq() - to disable an interrupt + * @wsa_res: pointer to wsa resource. + * @irq: interrupt number. + * + * To disable an interrupt without waiting for executing + * handler to complete. + * + * Return: void. + */ +void wsa_disable_irq(struct wsa_resource *wsa_res, int irq) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + disable_irq_nosync(phyirq_to_virq(wsa_res, irq)); +} + +/** + * wsa_disable_irq_sync() - to disable an interrupt + * @wsa_res: pointer to wsa resource. + * @irq: interrupt number. + * + * To disable an interrupt, wait for executing IRQ + * handler to complete. + * + * Return: void. + */ +void wsa_disable_irq_sync( + struct wsa_resource *wsa_res, int irq) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + disable_irq(phyirq_to_virq(wsa_res, irq)); +} + +static int wsa_irq_setup_downstream_irq(struct wsa_resource *wsa_res) +{ + int irq, virq, ret; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: enter\n", __func__); + + for (irq = 0; irq < wsa_res->num_irqs; irq++) { + /* Map OF irq */ + virq = wsa_map_irq(wsa_res, irq); + pr_debug("%s: irq %d -> %d\n", __func__, irq, virq); + if (virq == NO_IRQ) { + pr_err("%s, No interrupt specifier for irq %d\n", + __func__, irq); + return NO_IRQ; + } + + ret = irq_set_chip_data(virq, wsa_res); + if (ret) { + pr_err("%s: Failed to configure irq %d (%d)\n", + __func__, irq, ret); + return ret; + } + + if (wsa_res->irq_level_high[irq]) + irq_set_chip_and_handler(virq, &wsa_irq_chip, + handle_level_irq); + else + irq_set_chip_and_handler(virq, &wsa_irq_chip, + handle_edge_irq); + + irq_set_nested_thread(virq, 1); + } + + pr_debug("%s: leave\n", __func__); + + return 0; +} + +static int wsa_irq_init(struct wsa_resource *wsa_res) +{ + int i, ret; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + mutex_init(&wsa_res->irq_lock); + mutex_init(&wsa_res->nested_irq_lock); + + wsa_res->irq = wsa_irq_get_upstream_irq(wsa_res); + if (!wsa_res->irq) { + pr_warn("%s: irq driver is not yet initialized\n", __func__); + mutex_destroy(&wsa_res->irq_lock); + mutex_destroy(&wsa_res->nested_irq_lock); + return -EPROBE_DEFER; + } + pr_debug("%s: probed irq %d\n", __func__, wsa_res->irq); + + /* Setup downstream IRQs */ + ret = wsa_irq_setup_downstream_irq(wsa_res); + if (ret) { + pr_err("%s: Failed to setup downstream IRQ\n", __func__); + goto fail_irq_init; + } + + /* mask all the interrupts */ + for (i = 0; i < wsa_res->num_irqs; i++) { + wsa_res->irq_masks_cur |= BYTE_BIT_MASK(i); + wsa_res->irq_masks_cache |= BYTE_BIT_MASK(i); + } + + ret = request_threaded_irq(wsa_res->irq, NULL, wsa_irq_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "wsa", wsa_res); + if (ret != 0) { + dev_err(wsa_res->dev, "Failed to request IRQ %d: %d\n", + wsa_res->irq, ret); + } else { + ret = enable_irq_wake(wsa_res->irq); + if (ret) { + dev_err(wsa_res->dev, + "Failed to set wake interrupt on IRQ %d: %d\n", + wsa_res->irq, ret); + free_irq(wsa_res->irq, wsa_res); + } + } + + if (ret) + goto fail_irq_init; + + return ret; + +fail_irq_init: + dev_err(wsa_res->dev, + "%s: Failed to init wsa irq\n", __func__); + wsa_irq_put_upstream_irq(wsa_res); + mutex_destroy(&wsa_res->irq_lock); + mutex_destroy(&wsa_res->nested_irq_lock); + return ret; +} + +/** + * wsa_request_irq() - to request/register an interrupt + * @wsa_res: pointer to wsa_resource. + * @irq: interrupt number. + * @handler: interrupt handler function pointer. + * @name: interrupt name. + * @data: device info. + * + * Convert physical irq to virtual irq and then + * reguest for threaded handler. + * + * Return: Retuns success/failure. + */ +int wsa_request_irq(struct wsa_resource *wsa_res, + int irq, irq_handler_t handler, + const char *name, void *data) +{ + int virq; + + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + virq = phyirq_to_virq(wsa_res, irq); + + /* + * ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. + */ +#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) + set_irq_flags(virq, IRQF_VALID); +#else + set_irq_noprobe(virq); +#endif + + return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING, + name, data); +} + +/** + * wsa_irq_exit() - to disable/clear interrupt/resources + * @wsa_res: pointer to wsa_resource + * + * Disable and free the interrupts and then release resources. + * + * Return: void. + */ +void wsa_irq_exit(struct wsa_resource *wsa_res) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + dev_dbg(wsa_res->dev, "%s: Cleaning up irq %d\n", __func__, + wsa_res->irq); + + if (wsa_res->irq) { + disable_irq_wake(wsa_res->irq); + free_irq(wsa_res->irq, wsa_res); + /* Release parent's of node */ + wsa_irq_put_upstream_irq(wsa_res); + } + mutex_destroy(&wsa_res->irq_lock); + mutex_destroy(&wsa_res->nested_irq_lock); +} + +static int phyirq_to_virq(struct wsa_resource *wsa_res, int offset) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + return irq_linear_revmap(wsa_res->domain, offset); +} + +static int virq_to_phyirq(struct wsa_resource *wsa_res, int virq) +{ + struct irq_data *irq_data = irq_get_irq_data(virq); + + if (unlikely(!irq_data)) { + pr_err("%s: irq_data is NULL\n", __func__); + return -EINVAL; + } + return irq_data->hwirq; +} + +static unsigned int wsa_irq_get_upstream_irq(struct wsa_resource *wsa_res) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + return wsa_res->irq; +} + +static void wsa_irq_put_upstream_irq(struct wsa_resource *wsa_res) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return; + } + /* Hold parent's of node */ + of_node_put(wsa_res->dev->of_node); +} + +static int wsa_map_irq(struct wsa_resource *wsa_res, int irq) +{ + if (wsa_res == NULL) { + pr_err("%s: wsa_res is NULL\n", __func__); + return -EINVAL; + } + return of_irq_to_resource(wsa_res->dev->of_node, irq, NULL); +} + +static int wsa_irq_probe(struct platform_device *pdev) +{ + int irq; + struct wsa_resource *wsa_res = NULL; + int ret = -EINVAL; + + irq = platform_get_irq_byname(pdev, "wsa-int"); + if (irq < 0) { + dev_err(&pdev->dev, "%s: Couldn't find wsa-int node(%d)\n", + __func__, irq); + return -EINVAL; + } + pr_debug("%s: node %s\n", __func__, pdev->name); + wsa_res = kzalloc(sizeof(*wsa_res), GFP_KERNEL); + if (!wsa_res) { + pr_err("%s: could not allocate memory\n", __func__); + return -ENOMEM; + } + /* + * wsa interrupt controller supports N to N irq mapping with + * single cell binding with irq numbers(offsets) only. + * Use irq_domain_simple_ops that has irq_domain_simple_map and + * irq_domain_xlate_onetwocell. + */ + wsa_res->dev = &pdev->dev; + wsa_res->domain = irq_domain_add_linear(wsa_res->dev->of_node, + WSA_MAX_NUM_IRQS, &irq_domain_simple_ops, + wsa_res); + if (!wsa_res->domain) { + dev_err(&pdev->dev, "%s: domain is NULL\n", __func__); + ret = -ENOMEM; + goto err; + } + wsa_res->dev = &pdev->dev; + + dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq); + wsa_res->irq = irq; + wsa_res->num_irq_regs = 1; + wsa_res->num_irqs = WSA_NUM_IRQS; + ret = wsa_irq_init(wsa_res); + if (ret < 0) { + dev_err(&pdev->dev, "%s: failed to do irq init %d\n", + __func__, ret); + goto err; + } + + return ret; +err: + kfree(wsa_res); + return ret; +} + +static int wsa_irq_remove(struct platform_device *pdev) +{ + struct irq_domain *domain; + struct wsa_resource *data; + + domain = irq_find_host(pdev->dev.of_node); + if (unlikely(!domain)) { + pr_err("%s: domain is NULL\n", __func__); + return -EINVAL; + } + data = (struct wsa_resource *)domain->host_data; + data->irq = 0; + + return 0; +} + +static const struct of_device_id of_match[] = { + { .compatible = "qcom,wsa-irq" }, + { } +}; + +static struct platform_driver wsa_irq_driver = { + .probe = wsa_irq_probe, + .remove = wsa_irq_remove, + .driver = { + .name = "wsa_intc", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(of_match), + }, +}; + +static int wsa_irq_drv_init(void) +{ + return platform_driver_register(&wsa_irq_driver); +} +subsys_initcall(wsa_irq_drv_init); + +static void wsa_irq_drv_exit(void) +{ + platform_driver_unregister(&wsa_irq_driver); +} +module_exit(wsa_irq_drv_exit); + +MODULE_DESCRIPTION("WSA881x IRQ driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wsa881x-irq.h b/sound/soc/codecs/wsa881x-irq.h new file mode 100644 index 000000000000..270eb917a666 --- /dev/null +++ b/sound/soc/codecs/wsa881x-irq.h @@ -0,0 +1,82 @@ +/* Copyright (c) 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 __WSA881X_IRQ_H__ +#define __WSA881X_IRQ_H__ + +#include +#include +#include + +/** + * enum wsa_interrupts - wsa interrupt number + * @WSA_INT_SAF2WAR: Temp irq interrupt, from safe state to warning state. + * @WSA_INT_WAR2SAF: Temp irq interrupt, from warning state to safe state. + * @WSA_INT_DISABLE: Disable Temp sensor interrupts. + * @WSA_INT_OCP: OCP interrupt. + * @WSA_INT_CLIP: CLIP detect interrupt. + * @WSA_NUM_IRQS: MAX Interrupt number. + * + * WSA IRQ Interrupt numbers. + */ +enum wsa_interrupts { + WSA_INT_SAF2WAR = 0, + WSA_INT_WAR2SAF, + WSA_INT_DISABLE, + WSA_INT_OCP, + WSA_INT_CLIP, + WSA_NUM_IRQS, +}; + +/** + * struct wsa_resource - the basic wsa_resource structure + * @irq_lock: lock used by irq_chip functions. + * @nested_irq_lock: lock used while handling nested interrupts. + * @irq: interrupt number. + * @irq_masks_cur: current mask value to be written to mask registers. + * @irq_masks_cache: cached mask value. + * @num_irqs: number of supported interrupts. + * @num_irq_regs: number of irq registers. + * @parent: parent pointer. + * @dev: device pointer. + * @domain: irq domain pointer. + * codec: codec pointer. + * + * Contains required members used in wsa irq driver. + */ + +struct wsa_resource { + struct mutex irq_lock; + struct mutex nested_irq_lock; + unsigned int irq; + u8 irq_masks_cur; + u8 irq_masks_cache; + bool irq_level_high[8]; + int num_irqs; + int num_irq_regs; + void *parent; + struct device *dev; + struct irq_domain *domain; + struct snd_soc_codec *codec; +}; + +void wsa_set_codec(struct snd_soc_codec *codec); +void wsa_free_irq(int irq, void *data); +void wsa_enable_irq(struct wsa_resource *wsa_res, int irq); +void wsa_disable_irq(struct wsa_resource *wsa_res, int irq); +void wsa_disable_irq_sync(struct wsa_resource *wsa_res, int irq); +int wsa_request_irq(struct wsa_resource *wsa_res, + int irq, irq_handler_t handler, + const char *name, void *data); + +void wsa_irq_exit(struct wsa_resource *wsa_res); + +#endif /* __WSA881X_IRQ_H__ */ diff --git a/sound/soc/codecs/wsa881x-registers-analog.h b/sound/soc/codecs/wsa881x-registers-analog.h new file mode 100644 index 000000000000..a5ebf8e1aab4 --- /dev/null +++ b/sound/soc/codecs/wsa881x-registers-analog.h @@ -0,0 +1,206 @@ +/* Copyright (c) 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 WSA881X_REGISTERS_H +#define WSA881X_REGISTERS_H + +#define WSA881X_DIGITAL_BASE 0x0000 +#define WSA881X_ANALOG_BASE 0x0100 + +#define WSA881X_CHIP_ID0 (WSA881X_DIGITAL_BASE+0x0000) +#define WSA881X_CHIP_ID1 (WSA881X_DIGITAL_BASE+0x0001) +#define WSA881X_CHIP_ID2 (WSA881X_DIGITAL_BASE+0x0002) +#define WSA881X_CHIP_ID3 (WSA881X_DIGITAL_BASE+0x0003) +#define WSA881X_BUS_ID (WSA881X_DIGITAL_BASE+0x0004) +#define WSA881X_CDC_RST_CTL (WSA881X_DIGITAL_BASE+0x0005) +#define WSA881X_CDC_TOP_CLK_CTL (WSA881X_DIGITAL_BASE+0x0006) +#define WSA881X_CDC_ANA_CLK_CTL (WSA881X_DIGITAL_BASE+0x0007) +#define WSA881X_CDC_DIG_CLK_CTL (WSA881X_DIGITAL_BASE+0x0008) +#define WSA881X_CLOCK_CONFIG (WSA881X_DIGITAL_BASE+0x0009) +#define WSA881X_ANA_CTL (WSA881X_DIGITAL_BASE+0x000A) +#define WSA881X_SWR_RESET_EN (WSA881X_DIGITAL_BASE+0x000B) +#define WSA881X_RESET_CTL (WSA881X_DIGITAL_BASE+0x000C) +#define WSA881X_TADC_VALUE_CTL (WSA881X_DIGITAL_BASE+0x000F) +#define WSA881X_TEMP_DETECT_CTL (WSA881X_DIGITAL_BASE+0x0010) +#define WSA881X_TEMP_MSB (WSA881X_DIGITAL_BASE+0x0011) +#define WSA881X_TEMP_LSB (WSA881X_DIGITAL_BASE+0x0012) +#define WSA881X_TEMP_CONFIG0 (WSA881X_DIGITAL_BASE+0x0013) +#define WSA881X_TEMP_CONFIG1 (WSA881X_DIGITAL_BASE+0x0014) +#define WSA881X_CDC_CLIP_CTL (WSA881X_DIGITAL_BASE+0x0015) +#define WSA881X_SDM_PDM9_LSB (WSA881X_DIGITAL_BASE+0x0016) +#define WSA881X_SDM_PDM9_MSB (WSA881X_DIGITAL_BASE+0x0017) +#define WSA881X_CDC_RX_CTL (WSA881X_DIGITAL_BASE+0x0018) +#define WSA881X_DEM_BYPASS_DATA0 (WSA881X_DIGITAL_BASE+0x0019) +#define WSA881X_DEM_BYPASS_DATA1 (WSA881X_DIGITAL_BASE+0x001A) +#define WSA881X_DEM_BYPASS_DATA2 (WSA881X_DIGITAL_BASE+0x001B) +#define WSA881X_DEM_BYPASS_DATA3 (WSA881X_DIGITAL_BASE+0x001C) +#define WSA881X_OTP_CTRL0 (WSA881X_DIGITAL_BASE+0x001D) +#define WSA881X_OTP_CTRL1 (WSA881X_DIGITAL_BASE+0x001E) +#define WSA881X_HDRIVE_CTL_GROUP1 (WSA881X_DIGITAL_BASE+0x001F) +#define WSA881X_INTR_MODE (WSA881X_DIGITAL_BASE+0x0020) +#define WSA881X_INTR_MASK (WSA881X_DIGITAL_BASE+0x0021) +#define WSA881X_INTR_STATUS (WSA881X_DIGITAL_BASE+0x0022) +#define WSA881X_INTR_CLEAR (WSA881X_DIGITAL_BASE+0x0023) +#define WSA881X_INTR_LEVEL (WSA881X_DIGITAL_BASE+0x0024) +#define WSA881X_INTR_SET (WSA881X_DIGITAL_BASE+0x0025) +#define WSA881X_INTR_TEST (WSA881X_DIGITAL_BASE+0x0026) +#define WSA881X_PDM_TEST_MODE (WSA881X_DIGITAL_BASE+0x0030) +#define WSA881X_ATE_TEST_MODE (WSA881X_DIGITAL_BASE+0x0031) +#define WSA881X_PIN_CTL_MODE (WSA881X_DIGITAL_BASE+0x0032) +#define WSA881X_PIN_CTL_OE (WSA881X_DIGITAL_BASE+0x0033) +#define WSA881X_PIN_WDATA_IOPAD (WSA881X_DIGITAL_BASE+0x0034) +#define WSA881X_PIN_STATUS (WSA881X_DIGITAL_BASE+0x0035) +#define WSA881X_DIG_DEBUG_MODE (WSA881X_DIGITAL_BASE+0x0037) +#define WSA881X_DIG_DEBUG_SEL (WSA881X_DIGITAL_BASE+0x0038) +#define WSA881X_DIG_DEBUG_EN (WSA881X_DIGITAL_BASE+0x0039) +#define WSA881X_SWR_HM_TEST1 (WSA881X_DIGITAL_BASE+0x003B) +#define WSA881X_SWR_HM_TEST2 (WSA881X_DIGITAL_BASE+0x003C) +#define WSA881X_TEMP_DETECT_DBG_CTL (WSA881X_DIGITAL_BASE+0x003D) +#define WSA881X_TEMP_DEBUG_MSB (WSA881X_DIGITAL_BASE+0x003E) +#define WSA881X_TEMP_DEBUG_LSB (WSA881X_DIGITAL_BASE+0x003F) +#define WSA881X_SAMPLE_EDGE_SEL (WSA881X_DIGITAL_BASE+0x0044) +#define WSA881X_IOPAD_CTL (WSA881X_DIGITAL_BASE+0x0045) +#define WSA881X_SPARE_0 (WSA881X_DIGITAL_BASE+0x0050) +#define WSA881X_SPARE_1 (WSA881X_DIGITAL_BASE+0x0051) +#define WSA881X_SPARE_2 (WSA881X_DIGITAL_BASE+0x0052) +#define WSA881X_OTP_REG_0 (WSA881X_DIGITAL_BASE+0x0080) +#define WSA881X_OTP_REG_1 (WSA881X_DIGITAL_BASE+0x0081) +#define WSA881X_OTP_REG_2 (WSA881X_DIGITAL_BASE+0x0082) +#define WSA881X_OTP_REG_3 (WSA881X_DIGITAL_BASE+0x0083) +#define WSA881X_OTP_REG_4 (WSA881X_DIGITAL_BASE+0x0084) +#define WSA881X_OTP_REG_5 (WSA881X_DIGITAL_BASE+0x0085) +#define WSA881X_OTP_REG_6 (WSA881X_DIGITAL_BASE+0x0086) +#define WSA881X_OTP_REG_7 (WSA881X_DIGITAL_BASE+0x0087) +#define WSA881X_OTP_REG_8 (WSA881X_DIGITAL_BASE+0x0088) +#define WSA881X_OTP_REG_9 (WSA881X_DIGITAL_BASE+0x0089) +#define WSA881X_OTP_REG_10 (WSA881X_DIGITAL_BASE+0x008A) +#define WSA881X_OTP_REG_11 (WSA881X_DIGITAL_BASE+0x008B) +#define WSA881X_OTP_REG_12 (WSA881X_DIGITAL_BASE+0x008C) +#define WSA881X_OTP_REG_13 (WSA881X_DIGITAL_BASE+0x008D) +#define WSA881X_OTP_REG_14 (WSA881X_DIGITAL_BASE+0x008E) +#define WSA881X_OTP_REG_15 (WSA881X_DIGITAL_BASE+0x008F) +#define WSA881X_OTP_REG_16 (WSA881X_DIGITAL_BASE+0x0090) +#define WSA881X_OTP_REG_17 (WSA881X_DIGITAL_BASE+0x0091) +#define WSA881X_OTP_REG_18 (WSA881X_DIGITAL_BASE+0x0092) +#define WSA881X_OTP_REG_19 (WSA881X_DIGITAL_BASE+0x0093) +#define WSA881X_OTP_REG_20 (WSA881X_DIGITAL_BASE+0x0094) +#define WSA881X_OTP_REG_21 (WSA881X_DIGITAL_BASE+0x0095) +#define WSA881X_OTP_REG_22 (WSA881X_DIGITAL_BASE+0x0096) +#define WSA881X_OTP_REG_23 (WSA881X_DIGITAL_BASE+0x0097) +#define WSA881X_OTP_REG_24 (WSA881X_DIGITAL_BASE+0x0098) +#define WSA881X_OTP_REG_25 (WSA881X_DIGITAL_BASE+0x0099) +#define WSA881X_OTP_REG_26 (WSA881X_DIGITAL_BASE+0x009A) +#define WSA881X_OTP_REG_27 (WSA881X_DIGITAL_BASE+0x009B) +#define WSA881X_OTP_REG_28 (WSA881X_DIGITAL_BASE+0x009C) +#define WSA881X_OTP_REG_29 (WSA881X_DIGITAL_BASE+0x009D) +#define WSA881X_OTP_REG_30 (WSA881X_DIGITAL_BASE+0x009E) +#define WSA881X_OTP_REG_31 (WSA881X_DIGITAL_BASE+0x009F) +#define WSA881X_OTP_REG_32 (WSA881X_DIGITAL_BASE+0x00A0) +#define WSA881X_OTP_REG_33 (WSA881X_DIGITAL_BASE+0x00A1) +#define WSA881X_OTP_REG_34 (WSA881X_DIGITAL_BASE+0x00A2) +#define WSA881X_OTP_REG_35 (WSA881X_DIGITAL_BASE+0x00A3) +#define WSA881X_OTP_REG_36 (WSA881X_DIGITAL_BASE+0x00A4) +#define WSA881X_OTP_REG_37 (WSA881X_DIGITAL_BASE+0x00A5) +#define WSA881X_OTP_REG_38 (WSA881X_DIGITAL_BASE+0x00A6) +#define WSA881X_OTP_REG_39 (WSA881X_DIGITAL_BASE+0x00A7) +#define WSA881X_OTP_REG_40 (WSA881X_DIGITAL_BASE+0x00A8) +#define WSA881X_OTP_REG_41 (WSA881X_DIGITAL_BASE+0x00A9) +#define WSA881X_OTP_REG_42 (WSA881X_DIGITAL_BASE+0x00AA) +#define WSA881X_OTP_REG_43 (WSA881X_DIGITAL_BASE+0x00AB) +#define WSA881X_OTP_REG_44 (WSA881X_DIGITAL_BASE+0x00AC) +#define WSA881X_OTP_REG_45 (WSA881X_DIGITAL_BASE+0x00AD) +#define WSA881X_OTP_REG_46 (WSA881X_DIGITAL_BASE+0x00AE) +#define WSA881X_OTP_REG_47 (WSA881X_DIGITAL_BASE+0x00AF) +#define WSA881X_OTP_REG_48 (WSA881X_DIGITAL_BASE+0x00B0) +#define WSA881X_OTP_REG_49 (WSA881X_DIGITAL_BASE+0x00B1) +#define WSA881X_OTP_REG_50 (WSA881X_DIGITAL_BASE+0x00B2) +#define WSA881X_OTP_REG_51 (WSA881X_DIGITAL_BASE+0x00B3) +#define WSA881X_OTP_REG_52 (WSA881X_DIGITAL_BASE+0x00B4) +#define WSA881X_OTP_REG_53 (WSA881X_DIGITAL_BASE+0x00B5) +#define WSA881X_OTP_REG_54 (WSA881X_DIGITAL_BASE+0x00B6) +#define WSA881X_OTP_REG_55 (WSA881X_DIGITAL_BASE+0x00B7) +#define WSA881X_OTP_REG_56 (WSA881X_DIGITAL_BASE+0x00B8) +#define WSA881X_OTP_REG_57 (WSA881X_DIGITAL_BASE+0x00B9) +#define WSA881X_OTP_REG_58 (WSA881X_DIGITAL_BASE+0x00BA) +#define WSA881X_OTP_REG_59 (WSA881X_DIGITAL_BASE+0x00BB) +#define WSA881X_OTP_REG_60 (WSA881X_DIGITAL_BASE+0x00BC) +#define WSA881X_OTP_REG_61 (WSA881X_DIGITAL_BASE+0x00BD) +#define WSA881X_OTP_REG_62 (WSA881X_DIGITAL_BASE+0x00BE) +#define WSA881X_OTP_REG_63 (WSA881X_DIGITAL_BASE+0x00BF) +/* Analog Register address space */ +#define WSA881X_BIAS_REF_CTRL (WSA881X_ANALOG_BASE+0x0000) +#define WSA881X_BIAS_TEST (WSA881X_ANALOG_BASE+0x0001) +#define WSA881X_BIAS_BIAS (WSA881X_ANALOG_BASE+0x0002) +#define WSA881X_TEMP_OP (WSA881X_ANALOG_BASE+0x0003) +#define WSA881X_TEMP_IREF_CTRL (WSA881X_ANALOG_BASE+0x0004) +#define WSA881X_TEMP_ISENS_CTRL (WSA881X_ANALOG_BASE+0x0005) +#define WSA881X_TEMP_CLK_CTRL (WSA881X_ANALOG_BASE+0x0006) +#define WSA881X_TEMP_TEST (WSA881X_ANALOG_BASE+0x0007) +#define WSA881X_TEMP_BIAS (WSA881X_ANALOG_BASE+0x0008) +#define WSA881X_TEMP_ADC_CTRL (WSA881X_ANALOG_BASE+0x0009) +#define WSA881X_TEMP_DOUT_MSB (WSA881X_ANALOG_BASE+0x000A) +#define WSA881X_TEMP_DOUT_LSB (WSA881X_ANALOG_BASE+0x000B) +#define WSA881X_ADC_EN_MODU_V (WSA881X_ANALOG_BASE+0x0010) +#define WSA881X_ADC_EN_MODU_I (WSA881X_ANALOG_BASE+0x0011) +#define WSA881X_ADC_EN_DET_TEST_V (WSA881X_ANALOG_BASE+0x0012) +#define WSA881X_ADC_EN_DET_TEST_I (WSA881X_ANALOG_BASE+0x0013) +#define WSA881X_ADC_SEL_IBIAS (WSA881X_ANALOG_BASE+0x0014) +#define WSA881X_ADC_EN_SEL_IBIAS (WSA881X_ANALOG_BASE+0x0015) +#define WSA881X_SPKR_DRV_EN (WSA881X_ANALOG_BASE+0x001A) +#define WSA881X_SPKR_DRV_GAIN (WSA881X_ANALOG_BASE+0x001B) +#define WSA881X_SPKR_DAC_CTL (WSA881X_ANALOG_BASE+0x001C) +#define WSA881X_SPKR_DRV_DBG (WSA881X_ANALOG_BASE+0x001D) +#define WSA881X_SPKR_PWRSTG_DBG (WSA881X_ANALOG_BASE+0x001E) +#define WSA881X_SPKR_OCP_CTL (WSA881X_ANALOG_BASE+0x001F) +#define WSA881X_SPKR_CLIP_CTL (WSA881X_ANALOG_BASE+0x0020) +#define WSA881X_SPKR_BBM_CTL (WSA881X_ANALOG_BASE+0x0021) +#define WSA881X_SPKR_MISC_CTL1 (WSA881X_ANALOG_BASE+0x0022) +#define WSA881X_SPKR_MISC_CTL2 (WSA881X_ANALOG_BASE+0x0023) +#define WSA881X_SPKR_BIAS_INT (WSA881X_ANALOG_BASE+0x0024) +#define WSA881X_SPKR_PA_INT (WSA881X_ANALOG_BASE+0x0025) +#define WSA881X_SPKR_BIAS_CAL (WSA881X_ANALOG_BASE+0x0026) +#define WSA881X_SPKR_BIAS_PSRR (WSA881X_ANALOG_BASE+0x0027) +#define WSA881X_SPKR_STATUS1 (WSA881X_ANALOG_BASE+0x0028) +#define WSA881X_SPKR_STATUS2 (WSA881X_ANALOG_BASE+0x0029) +#define WSA881X_BOOST_EN_CTL (WSA881X_ANALOG_BASE+0x002A) +#define WSA881X_BOOST_CURRENT_LIMIT (WSA881X_ANALOG_BASE+0x002B) +#define WSA881X_BOOST_PS_CTL (WSA881X_ANALOG_BASE+0x002C) +#define WSA881X_BOOST_PRESET_OUT1 (WSA881X_ANALOG_BASE+0x002D) +#define WSA881X_BOOST_PRESET_OUT2 (WSA881X_ANALOG_BASE+0x002E) +#define WSA881X_BOOST_FORCE_OUT (WSA881X_ANALOG_BASE+0x002F) +#define WSA881X_BOOST_LDO_PROG (WSA881X_ANALOG_BASE+0x0030) +#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB (WSA881X_ANALOG_BASE+0x0031) +#define WSA881X_BOOST_RON_CTL (WSA881X_ANALOG_BASE+0x0032) +#define WSA881X_BOOST_LOOP_STABILITY (WSA881X_ANALOG_BASE+0x0033) +#define WSA881X_BOOST_ZX_CTL (WSA881X_ANALOG_BASE+0x0034) +#define WSA881X_BOOST_START_CTL (WSA881X_ANALOG_BASE+0x0035) +#define WSA881X_BOOST_MISC1_CTL (WSA881X_ANALOG_BASE+0x0036) +#define WSA881X_BOOST_MISC2_CTL (WSA881X_ANALOG_BASE+0x0037) +#define WSA881X_BOOST_MISC3_CTL (WSA881X_ANALOG_BASE+0x0038) +#define WSA881X_BOOST_ATEST_CTL (WSA881X_ANALOG_BASE+0x0039) +#define WSA881X_SPKR_PROT_FE_GAIN (WSA881X_ANALOG_BASE+0x003A) +#define WSA881X_SPKR_PROT_FE_CM_LDO_SET (WSA881X_ANALOG_BASE+0x003B) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x003C) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 (WSA881X_ANALOG_BASE+0x003D) +#define WSA881X_SPKR_PROT_ATEST1 (WSA881X_ANALOG_BASE+0x003E) +#define WSA881X_SPKR_PROT_ATEST2 (WSA881X_ANALOG_BASE+0x003F) +#define WSA881X_SPKR_PROT_FE_VSENSE_VCM (WSA881X_ANALOG_BASE+0x0040) +#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x0041) +#define WSA881X_BONGO_RESRV_REG1 (WSA881X_ANALOG_BASE+0x0042) +#define WSA881X_BONGO_RESRV_REG2 (WSA881X_ANALOG_BASE+0x0043) +#define WSA881X_SPKR_PROT_SAR (WSA881X_ANALOG_BASE+0x0044) +#define WSA881X_SPKR_STATUS3 (WSA881X_ANALOG_BASE+0x0045) + +#define WSA881X_NUM_REGISTERS (WSA881X_SPKR_STATUS3+1) +#define WSA881X_MAX_REGISTER (WSA881X_NUM_REGISTERS-1) +#define WSA881X_CACHE_SIZE WSA881X_NUM_REGISTERS +#endif /* WSA881X_REGISTERS_H */ diff --git a/sound/soc/codecs/wsa881x-registers.h b/sound/soc/codecs/wsa881x-registers.h new file mode 100644 index 000000000000..825a5f034da8 --- /dev/null +++ b/sound/soc/codecs/wsa881x-registers.h @@ -0,0 +1,178 @@ +/* Copyright (c) 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 WSA881X_REGISTERS_H +#define WSA881X_REGISTERS_H + +#define WSA881X_DIGITAL_BASE 0x3000 +#define WSA881X_ANALOG_BASE 0x3100 + +/* Digital register address space */ +#define WSA881X_CHIP_ID0 (WSA881X_DIGITAL_BASE+0x0000) +#define WSA881X_CHIP_ID1 (WSA881X_DIGITAL_BASE+0x0001) +#define WSA881X_CHIP_ID2 (WSA881X_DIGITAL_BASE+0x0002) +#define WSA881X_CHIP_ID3 (WSA881X_DIGITAL_BASE+0x0003) +#define WSA881X_BUS_ID (WSA881X_DIGITAL_BASE+0x0004) +#define WSA881X_CDC_RST_CTL (WSA881X_DIGITAL_BASE+0x0005) +#define WSA881X_CDC_TOP_CLK_CTL (WSA881X_DIGITAL_BASE+0x0006) +#define WSA881X_CDC_ANA_CLK_CTL (WSA881X_DIGITAL_BASE+0x0007) +#define WSA881X_CDC_DIG_CLK_CTL (WSA881X_DIGITAL_BASE+0x0008) +#define WSA881X_CLOCK_CONFIG (WSA881X_DIGITAL_BASE+0x0009) +#define WSA881X_ANA_CTL (WSA881X_DIGITAL_BASE+0x000A) +#define WSA881X_SWR_RESET_EN (WSA881X_DIGITAL_BASE+0x000B) +#define WSA881X_RESET_CTL (WSA881X_DIGITAL_BASE+0x000C) +#define WSA881X_TADC_VALUE_CTL (WSA881X_DIGITAL_BASE+0x000F) +#define WSA881X_TEMP_DETECT_CTL (WSA881X_DIGITAL_BASE+0x0010) +#define WSA881X_TEMP_MSB (WSA881X_DIGITAL_BASE+0x0011) +#define WSA881X_TEMP_LSB (WSA881X_DIGITAL_BASE+0x0012) +#define WSA881X_TEMP_CONFIG0 (WSA881X_DIGITAL_BASE+0x0013) +#define WSA881X_TEMP_CONFIG1 (WSA881X_DIGITAL_BASE+0x0014) +#define WSA881X_CDC_CLIP_CTL (WSA881X_DIGITAL_BASE+0x0015) +#define WSA881X_SDM_PDM9_LSB (WSA881X_DIGITAL_BASE+0x0016) +#define WSA881X_SDM_PDM9_MSB (WSA881X_DIGITAL_BASE+0x0017) +#define WSA881X_CDC_RX_CTL (WSA881X_DIGITAL_BASE+0x0018) +#define WSA881X_DEM_BYPASS_DATA0 (WSA881X_DIGITAL_BASE+0x0019) +#define WSA881X_DEM_BYPASS_DATA1 (WSA881X_DIGITAL_BASE+0x001A) +#define WSA881X_DEM_BYPASS_DATA2 (WSA881X_DIGITAL_BASE+0x001B) +#define WSA881X_DEM_BYPASS_DATA3 (WSA881X_DIGITAL_BASE+0x001C) +#define WSA881X_OTP_CTRL0 (WSA881X_DIGITAL_BASE+0x001D) +#define WSA881X_OTP_CTRL1 (WSA881X_DIGITAL_BASE+0x001E) +#define WSA881X_HDRIVE_CTL_GROUP1 (WSA881X_DIGITAL_BASE+0x001F) +#define WSA881X_INTR_MODE (WSA881X_DIGITAL_BASE+0x0020) +#define WSA881X_INTR_MASK (WSA881X_DIGITAL_BASE+0x0021) +#define WSA881X_INTR_STATUS (WSA881X_DIGITAL_BASE+0x0022) +#define WSA881X_INTR_CLEAR (WSA881X_DIGITAL_BASE+0x0023) +#define WSA881X_INTR_LEVEL (WSA881X_DIGITAL_BASE+0x0024) +#define WSA881X_INTR_SET (WSA881X_DIGITAL_BASE+0x0025) +#define WSA881X_INTR_TEST (WSA881X_DIGITAL_BASE+0x0026) +#define WSA881X_PDM_TEST_MODE (WSA881X_DIGITAL_BASE+0x0030) +#define WSA881X_ATE_TEST_MODE (WSA881X_DIGITAL_BASE+0x0031) +#define WSA881X_PIN_CTL_MODE (WSA881X_DIGITAL_BASE+0x0032) +#define WSA881X_PIN_CTL_OE (WSA881X_DIGITAL_BASE+0x0033) +#define WSA881X_PIN_WDATA_IOPAD (WSA881X_DIGITAL_BASE+0x0034) +#define WSA881X_PIN_STATUS (WSA881X_DIGITAL_BASE+0x0035) +#define WSA881X_DIG_DEBUG_MODE (WSA881X_DIGITAL_BASE+0x0037) +#define WSA881X_DIG_DEBUG_SEL (WSA881X_DIGITAL_BASE+0x0038) +#define WSA881X_DIG_DEBUG_EN (WSA881X_DIGITAL_BASE+0x0039) +#define WSA881X_SWR_HM_TEST1 (WSA881X_DIGITAL_BASE+0x003B) +#define WSA881X_SWR_HM_TEST2 (WSA881X_DIGITAL_BASE+0x003C) +#define WSA881X_TEMP_DETECT_DBG_CTL (WSA881X_DIGITAL_BASE+0x003D) +#define WSA881X_TEMP_DEBUG_MSB (WSA881X_DIGITAL_BASE+0x003E) +#define WSA881X_TEMP_DEBUG_LSB (WSA881X_DIGITAL_BASE+0x003F) +#define WSA881X_SAMPLE_EDGE_SEL (WSA881X_DIGITAL_BASE+0x0044) +#define WSA881X_IOPAD_CTL (WSA881X_DIGITAL_BASE+0x0045) +#define WSA881X_SPARE_0 (WSA881X_DIGITAL_BASE+0x0050) +#define WSA881X_SPARE_1 (WSA881X_DIGITAL_BASE+0x0051) +#define WSA881X_SPARE_2 (WSA881X_DIGITAL_BASE+0x0052) +#define WSA881X_OTP_REG_0 (WSA881X_DIGITAL_BASE+0x0080) +#define WSA881X_OTP_REG_1 (WSA881X_DIGITAL_BASE+0x0081) +#define WSA881X_OTP_REG_2 (WSA881X_DIGITAL_BASE+0x0082) +#define WSA881X_OTP_REG_3 (WSA881X_DIGITAL_BASE+0x0083) +#define WSA881X_OTP_REG_4 (WSA881X_DIGITAL_BASE+0x0084) +#define WSA881X_OTP_REG_5 (WSA881X_DIGITAL_BASE+0x0085) +#define WSA881X_OTP_REG_6 (WSA881X_DIGITAL_BASE+0x0086) +#define WSA881X_OTP_REG_7 (WSA881X_DIGITAL_BASE+0x0087) +#define WSA881X_OTP_REG_8 (WSA881X_DIGITAL_BASE+0x0088) +#define WSA881X_OTP_REG_9 (WSA881X_DIGITAL_BASE+0x0089) +#define WSA881X_OTP_REG_10 (WSA881X_DIGITAL_BASE+0x008A) +#define WSA881X_OTP_REG_11 (WSA881X_DIGITAL_BASE+0x008B) +#define WSA881X_OTP_REG_12 (WSA881X_DIGITAL_BASE+0x008C) +#define WSA881X_OTP_REG_13 (WSA881X_DIGITAL_BASE+0x008D) +#define WSA881X_OTP_REG_14 (WSA881X_DIGITAL_BASE+0x008E) +#define WSA881X_OTP_REG_15 (WSA881X_DIGITAL_BASE+0x008F) +#define WSA881X_OTP_REG_16 (WSA881X_DIGITAL_BASE+0x0090) +#define WSA881X_OTP_REG_17 (WSA881X_DIGITAL_BASE+0x0091) +#define WSA881X_OTP_REG_18 (WSA881X_DIGITAL_BASE+0x0092) +#define WSA881X_OTP_REG_19 (WSA881X_DIGITAL_BASE+0x0093) +#define WSA881X_OTP_REG_20 (WSA881X_DIGITAL_BASE+0x0094) +#define WSA881X_OTP_REG_21 (WSA881X_DIGITAL_BASE+0x0095) +#define WSA881X_OTP_REG_22 (WSA881X_DIGITAL_BASE+0x0096) +#define WSA881X_OTP_REG_23 (WSA881X_DIGITAL_BASE+0x0097) +#define WSA881X_OTP_REG_24 (WSA881X_DIGITAL_BASE+0x0098) +#define WSA881X_OTP_REG_25 (WSA881X_DIGITAL_BASE+0x0099) +#define WSA881X_OTP_REG_26 (WSA881X_DIGITAL_BASE+0x009A) +#define WSA881X_OTP_REG_27 (WSA881X_DIGITAL_BASE+0x009B) +#define WSA881X_OTP_REG_28 (WSA881X_DIGITAL_BASE+0x009C) +#define WSA881X_OTP_REG_29 (WSA881X_DIGITAL_BASE+0x009D) +#define WSA881X_OTP_REG_30 (WSA881X_DIGITAL_BASE+0x009E) +#define WSA881X_OTP_REG_31 (WSA881X_DIGITAL_BASE+0x009F) +#define WSA881X_OTP_REG_63 (WSA881X_DIGITAL_BASE+0x00BF) + +/* Analog Register address space */ +#define WSA881X_BIAS_REF_CTRL (WSA881X_ANALOG_BASE+0x0000) +#define WSA881X_BIAS_TEST (WSA881X_ANALOG_BASE+0x0001) +#define WSA881X_BIAS_BIAS (WSA881X_ANALOG_BASE+0x0002) +#define WSA881X_TEMP_OP (WSA881X_ANALOG_BASE+0x0003) +#define WSA881X_TEMP_IREF_CTRL (WSA881X_ANALOG_BASE+0x0004) +#define WSA881X_TEMP_ISENS_CTRL (WSA881X_ANALOG_BASE+0x0005) +#define WSA881X_TEMP_CLK_CTRL (WSA881X_ANALOG_BASE+0x0006) +#define WSA881X_TEMP_TEST (WSA881X_ANALOG_BASE+0x0007) +#define WSA881X_TEMP_BIAS (WSA881X_ANALOG_BASE+0x0008) +#define WSA881X_TEMP_ADC_CTRL (WSA881X_ANALOG_BASE+0x0009) +#define WSA881X_TEMP_DOUT_MSB (WSA881X_ANALOG_BASE+0x000A) +#define WSA881X_TEMP_DOUT_LSB (WSA881X_ANALOG_BASE+0x000B) +#define WSA881X_ADC_EN_MODU_V (WSA881X_ANALOG_BASE+0x0010) +#define WSA881X_ADC_EN_MODU_I (WSA881X_ANALOG_BASE+0x0011) +#define WSA881X_ADC_EN_DET_TEST_V (WSA881X_ANALOG_BASE+0x0012) +#define WSA881X_ADC_EN_DET_TEST_I (WSA881X_ANALOG_BASE+0x0013) +#define WSA881X_ADC_SEL_IBIAS (WSA881X_ANALOG_BASE+0x0014) +#define WSA881X_ADC_EN_SEL_IBAIS (WSA881X_ANALOG_BASE+0x0015) +#define WSA881X_SPKR_DRV_EN (WSA881X_ANALOG_BASE+0x001A) +#define WSA881X_SPKR_DRV_GAIN (WSA881X_ANALOG_BASE+0x001B) +#define WSA881X_SPKR_DAC_CTL (WSA881X_ANALOG_BASE+0x001C) +#define WSA881X_SPKR_DRV_DBG (WSA881X_ANALOG_BASE+0x001D) +#define WSA881X_SPKR_PWRSTG_DBG (WSA881X_ANALOG_BASE+0x001E) +#define WSA881X_SPKR_OCP_CTL (WSA881X_ANALOG_BASE+0x001F) +#define WSA881X_SPKR_CLIP_CTL (WSA881X_ANALOG_BASE+0x0020) +#define WSA881X_SPKR_BBM_CTL (WSA881X_ANALOG_BASE+0x0021) +#define WSA881X_SPKR_MISC_CTL1 (WSA881X_ANALOG_BASE+0x0022) +#define WSA881X_SPKR_MISC_CTL2 (WSA881X_ANALOG_BASE+0x0023) +#define WSA881X_SPKR_BIAS_INT (WSA881X_ANALOG_BASE+0x0024) +#define WSA881X_SPKR_PA_INT (WSA881X_ANALOG_BASE+0x0025) +#define WSA881X_SPKR_BIAS_CAL (WSA881X_ANALOG_BASE+0x0026) +#define WSA881X_SPKR_BIAS_PSRR (WSA881X_ANALOG_BASE+0x0027) +#define WSA881X_SPKR_STATUS1 (WSA881X_ANALOG_BASE+0x0028) +#define WSA881X_SPKR_STATUS2 (WSA881X_ANALOG_BASE+0x0029) +#define WSA881X_BOOST_EN_CTL (WSA881X_ANALOG_BASE+0x002A) +#define WSA881X_BOOST_CURRENT_LIMIT (WSA881X_ANALOG_BASE+0x002B) +#define WSA881X_BOOST_PS_CTL (WSA881X_ANALOG_BASE+0x002C) +#define WSA881X_BOOST_PRESET_OUT1 (WSA881X_ANALOG_BASE+0x002D) +#define WSA881X_BOOST_PRESET_OUT2 (WSA881X_ANALOG_BASE+0x002E) +#define WSA881X_BOOST_FORCE_OUT (WSA881X_ANALOG_BASE+0x002F) +#define WSA881X_BOOST_LDO_PROG (WSA881X_ANALOG_BASE+0x0030) +#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB (WSA881X_ANALOG_BASE+0x0031) +#define WSA881X_BOOST_RON_CTL (WSA881X_ANALOG_BASE+0x0032) +#define WSA881X_BOOST_LOOP_STABILITY (WSA881X_ANALOG_BASE+0x0033) +#define WSA881X_BOOST_ZX_CTL (WSA881X_ANALOG_BASE+0x0034) +#define WSA881X_BOOST_START_CTL (WSA881X_ANALOG_BASE+0x0035) +#define WSA881X_BOOST_MISC1_CTL (WSA881X_ANALOG_BASE+0x0036) +#define WSA881X_BOOST_MISC2_CTL (WSA881X_ANALOG_BASE+0x0037) +#define WSA881X_BOOST_MISC3_CTL (WSA881X_ANALOG_BASE+0x0038) +#define WSA881X_BOOST_ATEST_CTL (WSA881X_ANALOG_BASE+0x0039) +#define WSA881X_SPKR_PROT_FE_GAIN (WSA881X_ANALOG_BASE+0x003A) +#define WSA881X_SPKR_PROT_FE_CM_LDO_SET (WSA881X_ANALOG_BASE+0x003B) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x003C) +#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 (WSA881X_ANALOG_BASE+0x003D) +#define WSA881X_SPKR_PROT_ATEST1 (WSA881X_ANALOG_BASE+0x003E) +#define WSA881X_SPKR_PROT_ATEST2 (WSA881X_ANALOG_BASE+0x003F) +#define WSA881X_SPKR_PROT_FE_VSENSE_VCM (WSA881X_ANALOG_BASE+0x0040) +#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x0041) +#define WSA881X_BONGO_RESRV_REG1 (WSA881X_ANALOG_BASE+0x0042) +#define WSA881X_BONGO_RESRV_REG2 (WSA881X_ANALOG_BASE+0x0043) +#define WSA881X_SPKR_PROT_SAR (WSA881X_ANALOG_BASE+0x0044) +#define WSA881X_SPKR_STATUS3 (WSA881X_ANALOG_BASE+0x0045) + +#define WSA881X_NUM_REGISTERS (WSA881X_SPKR_STATUS3+1) +#define WSA881X_MAX_REGISTER (WSA881X_NUM_REGISTERS-1) +#define WSA881X_CACHE_SIZE WSA881X_NUM_REGISTERS + +#endif /* WSA881X_REGISTERS_H */ diff --git a/sound/soc/codecs/wsa881x-regmap-analog.c b/sound/soc/codecs/wsa881x-regmap-analog.c new file mode 100644 index 000000000000..2bc3c9eb7061 --- /dev/null +++ b/sound/soc/codecs/wsa881x-regmap-analog.c @@ -0,0 +1,499 @@ +/* + * 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 "wsa881x-registers-analog.h" +#include "wsa881x-analog.h" + +struct reg_default wsa881x_ana_reg_defaults[] = { + {WSA881X_CHIP_ID0, 0x00}, + {WSA881X_CHIP_ID1, 0x00}, + {WSA881X_CHIP_ID2, 0x00}, + {WSA881X_CHIP_ID3, 0x02}, + {WSA881X_BUS_ID, 0x00}, + {WSA881X_CDC_RST_CTL, 0x00}, + {WSA881X_CDC_TOP_CLK_CTL, 0x03}, + {WSA881X_CDC_ANA_CLK_CTL, 0x00}, + {WSA881X_CDC_DIG_CLK_CTL, 0x00}, + {WSA881X_CLOCK_CONFIG, 0x00}, + {WSA881X_ANA_CTL, 0x08}, + {WSA881X_SWR_RESET_EN, 0x00}, + {WSA881X_TEMP_DETECT_CTL, 0x01}, + {WSA881X_TEMP_MSB, 0x00}, + {WSA881X_TEMP_LSB, 0x00}, + {WSA881X_TEMP_CONFIG0, 0x00}, + {WSA881X_TEMP_CONFIG1, 0x00}, + {WSA881X_CDC_CLIP_CTL, 0x03}, + {WSA881X_SDM_PDM9_LSB, 0x00}, + {WSA881X_SDM_PDM9_MSB, 0x00}, + {WSA881X_CDC_RX_CTL, 0x7E}, + {WSA881X_DEM_BYPASS_DATA0, 0x00}, + {WSA881X_DEM_BYPASS_DATA1, 0x00}, + {WSA881X_DEM_BYPASS_DATA2, 0x00}, + {WSA881X_DEM_BYPASS_DATA3, 0x00}, + {WSA881X_OTP_CTRL0, 0x00}, + {WSA881X_OTP_CTRL1, 0x00}, + {WSA881X_HDRIVE_CTL_GROUP1, 0x00}, + {WSA881X_INTR_MODE, 0x00}, + {WSA881X_INTR_MASK, 0x1F}, + {WSA881X_INTR_STATUS, 0x00}, + {WSA881X_INTR_CLEAR, 0x00}, + {WSA881X_INTR_LEVEL, 0x00}, + {WSA881X_INTR_SET, 0x00}, + {WSA881X_INTR_TEST, 0x00}, + {WSA881X_PDM_TEST_MODE, 0x00}, + {WSA881X_ATE_TEST_MODE, 0x00}, + {WSA881X_PIN_CTL_MODE, 0x00}, + {WSA881X_PIN_CTL_OE, 0x00}, + {WSA881X_PIN_WDATA_IOPAD, 0x00}, + {WSA881X_PIN_STATUS, 0x00}, + {WSA881X_DIG_DEBUG_MODE, 0x00}, + {WSA881X_DIG_DEBUG_SEL, 0x00}, + {WSA881X_DIG_DEBUG_EN, 0x00}, + {WSA881X_SWR_HM_TEST1, 0x08}, + {WSA881X_SWR_HM_TEST2, 0x00}, + {WSA881X_TEMP_DETECT_DBG_CTL, 0x00}, + {WSA881X_TEMP_DEBUG_MSB, 0x00}, + {WSA881X_TEMP_DEBUG_LSB, 0x00}, + {WSA881X_SAMPLE_EDGE_SEL, 0x0C}, + {WSA881X_SPARE_0, 0x00}, + {WSA881X_SPARE_1, 0x00}, + {WSA881X_SPARE_2, 0x00}, + {WSA881X_OTP_REG_0, 0x01}, + {WSA881X_OTP_REG_1, 0xFF}, + {WSA881X_OTP_REG_2, 0xC0}, + {WSA881X_OTP_REG_3, 0xFF}, + {WSA881X_OTP_REG_4, 0xC0}, + {WSA881X_OTP_REG_5, 0xFF}, + {WSA881X_OTP_REG_6, 0xFF}, + {WSA881X_OTP_REG_7, 0xFF}, + {WSA881X_OTP_REG_8, 0xFF}, + {WSA881X_OTP_REG_9, 0xFF}, + {WSA881X_OTP_REG_10, 0xFF}, + {WSA881X_OTP_REG_11, 0xFF}, + {WSA881X_OTP_REG_12, 0xFF}, + {WSA881X_OTP_REG_13, 0xFF}, + {WSA881X_OTP_REG_14, 0xFF}, + {WSA881X_OTP_REG_15, 0xFF}, + {WSA881X_OTP_REG_16, 0xFF}, + {WSA881X_OTP_REG_17, 0xFF}, + {WSA881X_OTP_REG_18, 0xFF}, + {WSA881X_OTP_REG_19, 0xFF}, + {WSA881X_OTP_REG_20, 0xFF}, + {WSA881X_OTP_REG_21, 0xFF}, + {WSA881X_OTP_REG_22, 0xFF}, + {WSA881X_OTP_REG_23, 0xFF}, + {WSA881X_OTP_REG_24, 0x03}, + {WSA881X_OTP_REG_25, 0x01}, + {WSA881X_OTP_REG_26, 0x03}, + {WSA881X_OTP_REG_27, 0x11}, + {WSA881X_OTP_REG_28, 0xFF}, + {WSA881X_OTP_REG_29, 0xFF}, + {WSA881X_OTP_REG_30, 0xFF}, + {WSA881X_OTP_REG_31, 0xFF}, + {WSA881X_OTP_REG_63, 0x40}, + /* WSA881x Analog registers */ + {WSA881X_BIAS_REF_CTRL, 0x6C}, + {WSA881X_BIAS_TEST, 0x16}, + {WSA881X_BIAS_BIAS, 0xF0}, + {WSA881X_TEMP_OP, 0x00}, + {WSA881X_TEMP_IREF_CTRL, 0x56}, + {WSA881X_TEMP_ISENS_CTRL, 0x47}, + {WSA881X_TEMP_CLK_CTRL, 0x87}, + {WSA881X_TEMP_TEST, 0x00}, + {WSA881X_TEMP_BIAS, 0x51}, + {WSA881X_TEMP_ADC_CTRL, 0x00}, + {WSA881X_TEMP_DOUT_MSB, 0x00}, + {WSA881X_TEMP_DOUT_LSB, 0x00}, + {WSA881X_ADC_EN_MODU_V, 0x00}, + {WSA881X_ADC_EN_MODU_I, 0x00}, + {WSA881X_ADC_EN_DET_TEST_V, 0x00}, + {WSA881X_ADC_EN_DET_TEST_I, 0x00}, + {WSA881X_ADC_SEL_IBIAS, 0x25}, + {WSA881X_ADC_EN_SEL_IBIAS, 0x10}, + {WSA881X_SPKR_DRV_EN, 0x74}, + {WSA881X_SPKR_DRV_GAIN, 0x01}, + {WSA881X_SPKR_DAC_CTL, 0x40}, + {WSA881X_SPKR_DRV_DBG, 0x15}, + {WSA881X_SPKR_PWRSTG_DBG, 0x00}, + {WSA881X_SPKR_OCP_CTL, 0xD4}, + {WSA881X_SPKR_CLIP_CTL, 0x90}, + {WSA881X_SPKR_BBM_CTL, 0x00}, + {WSA881X_SPKR_MISC_CTL1, 0x80}, + {WSA881X_SPKR_MISC_CTL2, 0x00}, + {WSA881X_SPKR_BIAS_INT, 0x56}, + {WSA881X_SPKR_PA_INT, 0x54}, + {WSA881X_SPKR_BIAS_CAL, 0xAC}, + {WSA881X_SPKR_BIAS_PSRR, 0x54}, + {WSA881X_SPKR_STATUS1, 0x00}, + {WSA881X_SPKR_STATUS2, 0x00}, + {WSA881X_BOOST_EN_CTL, 0x18}, + {WSA881X_BOOST_CURRENT_LIMIT, 0x7A}, + {WSA881X_BOOST_PS_CTL, 0xC0}, + {WSA881X_BOOST_PRESET_OUT1, 0x77}, + {WSA881X_BOOST_PRESET_OUT2, 0x70}, + {WSA881X_BOOST_FORCE_OUT, 0x0E}, + {WSA881X_BOOST_LDO_PROG, 0x16}, + {WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71}, + {WSA881X_BOOST_RON_CTL, 0x0F}, + {WSA881X_BOOST_LOOP_STABILITY, 0xAD}, + {WSA881X_BOOST_ZX_CTL, 0x34}, + {WSA881X_BOOST_START_CTL, 0x23}, + {WSA881X_BOOST_MISC1_CTL, 0x80}, + {WSA881X_BOOST_MISC2_CTL, 0x00}, + {WSA881X_BOOST_MISC3_CTL, 0x00}, + {WSA881X_BOOST_ATEST_CTL, 0x00}, + {WSA881X_SPKR_PROT_FE_GAIN, 0x46}, + {WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D}, + {WSA881X_SPKR_PROT_ATEST1, 0x01}, + {WSA881X_SPKR_PROT_ATEST2, 0x00}, + {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D}, + {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D}, + {WSA881X_BONGO_RESRV_REG1, 0x00}, + {WSA881X_BONGO_RESRV_REG2, 0x00}, + {WSA881X_SPKR_PROT_SAR, 0x00}, + {WSA881X_SPKR_STATUS3, 0x00}, +}; + +struct reg_default wsa881x_ana_reg_defaults_0[] = { + {WSA881X_CHIP_ID0, 0x00}, + {WSA881X_CHIP_ID1, 0x00}, + {WSA881X_CHIP_ID2, 0x00}, + {WSA881X_CHIP_ID3, 0x02}, + {WSA881X_BUS_ID, 0x00}, + {WSA881X_CDC_RST_CTL, 0x00}, + {WSA881X_CDC_TOP_CLK_CTL, 0x03}, + {WSA881X_CDC_ANA_CLK_CTL, 0x00}, + {WSA881X_CDC_DIG_CLK_CTL, 0x00}, + {WSA881X_CLOCK_CONFIG, 0x00}, + {WSA881X_ANA_CTL, 0x08}, + {WSA881X_SWR_RESET_EN, 0x00}, + {WSA881X_TEMP_DETECT_CTL, 0x01}, + {WSA881X_TEMP_MSB, 0x00}, + {WSA881X_TEMP_LSB, 0x00}, + {WSA881X_TEMP_CONFIG0, 0x00}, + {WSA881X_TEMP_CONFIG1, 0x00}, + {WSA881X_CDC_CLIP_CTL, 0x03}, + {WSA881X_SDM_PDM9_LSB, 0x00}, + {WSA881X_SDM_PDM9_MSB, 0x00}, + {WSA881X_CDC_RX_CTL, 0x7E}, + {WSA881X_DEM_BYPASS_DATA0, 0x00}, + {WSA881X_DEM_BYPASS_DATA1, 0x00}, + {WSA881X_DEM_BYPASS_DATA2, 0x00}, + {WSA881X_DEM_BYPASS_DATA3, 0x00}, + {WSA881X_OTP_CTRL0, 0x00}, + {WSA881X_OTP_CTRL1, 0x00}, + {WSA881X_HDRIVE_CTL_GROUP1, 0x00}, + {WSA881X_INTR_MODE, 0x00}, + {WSA881X_INTR_MASK, 0x1F}, + {WSA881X_INTR_STATUS, 0x00}, + {WSA881X_INTR_CLEAR, 0x00}, + {WSA881X_INTR_LEVEL, 0x00}, + {WSA881X_INTR_SET, 0x00}, + {WSA881X_INTR_TEST, 0x00}, + {WSA881X_PDM_TEST_MODE, 0x00}, + {WSA881X_ATE_TEST_MODE, 0x00}, + {WSA881X_PIN_CTL_MODE, 0x00}, + {WSA881X_PIN_CTL_OE, 0x00}, + {WSA881X_PIN_WDATA_IOPAD, 0x00}, + {WSA881X_PIN_STATUS, 0x00}, + {WSA881X_DIG_DEBUG_MODE, 0x00}, + {WSA881X_DIG_DEBUG_SEL, 0x00}, + {WSA881X_DIG_DEBUG_EN, 0x00}, + {WSA881X_SWR_HM_TEST1, 0x08}, + {WSA881X_SWR_HM_TEST2, 0x00}, + {WSA881X_TEMP_DETECT_DBG_CTL, 0x00}, + {WSA881X_TEMP_DEBUG_MSB, 0x00}, + {WSA881X_TEMP_DEBUG_LSB, 0x00}, + {WSA881X_SAMPLE_EDGE_SEL, 0x0C}, + {WSA881X_SPARE_0, 0x00}, + {WSA881X_SPARE_1, 0x00}, + {WSA881X_SPARE_2, 0x00}, + {WSA881X_OTP_REG_0, 0x01}, + {WSA881X_OTP_REG_1, 0xFF}, + {WSA881X_OTP_REG_2, 0xC0}, + {WSA881X_OTP_REG_3, 0xFF}, + {WSA881X_OTP_REG_4, 0xC0}, + {WSA881X_OTP_REG_5, 0xFF}, + {WSA881X_OTP_REG_6, 0xFF}, + {WSA881X_OTP_REG_7, 0xFF}, + {WSA881X_OTP_REG_8, 0xFF}, + {WSA881X_OTP_REG_9, 0xFF}, + {WSA881X_OTP_REG_10, 0xFF}, + {WSA881X_OTP_REG_11, 0xFF}, + {WSA881X_OTP_REG_12, 0xFF}, + {WSA881X_OTP_REG_13, 0xFF}, + {WSA881X_OTP_REG_14, 0xFF}, + {WSA881X_OTP_REG_15, 0xFF}, + {WSA881X_OTP_REG_16, 0xFF}, + {WSA881X_OTP_REG_17, 0xFF}, + {WSA881X_OTP_REG_18, 0xFF}, + {WSA881X_OTP_REG_19, 0xFF}, + {WSA881X_OTP_REG_20, 0xFF}, + {WSA881X_OTP_REG_21, 0xFF}, + {WSA881X_OTP_REG_22, 0xFF}, + {WSA881X_OTP_REG_23, 0xFF}, + {WSA881X_OTP_REG_24, 0x03}, + {WSA881X_OTP_REG_25, 0x01}, + {WSA881X_OTP_REG_26, 0x03}, + {WSA881X_OTP_REG_27, 0x11}, + {WSA881X_OTP_REG_28, 0xFF}, + {WSA881X_OTP_REG_29, 0xFF}, + {WSA881X_OTP_REG_30, 0xFF}, + {WSA881X_OTP_REG_31, 0xFF}, + {WSA881X_OTP_REG_63, 0x40}, +}; + +struct reg_default wsa881x_ana_reg_defaults_1[] = { + {WSA881X_BIAS_REF_CTRL - WSA881X_ANALOG_BASE, 0x6C}, + {WSA881X_BIAS_TEST - WSA881X_ANALOG_BASE, 0x16}, + {WSA881X_BIAS_BIAS - WSA881X_ANALOG_BASE, 0xF0}, + {WSA881X_TEMP_OP - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_TEMP_IREF_CTRL - WSA881X_ANALOG_BASE, 0x56}, + {WSA881X_TEMP_ISENS_CTRL - WSA881X_ANALOG_BASE, 0x47}, + {WSA881X_TEMP_CLK_CTRL - WSA881X_ANALOG_BASE, 0x87}, + {WSA881X_TEMP_TEST - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_TEMP_BIAS - WSA881X_ANALOG_BASE, 0x51}, + {WSA881X_TEMP_ADC_CTRL - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_TEMP_DOUT_MSB - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_TEMP_DOUT_LSB - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_ADC_EN_MODU_V - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_ADC_EN_MODU_I - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_ADC_EN_DET_TEST_V - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_ADC_EN_DET_TEST_I - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_ADC_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x25}, + {WSA881X_ADC_EN_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x10}, + {WSA881X_SPKR_DRV_EN - WSA881X_ANALOG_BASE, 0x74}, + {WSA881X_SPKR_DRV_GAIN - WSA881X_ANALOG_BASE, 0x01}, + {WSA881X_SPKR_DAC_CTL - WSA881X_ANALOG_BASE, 0x40}, + {WSA881X_SPKR_DRV_DBG - WSA881X_ANALOG_BASE, 0x15}, + {WSA881X_SPKR_PWRSTG_DBG - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_OCP_CTL - WSA881X_ANALOG_BASE, 0xD4}, + {WSA881X_SPKR_CLIP_CTL - WSA881X_ANALOG_BASE, 0x90}, + {WSA881X_SPKR_BBM_CTL - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_MISC_CTL1 - WSA881X_ANALOG_BASE, 0x80}, + {WSA881X_SPKR_MISC_CTL2 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_BIAS_INT - WSA881X_ANALOG_BASE, 0x56}, + {WSA881X_SPKR_PA_INT - WSA881X_ANALOG_BASE, 0x54}, + {WSA881X_SPKR_BIAS_CAL - WSA881X_ANALOG_BASE, 0xAC}, + {WSA881X_SPKR_BIAS_PSRR - WSA881X_ANALOG_BASE, 0x54}, + {WSA881X_SPKR_STATUS1 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_STATUS2 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_BOOST_EN_CTL - WSA881X_ANALOG_BASE, 0x18}, + {WSA881X_BOOST_CURRENT_LIMIT - WSA881X_ANALOG_BASE, 0x7A}, + {WSA881X_BOOST_PS_CTL - WSA881X_ANALOG_BASE, 0xC0}, + {WSA881X_BOOST_PRESET_OUT1 - WSA881X_ANALOG_BASE, 0x77}, + {WSA881X_BOOST_PRESET_OUT2 - WSA881X_ANALOG_BASE, 0x70}, + {WSA881X_BOOST_FORCE_OUT - WSA881X_ANALOG_BASE, 0x0E}, + {WSA881X_BOOST_LDO_PROG - WSA881X_ANALOG_BASE, 0x16}, + {WSA881X_BOOST_SLOPE_COMP_ISENSE_FB - WSA881X_ANALOG_BASE, 0x71}, + {WSA881X_BOOST_RON_CTL - WSA881X_ANALOG_BASE, 0x0F}, + {WSA881X_BOOST_LOOP_STABILITY - WSA881X_ANALOG_BASE, 0xAD}, + {WSA881X_BOOST_ZX_CTL - WSA881X_ANALOG_BASE, 0x34}, + {WSA881X_BOOST_START_CTL - WSA881X_ANALOG_BASE, 0x23}, + {WSA881X_BOOST_MISC1_CTL - WSA881X_ANALOG_BASE, 0x80}, + {WSA881X_BOOST_MISC2_CTL - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_BOOST_MISC3_CTL - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_BOOST_ATEST_CTL - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_PROT_FE_GAIN - WSA881X_ANALOG_BASE, 0x46}, + {WSA881X_SPKR_PROT_FE_CM_LDO_SET - WSA881X_ANALOG_BASE, 0x3B}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 - WSA881X_ANALOG_BASE, 0x8D}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 - WSA881X_ANALOG_BASE, 0x8D}, + {WSA881X_SPKR_PROT_ATEST1 - WSA881X_ANALOG_BASE, 0x01}, + {WSA881X_SPKR_PROT_ATEST2 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_PROT_FE_VSENSE_VCM - WSA881X_ANALOG_BASE, 0x8D}, + {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 - WSA881X_ANALOG_BASE, 0x4D}, + {WSA881X_BONGO_RESRV_REG1 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_BONGO_RESRV_REG2 - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_PROT_SAR - WSA881X_ANALOG_BASE, 0x00}, + {WSA881X_SPKR_STATUS3 - WSA881X_ANALOG_BASE, 0x00}, +}; + +struct reg_default wsa881x_rev_2_0_dig[] = { + {WSA881X_RESET_CTL, 0x00}, + {WSA881X_TADC_VALUE_CTL, 0x01}, + {WSA881X_INTR_MASK, 0x1B}, + {WSA881X_IOPAD_CTL, 0x00}, + {WSA881X_OTP_REG_28, 0x3F}, + {WSA881X_OTP_REG_29, 0x3F}, + {WSA881X_OTP_REG_30, 0x01}, + {WSA881X_OTP_REG_31, 0x01}, +}; + +struct reg_default wsa881x_rev_2_0_ana[] = { + {WSA881X_TEMP_ADC_CTRL, 0x03}, + {WSA881X_ADC_SEL_IBIAS, 0x45}, + {WSA881X_SPKR_DRV_GAIN, 0xC1}, + {WSA881X_SPKR_DAC_CTL, 0x42}, + {WSA881X_SPKR_BBM_CTL, 0x02}, + {WSA881X_SPKR_MISC_CTL1, 0x40}, + {WSA881X_SPKR_MISC_CTL2, 0x07}, + {WSA881X_SPKR_BIAS_INT, 0x5F}, + {WSA881X_SPKR_BIAS_PSRR, 0x44}, + {WSA881X_BOOST_PS_CTL, 0xA0}, + {WSA881X_BOOST_PRESET_OUT1, 0xB7}, + {WSA881X_BOOST_LOOP_STABILITY, 0x8D}, + {WSA881X_SPKR_PROT_ATEST2, 0x02}, + {WSA881X_BONGO_RESRV_REG1, 0x5E}, + {WSA881X_BONGO_RESRV_REG2, 0x07}, +}; + +struct reg_default wsa881x_rev_2_0_regmap_ana[] = { + {WSA881X_TEMP_ADC_CTRL - WSA881X_ANALOG_BASE, 0x03}, + {WSA881X_ADC_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x45}, + {WSA881X_SPKR_DRV_GAIN - WSA881X_ANALOG_BASE, 0xC1}, + {WSA881X_SPKR_DAC_CTL - WSA881X_ANALOG_BASE, 0x42}, + {WSA881X_SPKR_BBM_CTL - WSA881X_ANALOG_BASE, 0x02}, + {WSA881X_SPKR_MISC_CTL1 - WSA881X_ANALOG_BASE, 0x40}, + {WSA881X_SPKR_MISC_CTL2 - WSA881X_ANALOG_BASE, 0x07}, + {WSA881X_SPKR_BIAS_INT - WSA881X_ANALOG_BASE, 0x5F}, + {WSA881X_SPKR_BIAS_PSRR - WSA881X_ANALOG_BASE, 0x44}, + {WSA881X_BOOST_PS_CTL - WSA881X_ANALOG_BASE, 0xA0}, + {WSA881X_BOOST_PRESET_OUT1 - WSA881X_ANALOG_BASE, 0xB7}, + {WSA881X_BOOST_LOOP_STABILITY - WSA881X_ANALOG_BASE, 0x8D}, + {WSA881X_SPKR_PROT_ATEST2 - WSA881X_ANALOG_BASE, 0x02}, + {WSA881X_BONGO_RESRV_REG1 - WSA881X_ANALOG_BASE, 0x5E}, + {WSA881X_BONGO_RESRV_REG2 - WSA881X_ANALOG_BASE, 0x07}, +}; + +/** + * wsa881x_update_reg_defaults_2_0 - update default values of regs for v2.0 + * + * WSA881x v2.0 has different default values for certain analog and digital + * registers compared to v1.x. Therefore, update the values of these registers + * with the values from tables defined above for v2.0. + */ +void wsa881x_update_reg_defaults_2_0(void) +{ + int i, j; + + for (i = 0; i < ARRAY_SIZE(wsa881x_rev_2_0_dig); i++) { + for (j = 0; j < ARRAY_SIZE(wsa881x_ana_reg_defaults); j++) + if (wsa881x_ana_reg_defaults[j].reg == + wsa881x_rev_2_0_dig[i].reg) + wsa881x_ana_reg_defaults[j].def = + wsa881x_rev_2_0_dig[i].def; + } + for (i = 0; i < ARRAY_SIZE(wsa881x_rev_2_0_ana); i++) { + for (j = 0; j < ARRAY_SIZE(wsa881x_ana_reg_defaults); j++) + if (wsa881x_ana_reg_defaults[j].reg == + wsa881x_rev_2_0_ana[i].reg) + wsa881x_ana_reg_defaults[j].def = + wsa881x_rev_2_0_ana[i].def; + } +} +EXPORT_SYMBOL(wsa881x_update_reg_defaults_2_0); + +/** + * wsa881x_update_regmap_2_0 - update regmap framework with new tables + * @regmap: pointer to WSA881x regmap structure + * @flag: indicates digital or analog WSA881x slave + * + * WSA881x v2.0 has some new registers for both analog and digital slaves. + * Update the regmap framework with all the new registers. + */ +void wsa881x_update_regmap_2_0(struct regmap *regmap, int flag) +{ + u16 ret = 0; + + switch (flag) { + case WSA881X_DIGITAL_SLAVE: + ret = regmap_register_patch(regmap, wsa881x_rev_2_0_dig, + ARRAY_SIZE(wsa881x_rev_2_0_dig)); + break; + case WSA881X_ANALOG_SLAVE: + ret = regmap_register_patch(regmap, wsa881x_rev_2_0_ana, + ARRAY_SIZE(wsa881x_rev_2_0_ana)); + break; + default: + pr_debug("%s: unknown version", __func__); + ret = -EINVAL; + break; + } + if (ret) + pr_err("%s: Failed to update regmap defaults ret= %d\n", + __func__, ret); +} +EXPORT_SYMBOL(wsa881x_update_regmap_2_0); + +static bool wsa881x_readable_register(struct device *dev, unsigned int reg) +{ + return wsa881x_ana_reg_readable[reg]; +} + +static bool wsa881x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA881X_CHIP_ID0: + case WSA881X_CHIP_ID1: + case WSA881X_CHIP_ID2: + case WSA881X_CHIP_ID3: + case WSA881X_BUS_ID: + case WSA881X_TEMP_MSB: + case WSA881X_TEMP_LSB: + case WSA881X_SDM_PDM9_LSB: + case WSA881X_SDM_PDM9_MSB: + case WSA881X_OTP_REG_0: + case WSA881X_OTP_REG_1: + case WSA881X_OTP_REG_2: + case WSA881X_OTP_REG_3: + case WSA881X_OTP_REG_4: + case WSA881X_OTP_REG_5: + case WSA881X_OTP_REG_31: + case WSA881X_TEMP_DOUT_MSB: + case WSA881X_TEMP_DOUT_LSB: + case WSA881X_TEMP_OP: + case WSA881X_OTP_CTRL1: + case WSA881X_INTR_STATUS: + case WSA881X_ATE_TEST_MODE: + case WSA881X_PIN_STATUS: + case WSA881X_SWR_HM_TEST2: + case WSA881X_SPKR_STATUS1: + case WSA881X_SPKR_STATUS2: + case WSA881X_SPKR_STATUS3: + case WSA881X_SPKR_PROT_SAR: + return true; + default: + return false; + } +} + +struct regmap_config wsa881x_ana_regmap_config[] = { +{ + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, + .reg_defaults = wsa881x_ana_reg_defaults_0, + .num_reg_defaults = ARRAY_SIZE(wsa881x_ana_reg_defaults_0), + .max_register = WSA881X_MAX_REGISTER, + .volatile_reg = wsa881x_volatile_register, + .readable_reg = wsa881x_readable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}, +{ + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_NONE, + .reg_defaults = wsa881x_ana_reg_defaults_1, + .num_reg_defaults = ARRAY_SIZE(wsa881x_ana_reg_defaults_1), + .max_register = WSA881X_MAX_REGISTER, + .volatile_reg = wsa881x_volatile_register, + .readable_reg = wsa881x_readable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +} +}; diff --git a/sound/soc/codecs/wsa881x-regmap.c b/sound/soc/codecs/wsa881x-regmap.c new file mode 100644 index 000000000000..63bbbfa6beab --- /dev/null +++ b/sound/soc/codecs/wsa881x-regmap.c @@ -0,0 +1,266 @@ +/* + * 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 "wsa881x-registers.h" +#include "wsa881x.h" + +/* + * Default register reset values that are common across different versions + * are defined here. If a register reset value is changed based on version + * then remove it from this structure and add it in version specific + * structures. + */ +static struct reg_default wsa881x_defaults[] = { + {WSA881X_CHIP_ID0, 0x00}, + {WSA881X_CHIP_ID1, 0x00}, + {WSA881X_CHIP_ID2, 0x00}, + {WSA881X_CHIP_ID3, 0x02}, + {WSA881X_BUS_ID, 0x00}, + {WSA881X_CDC_RST_CTL, 0x00}, + {WSA881X_CDC_TOP_CLK_CTL, 0x03}, + {WSA881X_CDC_ANA_CLK_CTL, 0x00}, + {WSA881X_CDC_DIG_CLK_CTL, 0x00}, + {WSA881X_CLOCK_CONFIG, 0x00}, + {WSA881X_ANA_CTL, 0x08}, + {WSA881X_SWR_RESET_EN, 0x00}, + {WSA881X_TEMP_DETECT_CTL, 0x01}, + {WSA881X_TEMP_MSB, 0x00}, + {WSA881X_TEMP_LSB, 0x00}, + {WSA881X_TEMP_CONFIG0, 0x00}, + {WSA881X_TEMP_CONFIG1, 0x00}, + {WSA881X_CDC_CLIP_CTL, 0x03}, + {WSA881X_SDM_PDM9_LSB, 0x00}, + {WSA881X_SDM_PDM9_MSB, 0x00}, + {WSA881X_CDC_RX_CTL, 0x7E}, + {WSA881X_DEM_BYPASS_DATA0, 0x00}, + {WSA881X_DEM_BYPASS_DATA1, 0x00}, + {WSA881X_DEM_BYPASS_DATA2, 0x00}, + {WSA881X_DEM_BYPASS_DATA3, 0x00}, + {WSA881X_OTP_CTRL0, 0x00}, + {WSA881X_OTP_CTRL1, 0x00}, + {WSA881X_HDRIVE_CTL_GROUP1, 0x00}, + {WSA881X_INTR_MODE, 0x00}, + {WSA881X_INTR_STATUS, 0x00}, + {WSA881X_INTR_CLEAR, 0x00}, + {WSA881X_INTR_LEVEL, 0x00}, + {WSA881X_INTR_SET, 0x00}, + {WSA881X_INTR_TEST, 0x00}, + {WSA881X_PDM_TEST_MODE, 0x00}, + {WSA881X_ATE_TEST_MODE, 0x00}, + {WSA881X_PIN_CTL_MODE, 0x00}, + {WSA881X_PIN_CTL_OE, 0x00}, + {WSA881X_PIN_WDATA_IOPAD, 0x00}, + {WSA881X_PIN_STATUS, 0x00}, + {WSA881X_DIG_DEBUG_MODE, 0x00}, + {WSA881X_DIG_DEBUG_SEL, 0x00}, + {WSA881X_DIG_DEBUG_EN, 0x00}, + {WSA881X_SWR_HM_TEST1, 0x08}, + {WSA881X_SWR_HM_TEST2, 0x00}, + {WSA881X_TEMP_DETECT_DBG_CTL, 0x00}, + {WSA881X_TEMP_DEBUG_MSB, 0x00}, + {WSA881X_TEMP_DEBUG_LSB, 0x00}, + {WSA881X_SAMPLE_EDGE_SEL, 0x0C}, + {WSA881X_SPARE_0, 0x00}, + {WSA881X_SPARE_1, 0x00}, + {WSA881X_SPARE_2, 0x00}, + {WSA881X_OTP_REG_0, 0x01}, + {WSA881X_OTP_REG_1, 0xFF}, + {WSA881X_OTP_REG_2, 0xC0}, + {WSA881X_OTP_REG_3, 0xFF}, + {WSA881X_OTP_REG_4, 0xC0}, + {WSA881X_OTP_REG_5, 0xFF}, + {WSA881X_OTP_REG_6, 0xFF}, + {WSA881X_OTP_REG_7, 0xFF}, + {WSA881X_OTP_REG_8, 0xFF}, + {WSA881X_OTP_REG_9, 0xFF}, + {WSA881X_OTP_REG_10, 0xFF}, + {WSA881X_OTP_REG_11, 0xFF}, + {WSA881X_OTP_REG_12, 0xFF}, + {WSA881X_OTP_REG_13, 0xFF}, + {WSA881X_OTP_REG_14, 0xFF}, + {WSA881X_OTP_REG_15, 0xFF}, + {WSA881X_OTP_REG_16, 0xFF}, + {WSA881X_OTP_REG_17, 0xFF}, + {WSA881X_OTP_REG_18, 0xFF}, + {WSA881X_OTP_REG_19, 0xFF}, + {WSA881X_OTP_REG_20, 0xFF}, + {WSA881X_OTP_REG_21, 0xFF}, + {WSA881X_OTP_REG_22, 0xFF}, + {WSA881X_OTP_REG_23, 0xFF}, + {WSA881X_OTP_REG_24, 0x03}, + {WSA881X_OTP_REG_25, 0x01}, + {WSA881X_OTP_REG_26, 0x03}, + {WSA881X_OTP_REG_27, 0x11}, + {WSA881X_OTP_REG_63, 0x40}, + /* WSA881x Analog registers */ + {WSA881X_BIAS_REF_CTRL, 0x6C}, + {WSA881X_BIAS_TEST, 0x16}, + {WSA881X_BIAS_BIAS, 0xF0}, + {WSA881X_TEMP_OP, 0x00}, + {WSA881X_TEMP_IREF_CTRL, 0x56}, + {WSA881X_TEMP_ISENS_CTRL, 0x47}, + {WSA881X_TEMP_CLK_CTRL, 0x87}, + {WSA881X_TEMP_TEST, 0x00}, + {WSA881X_TEMP_BIAS, 0x51}, + {WSA881X_TEMP_DOUT_MSB, 0x00}, + {WSA881X_TEMP_DOUT_LSB, 0x00}, + {WSA881X_ADC_EN_MODU_V, 0x00}, + {WSA881X_ADC_EN_MODU_I, 0x00}, + {WSA881X_ADC_EN_DET_TEST_V, 0x00}, + {WSA881X_ADC_EN_DET_TEST_I, 0x00}, + {WSA881X_ADC_EN_SEL_IBAIS, 0x10}, + {WSA881X_SPKR_DRV_EN, 0x74}, + {WSA881X_SPKR_DRV_DBG, 0x15}, + {WSA881X_SPKR_PWRSTG_DBG, 0x00}, + {WSA881X_SPKR_OCP_CTL, 0xD4}, + {WSA881X_SPKR_CLIP_CTL, 0x90}, + {WSA881X_SPKR_PA_INT, 0x54}, + {WSA881X_SPKR_BIAS_CAL, 0xAC}, + {WSA881X_SPKR_STATUS1, 0x00}, + {WSA881X_SPKR_STATUS2, 0x00}, + {WSA881X_BOOST_EN_CTL, 0x18}, + {WSA881X_BOOST_CURRENT_LIMIT, 0x7A}, + {WSA881X_BOOST_PRESET_OUT2, 0x70}, + {WSA881X_BOOST_FORCE_OUT, 0x0E}, + {WSA881X_BOOST_LDO_PROG, 0x16}, + {WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71}, + {WSA881X_BOOST_RON_CTL, 0x0F}, + {WSA881X_BOOST_ZX_CTL, 0x34}, + {WSA881X_BOOST_START_CTL, 0x23}, + {WSA881X_BOOST_MISC1_CTL, 0x80}, + {WSA881X_BOOST_MISC2_CTL, 0x00}, + {WSA881X_BOOST_MISC3_CTL, 0x00}, + {WSA881X_BOOST_ATEST_CTL, 0x00}, + {WSA881X_SPKR_PROT_FE_GAIN, 0x46}, + {WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D}, + {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D}, + {WSA881X_SPKR_PROT_ATEST1, 0x01}, + {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D}, + {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D}, + {WSA881X_SPKR_PROT_SAR, 0x00}, + {WSA881X_SPKR_STATUS3, 0x00}, +}; + +/* Default register reset values for WSA881x rev 2.0 */ +static struct reg_sequence wsa881x_rev_2_0[] = { + {WSA881X_RESET_CTL, 0x00, 0x00}, + {WSA881X_TADC_VALUE_CTL, 0x01, 0x00}, + {WSA881X_INTR_MASK, 0x1B, 0x00}, + {WSA881X_IOPAD_CTL, 0x00, 0x00}, + {WSA881X_OTP_REG_28, 0x3F, 0x00}, + {WSA881X_OTP_REG_29, 0x3F, 0x00}, + {WSA881X_OTP_REG_30, 0x01, 0x00}, + {WSA881X_OTP_REG_31, 0x01, 0x00}, + {WSA881X_TEMP_ADC_CTRL, 0x03, 0x00}, + {WSA881X_ADC_SEL_IBIAS, 0x45, 0x00}, + {WSA881X_SPKR_DRV_GAIN, 0xC1, 0x00}, + {WSA881X_SPKR_DAC_CTL, 0x42, 0x00}, + {WSA881X_SPKR_BBM_CTL, 0x02, 0x00}, + {WSA881X_SPKR_MISC_CTL1, 0x40, 0x00}, + {WSA881X_SPKR_MISC_CTL2, 0x07, 0x00}, + {WSA881X_SPKR_BIAS_INT, 0x5F, 0x00}, + {WSA881X_SPKR_BIAS_PSRR, 0x44, 0x00}, + {WSA881X_BOOST_PS_CTL, 0xA0, 0x00}, + {WSA881X_BOOST_PRESET_OUT1, 0xB7, 0x00}, + {WSA881X_BOOST_LOOP_STABILITY, 0x8D, 0x00}, + {WSA881X_SPKR_PROT_ATEST2, 0x02, 0x00}, + {WSA881X_BONGO_RESRV_REG1, 0x5E, 0x00}, + {WSA881X_BONGO_RESRV_REG2, 0x07, 0x00}, +}; + +/* + * wsa881x_regmap_defaults - update regmap default register values + * @regmap: pointer to regmap structure + * @version: wsa881x version id + * + * Update regmap default register values based on version id + * + */ +void wsa881x_regmap_defaults(struct regmap *regmap, u8 version) +{ + u16 ret = 0; + + if (!regmap) { + pr_debug("%s: regmap structure is NULL\n", __func__); + return; + } + + regcache_cache_only(regmap, true); + ret = regmap_multi_reg_write(regmap, wsa881x_rev_2_0, + ARRAY_SIZE(wsa881x_rev_2_0)); + regcache_cache_only(regmap, false); + + if (ret) + pr_debug("%s: Failed to update regmap defaults ret= %d\n", + __func__, ret); +} +EXPORT_SYMBOL(wsa881x_regmap_defaults); + +static bool wsa881x_readable_register(struct device *dev, unsigned int reg) +{ + return wsa881x_reg_readable[reg]; +} + +static bool wsa881x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA881X_CHIP_ID0: + case WSA881X_CHIP_ID1: + case WSA881X_CHIP_ID2: + case WSA881X_CHIP_ID3: + case WSA881X_BUS_ID: + case WSA881X_TEMP_MSB: + case WSA881X_TEMP_LSB: + case WSA881X_SDM_PDM9_LSB: + case WSA881X_SDM_PDM9_MSB: + case WSA881X_OTP_CTRL1: + case WSA881X_INTR_STATUS: + case WSA881X_ATE_TEST_MODE: + case WSA881X_PIN_STATUS: + case WSA881X_SWR_HM_TEST2: + case WSA881X_SPKR_STATUS1: + case WSA881X_SPKR_STATUS2: + case WSA881X_SPKR_STATUS3: + case WSA881X_OTP_REG_0: + case WSA881X_OTP_REG_1: + case WSA881X_OTP_REG_2: + case WSA881X_OTP_REG_3: + case WSA881X_OTP_REG_4: + case WSA881X_OTP_REG_5: + case WSA881X_OTP_REG_31: + case WSA881X_TEMP_DOUT_MSB: + case WSA881X_TEMP_DOUT_LSB: + case WSA881X_TEMP_OP: + case WSA881X_SPKR_PROT_SAR: + return true; + default: + return false; + } +} + +struct regmap_config wsa881x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wsa881x_defaults, + .num_reg_defaults = ARRAY_SIZE(wsa881x_defaults), + .max_register = WSA881X_MAX_REGISTER, + .volatile_reg = wsa881x_volatile_register, + .readable_reg = wsa881x_readable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .can_multi_write = true, +}; diff --git a/sound/soc/codecs/wsa881x-tables-analog.c b/sound/soc/codecs/wsa881x-tables-analog.c new file mode 100644 index 000000000000..061ed6fff798 --- /dev/null +++ b/sound/soc/codecs/wsa881x-tables-analog.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 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. + */ + +#include +#include +#include "wsa881x-registers-analog.h" + +const u8 wsa881x_ana_reg_readable[WSA881X_CACHE_SIZE] = { + [WSA881X_CHIP_ID0] = 1, + [WSA881X_CHIP_ID1] = 1, + [WSA881X_CHIP_ID2] = 1, + [WSA881X_CHIP_ID3] = 1, + [WSA881X_BUS_ID] = 1, + [WSA881X_CDC_RST_CTL] = 1, + [WSA881X_CDC_TOP_CLK_CTL] = 1, + [WSA881X_CDC_ANA_CLK_CTL] = 1, + [WSA881X_CDC_DIG_CLK_CTL] = 1, + [WSA881X_CLOCK_CONFIG] = 1, + [WSA881X_ANA_CTL] = 1, + [WSA881X_SWR_RESET_EN] = 1, + [WSA881X_RESET_CTL] = 1, + [WSA881X_TADC_VALUE_CTL] = 1, + [WSA881X_TEMP_DETECT_CTL] = 1, + [WSA881X_TEMP_MSB] = 1, + [WSA881X_TEMP_LSB] = 1, + [WSA881X_TEMP_CONFIG0] = 1, + [WSA881X_TEMP_CONFIG1] = 1, + [WSA881X_CDC_CLIP_CTL] = 1, + [WSA881X_SDM_PDM9_LSB] = 1, + [WSA881X_SDM_PDM9_MSB] = 1, + [WSA881X_CDC_RX_CTL] = 1, + [WSA881X_DEM_BYPASS_DATA0] = 1, + [WSA881X_DEM_BYPASS_DATA1] = 1, + [WSA881X_DEM_BYPASS_DATA2] = 1, + [WSA881X_DEM_BYPASS_DATA3] = 1, + [WSA881X_OTP_CTRL0] = 1, + [WSA881X_OTP_CTRL1] = 1, + [WSA881X_HDRIVE_CTL_GROUP1] = 1, + [WSA881X_INTR_MODE] = 1, + [WSA881X_INTR_MASK] = 1, + [WSA881X_INTR_STATUS] = 1, + [WSA881X_INTR_CLEAR] = 1, + [WSA881X_INTR_LEVEL] = 1, + [WSA881X_INTR_SET] = 1, + [WSA881X_INTR_TEST] = 1, + [WSA881X_PDM_TEST_MODE] = 1, + [WSA881X_ATE_TEST_MODE] = 1, + [WSA881X_PIN_CTL_MODE] = 1, + [WSA881X_PIN_CTL_OE] = 1, + [WSA881X_PIN_WDATA_IOPAD] = 1, + [WSA881X_PIN_STATUS] = 1, + [WSA881X_DIG_DEBUG_MODE] = 1, + [WSA881X_DIG_DEBUG_SEL] = 1, + [WSA881X_DIG_DEBUG_EN] = 1, + [WSA881X_SWR_HM_TEST1] = 1, + [WSA881X_SWR_HM_TEST2] = 1, + [WSA881X_TEMP_DETECT_DBG_CTL] = 1, + [WSA881X_TEMP_DEBUG_MSB] = 1, + [WSA881X_TEMP_DEBUG_LSB] = 1, + [WSA881X_SAMPLE_EDGE_SEL] = 1, + [WSA881X_IOPAD_CTL] = 1, + [WSA881X_SPARE_0] = 1, + [WSA881X_SPARE_1] = 1, + [WSA881X_SPARE_2] = 1, + [WSA881X_OTP_REG_0] = 1, + [WSA881X_OTP_REG_1] = 1, + [WSA881X_OTP_REG_2] = 1, + [WSA881X_OTP_REG_3] = 1, + [WSA881X_OTP_REG_4] = 1, + [WSA881X_OTP_REG_5] = 1, + [WSA881X_OTP_REG_6] = 1, + [WSA881X_OTP_REG_7] = 1, + [WSA881X_OTP_REG_8] = 1, + [WSA881X_OTP_REG_9] = 1, + [WSA881X_OTP_REG_10] = 1, + [WSA881X_OTP_REG_11] = 1, + [WSA881X_OTP_REG_12] = 1, + [WSA881X_OTP_REG_13] = 1, + [WSA881X_OTP_REG_14] = 1, + [WSA881X_OTP_REG_15] = 1, + [WSA881X_OTP_REG_16] = 1, + [WSA881X_OTP_REG_17] = 1, + [WSA881X_OTP_REG_18] = 1, + [WSA881X_OTP_REG_19] = 1, + [WSA881X_OTP_REG_20] = 1, + [WSA881X_OTP_REG_21] = 1, + [WSA881X_OTP_REG_22] = 1, + [WSA881X_OTP_REG_23] = 1, + [WSA881X_OTP_REG_24] = 1, + [WSA881X_OTP_REG_25] = 1, + [WSA881X_OTP_REG_26] = 1, + [WSA881X_OTP_REG_27] = 1, + [WSA881X_OTP_REG_28] = 1, + [WSA881X_OTP_REG_29] = 1, + [WSA881X_OTP_REG_30] = 1, + [WSA881X_OTP_REG_31] = 1, + [WSA881X_OTP_REG_63] = 1, + /* Analog Registers */ + [WSA881X_BIAS_REF_CTRL] = 1, + [WSA881X_BIAS_TEST] = 1, + [WSA881X_BIAS_BIAS] = 1, + [WSA881X_TEMP_OP] = 1, + [WSA881X_TEMP_IREF_CTRL] = 1, + [WSA881X_TEMP_ISENS_CTRL] = 1, + [WSA881X_TEMP_CLK_CTRL] = 1, + [WSA881X_TEMP_TEST] = 1, + [WSA881X_TEMP_BIAS] = 1, + [WSA881X_TEMP_ADC_CTRL] = 1, + [WSA881X_TEMP_DOUT_MSB] = 1, + [WSA881X_TEMP_DOUT_LSB] = 1, + [WSA881X_ADC_EN_MODU_V] = 1, + [WSA881X_ADC_EN_MODU_I] = 1, + [WSA881X_ADC_EN_DET_TEST_V] = 1, + [WSA881X_ADC_EN_DET_TEST_I] = 1, + [WSA881X_ADC_SEL_IBIAS] = 1, + [WSA881X_ADC_EN_SEL_IBIAS] = 1, + [WSA881X_SPKR_DRV_EN] = 1, + [WSA881X_SPKR_DRV_GAIN] = 1, + [WSA881X_SPKR_DAC_CTL] = 1, + [WSA881X_SPKR_DRV_DBG] = 1, + [WSA881X_SPKR_PWRSTG_DBG] = 1, + [WSA881X_SPKR_OCP_CTL] = 1, + [WSA881X_SPKR_CLIP_CTL] = 1, + [WSA881X_SPKR_BBM_CTL] = 1, + [WSA881X_SPKR_MISC_CTL1] = 1, + [WSA881X_SPKR_MISC_CTL2] = 1, + [WSA881X_SPKR_BIAS_INT] = 1, + [WSA881X_SPKR_PA_INT] = 1, + [WSA881X_SPKR_BIAS_CAL] = 1, + [WSA881X_SPKR_BIAS_PSRR] = 1, + [WSA881X_SPKR_STATUS1] = 1, + [WSA881X_SPKR_STATUS2] = 1, + [WSA881X_BOOST_EN_CTL] = 1, + [WSA881X_BOOST_CURRENT_LIMIT] = 1, + [WSA881X_BOOST_PS_CTL] = 1, + [WSA881X_BOOST_PRESET_OUT1] = 1, + [WSA881X_BOOST_PRESET_OUT2] = 1, + [WSA881X_BOOST_FORCE_OUT] = 1, + [WSA881X_BOOST_LDO_PROG] = 1, + [WSA881X_BOOST_SLOPE_COMP_ISENSE_FB] = 1, + [WSA881X_BOOST_RON_CTL] = 1, + [WSA881X_BOOST_LOOP_STABILITY] = 1, + [WSA881X_BOOST_ZX_CTL] = 1, + [WSA881X_BOOST_START_CTL] = 1, + [WSA881X_BOOST_MISC1_CTL] = 1, + [WSA881X_BOOST_MISC2_CTL] = 1, + [WSA881X_BOOST_MISC3_CTL] = 1, + [WSA881X_BOOST_ATEST_CTL] = 1, + [WSA881X_SPKR_PROT_FE_GAIN] = 1, + [WSA881X_SPKR_PROT_FE_CM_LDO_SET] = 1, + [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1] = 1, + [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2] = 1, + [WSA881X_SPKR_PROT_ATEST1] = 1, + [WSA881X_SPKR_PROT_ATEST2] = 1, + [WSA881X_SPKR_PROT_FE_VSENSE_VCM] = 1, + [WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1] = 1, + [WSA881X_BONGO_RESRV_REG1] = 1, + [WSA881X_BONGO_RESRV_REG2] = 1, + [WSA881X_SPKR_PROT_SAR] = 1, + [WSA881X_SPKR_STATUS3] = 1, +}; diff --git a/sound/soc/codecs/wsa881x-tables.c b/sound/soc/codecs/wsa881x-tables.c new file mode 100644 index 000000000000..4f1212be9c5b --- /dev/null +++ b/sound/soc/codecs/wsa881x-tables.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 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. + */ + +#include +#include +#include "wsa881x-registers.h" + +const u8 wsa881x_reg_readable[WSA881X_CACHE_SIZE] = { + [WSA881X_CHIP_ID0] = 1, + [WSA881X_CHIP_ID1] = 1, + [WSA881X_CHIP_ID2] = 1, + [WSA881X_CHIP_ID3] = 1, + [WSA881X_BUS_ID] = 1, + [WSA881X_CDC_RST_CTL] = 1, + [WSA881X_CDC_TOP_CLK_CTL] = 1, + [WSA881X_CDC_ANA_CLK_CTL] = 1, + [WSA881X_CDC_DIG_CLK_CTL] = 1, + [WSA881X_CLOCK_CONFIG] = 1, + [WSA881X_ANA_CTL] = 1, + [WSA881X_SWR_RESET_EN] = 1, + [WSA881X_RESET_CTL] = 1, + [WSA881X_TADC_VALUE_CTL] = 1, + [WSA881X_TEMP_DETECT_CTL] = 1, + [WSA881X_TEMP_MSB] = 1, + [WSA881X_TEMP_LSB] = 1, + [WSA881X_TEMP_CONFIG0] = 1, + [WSA881X_TEMP_CONFIG1] = 1, + [WSA881X_CDC_CLIP_CTL] = 1, + [WSA881X_SDM_PDM9_LSB] = 1, + [WSA881X_SDM_PDM9_MSB] = 1, + [WSA881X_CDC_RX_CTL] = 1, + [WSA881X_DEM_BYPASS_DATA0] = 1, + [WSA881X_DEM_BYPASS_DATA1] = 1, + [WSA881X_DEM_BYPASS_DATA2] = 1, + [WSA881X_DEM_BYPASS_DATA3] = 1, + [WSA881X_OTP_CTRL0] = 1, + [WSA881X_OTP_CTRL1] = 1, + [WSA881X_HDRIVE_CTL_GROUP1] = 1, + [WSA881X_INTR_MODE] = 1, + [WSA881X_INTR_MASK] = 1, + [WSA881X_INTR_STATUS] = 1, + [WSA881X_INTR_CLEAR] = 1, + [WSA881X_INTR_LEVEL] = 1, + [WSA881X_INTR_SET] = 1, + [WSA881X_INTR_TEST] = 1, + [WSA881X_PDM_TEST_MODE] = 1, + [WSA881X_ATE_TEST_MODE] = 1, + [WSA881X_PIN_CTL_MODE] = 1, + [WSA881X_PIN_CTL_OE] = 1, + [WSA881X_PIN_WDATA_IOPAD] = 1, + [WSA881X_PIN_STATUS] = 1, + [WSA881X_DIG_DEBUG_MODE] = 1, + [WSA881X_DIG_DEBUG_SEL] = 1, + [WSA881X_DIG_DEBUG_EN] = 1, + [WSA881X_SWR_HM_TEST1] = 1, + [WSA881X_SWR_HM_TEST2] = 1, + [WSA881X_TEMP_DETECT_DBG_CTL] = 1, + [WSA881X_TEMP_DEBUG_MSB] = 1, + [WSA881X_TEMP_DEBUG_LSB] = 1, + [WSA881X_SAMPLE_EDGE_SEL] = 1, + [WSA881X_IOPAD_CTL] = 1, + [WSA881X_SPARE_0] = 1, + [WSA881X_SPARE_1] = 1, + [WSA881X_SPARE_2] = 1, + [WSA881X_OTP_REG_0] = 1, + [WSA881X_OTP_REG_1] = 1, + [WSA881X_OTP_REG_2] = 1, + [WSA881X_OTP_REG_3] = 1, + [WSA881X_OTP_REG_4] = 1, + [WSA881X_OTP_REG_5] = 1, + [WSA881X_OTP_REG_6] = 1, + [WSA881X_OTP_REG_7] = 1, + [WSA881X_OTP_REG_8] = 1, + [WSA881X_OTP_REG_9] = 1, + [WSA881X_OTP_REG_10] = 1, + [WSA881X_OTP_REG_11] = 1, + [WSA881X_OTP_REG_12] = 1, + [WSA881X_OTP_REG_13] = 1, + [WSA881X_OTP_REG_14] = 1, + [WSA881X_OTP_REG_15] = 1, + [WSA881X_OTP_REG_16] = 1, + [WSA881X_OTP_REG_17] = 1, + [WSA881X_OTP_REG_18] = 1, + [WSA881X_OTP_REG_19] = 1, + [WSA881X_OTP_REG_20] = 1, + [WSA881X_OTP_REG_21] = 1, + [WSA881X_OTP_REG_22] = 1, + [WSA881X_OTP_REG_23] = 1, + [WSA881X_OTP_REG_24] = 1, + [WSA881X_OTP_REG_25] = 1, + [WSA881X_OTP_REG_26] = 1, + [WSA881X_OTP_REG_27] = 1, + [WSA881X_OTP_REG_28] = 1, + [WSA881X_OTP_REG_29] = 1, + [WSA881X_OTP_REG_30] = 1, + [WSA881X_OTP_REG_31] = 1, + [WSA881X_OTP_REG_63] = 1, + /* Analog Registers */ + [WSA881X_BIAS_REF_CTRL] = 1, + [WSA881X_BIAS_TEST] = 1, + [WSA881X_BIAS_BIAS] = 1, + [WSA881X_TEMP_OP] = 1, + [WSA881X_TEMP_IREF_CTRL] = 1, + [WSA881X_TEMP_ISENS_CTRL] = 1, + [WSA881X_TEMP_CLK_CTRL] = 1, + [WSA881X_TEMP_TEST] = 1, + [WSA881X_TEMP_BIAS] = 1, + [WSA881X_TEMP_ADC_CTRL] = 1, + [WSA881X_TEMP_DOUT_MSB] = 1, + [WSA881X_TEMP_DOUT_LSB] = 1, + [WSA881X_ADC_EN_MODU_V] = 1, + [WSA881X_ADC_EN_MODU_I] = 1, + [WSA881X_ADC_EN_DET_TEST_V] = 1, + [WSA881X_ADC_EN_DET_TEST_I] = 1, + [WSA881X_ADC_SEL_IBIAS] = 1, + [WSA881X_ADC_EN_SEL_IBAIS] = 1, + [WSA881X_SPKR_DRV_EN] = 1, + [WSA881X_SPKR_DRV_GAIN] = 1, + [WSA881X_SPKR_DAC_CTL] = 1, + [WSA881X_SPKR_DRV_DBG] = 1, + [WSA881X_SPKR_PWRSTG_DBG] = 1, + [WSA881X_SPKR_OCP_CTL] = 1, + [WSA881X_SPKR_CLIP_CTL] = 1, + [WSA881X_SPKR_BBM_CTL] = 1, + [WSA881X_SPKR_MISC_CTL1] = 1, + [WSA881X_SPKR_MISC_CTL2] = 1, + [WSA881X_SPKR_BIAS_INT] = 1, + [WSA881X_SPKR_PA_INT] = 1, + [WSA881X_SPKR_BIAS_CAL] = 1, + [WSA881X_SPKR_BIAS_PSRR] = 1, + [WSA881X_SPKR_STATUS1] = 1, + [WSA881X_SPKR_STATUS2] = 1, + [WSA881X_BOOST_EN_CTL] = 1, + [WSA881X_BOOST_CURRENT_LIMIT] = 1, + [WSA881X_BOOST_PS_CTL] = 1, + [WSA881X_BOOST_PRESET_OUT1] = 1, + [WSA881X_BOOST_PRESET_OUT2] = 1, + [WSA881X_BOOST_FORCE_OUT] = 1, + [WSA881X_BOOST_LDO_PROG] = 1, + [WSA881X_BOOST_SLOPE_COMP_ISENSE_FB] = 1, + [WSA881X_BOOST_RON_CTL] = 1, + [WSA881X_BOOST_LOOP_STABILITY] = 1, + [WSA881X_BOOST_ZX_CTL] = 1, + [WSA881X_BOOST_START_CTL] = 1, + [WSA881X_BOOST_MISC1_CTL] = 1, + [WSA881X_BOOST_MISC2_CTL] = 1, + [WSA881X_BOOST_MISC3_CTL] = 1, + [WSA881X_BOOST_ATEST_CTL] = 1, + [WSA881X_SPKR_PROT_FE_GAIN] = 1, + [WSA881X_SPKR_PROT_FE_CM_LDO_SET] = 1, + [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1] = 1, + [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2] = 1, + [WSA881X_SPKR_PROT_ATEST1] = 1, + [WSA881X_SPKR_PROT_ATEST2] = 1, + [WSA881X_SPKR_PROT_FE_VSENSE_VCM] = 1, + [WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1] = 1, + [WSA881X_BONGO_RESRV_REG1] = 1, + [WSA881X_BONGO_RESRV_REG2] = 1, + [WSA881X_SPKR_PROT_SAR] = 1, + [WSA881X_SPKR_STATUS3] = 1, +}; diff --git a/sound/soc/codecs/wsa881x-temp-sensor.c b/sound/soc/codecs/wsa881x-temp-sensor.c new file mode 100644 index 000000000000..5ab0ecfdc022 --- /dev/null +++ b/sound/soc/codecs/wsa881x-temp-sensor.c @@ -0,0 +1,149 @@ +/* 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 "wsa881x-temp-sensor.h" + +#define T1_TEMP -10 +#define T2_TEMP 150 +#define LOW_TEMP_THRESHOLD 5 +#define HIGH_TEMP_THRESHOLD 45 +#define TEMP_INVALID 0xFFFF +#define WSA881X_TEMP_RETRY 3 +/* + * wsa881x_get_temp - get wsa temperature + * @thermal: thermal zone device + * @temp: temperature value + * + * Get the temperature of wsa881x. + * + * Return: 0 on success or negative error code on failure. + */ +int wsa881x_get_temp(struct thermal_zone_device *thermal, + int *temp) +{ + struct wsa881x_tz_priv *pdata; + struct snd_soc_codec *codec; + struct wsa_temp_register reg; + int dmeas, d1, d2; + int ret = 0; + int temp_val; + int t1 = T1_TEMP; + int t2 = T2_TEMP; + u8 retry = WSA881X_TEMP_RETRY; + + if (!thermal) + return -EINVAL; + + if (thermal->devdata) { + pdata = thermal->devdata; + if (pdata->codec) { + codec = pdata->codec; + } else { + pr_err("%s: codec is NULL\n", __func__); + return -EINVAL; + } + } else { + pr_err("%s: pdata is NULL\n", __func__); + return -EINVAL; + } +temp_retry: + if (pdata->wsa_temp_reg_read) { + ret = pdata->wsa_temp_reg_read(codec, ®); + if (ret) { + pr_err("%s: temperature register read failed: %d\n", + __func__, ret); + return ret; + } + } else { + pr_err("%s: wsa_temp_reg_read is NULL\n", __func__); + return -EINVAL; + } + /* + * Temperature register values are expected to be in the + * following range. + * d1_msb = 68 - 92 and d1_lsb = 0, 64, 128, 192 + * d2_msb = 185 -218 and d2_lsb = 0, 64, 128, 192 + */ + if ((reg.d1_msb < 68 || reg.d1_msb > 92) || + (!(reg.d1_lsb == 0 || reg.d1_lsb == 64 || reg.d1_lsb == 128 || + reg.d1_lsb == 192)) || + (reg.d2_msb < 185 || reg.d2_msb > 218) || + (!(reg.d2_lsb == 0 || reg.d2_lsb == 64 || reg.d2_lsb == 128 || + reg.d2_lsb == 192))) { + printk_ratelimited("%s: Temperature registers[%d %d %d %d] are out of range\n", + __func__, reg.d1_msb, reg.d1_lsb, reg.d2_msb, + reg.d2_lsb); + } + dmeas = ((reg.dmeas_msb << 0x8) | reg.dmeas_lsb) >> 0x6; + d1 = ((reg.d1_msb << 0x8) | reg.d1_lsb) >> 0x6; + d2 = ((reg.d2_msb << 0x8) | reg.d2_lsb) >> 0x6; + + if (d1 == d2) + temp_val = TEMP_INVALID; + else + temp_val = t1 + (((dmeas - d1) * (t2 - t1))/(d2 - d1)); + + if (temp_val <= LOW_TEMP_THRESHOLD || + temp_val >= HIGH_TEMP_THRESHOLD) { + printk_ratelimited("%s: T0: %d is out of range[%d, %d]\n", + __func__, temp_val, LOW_TEMP_THRESHOLD, + HIGH_TEMP_THRESHOLD); + if (retry--) { + msleep(20); + goto temp_retry; + } + } + if (temp) + *temp = temp_val; + pr_debug("%s: t0 measured: %d dmeas = %d, d1 = %d, d2 = %d\n", + __func__, temp_val, dmeas, d1, d2); + return ret; +} +EXPORT_SYMBOL(wsa881x_get_temp); + +static struct thermal_zone_device_ops wsa881x_thermal_ops = { + .get_temp = wsa881x_get_temp, +}; + +int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata) +{ + struct thermal_zone_device *tz_dev; + + if (tz_pdata == NULL) { + pr_err("%s: thermal pdata is NULL\n", __func__); + return -EINVAL; + } + /* Register with the thermal zone */ + tz_dev = thermal_zone_device_register(tz_pdata->name, + 0, 0, tz_pdata, + &wsa881x_thermal_ops, NULL, 0, 0); + if (IS_ERR(tz_dev)) { + pr_err("%s: thermal device register failed.\n", __func__); + return -EINVAL; + } + tz_pdata->tz_dev = tz_dev; + return 0; +} +EXPORT_SYMBOL(wsa881x_init_thermal); + +void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev) +{ + if (tz_dev) + thermal_zone_device_unregister(tz_dev); +} +EXPORT_SYMBOL(wsa881x_deinit_thermal); diff --git a/sound/soc/codecs/wsa881x-temp-sensor.h b/sound/soc/codecs/wsa881x-temp-sensor.h new file mode 100644 index 000000000000..d6c1eb75e940 --- /dev/null +++ b/sound/soc/codecs/wsa881x-temp-sensor.h @@ -0,0 +1,39 @@ +/* Copyright (c) 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 WSA881X_TEMP_SENSOR_H +#define WSA881X_TEMP_SENSOR_H + +#include +#include + +struct wsa_temp_register { + u8 d1_msb; + u8 d1_lsb; + u8 d2_msb; + u8 d2_lsb; + u8 dmeas_msb; + u8 dmeas_lsb; +}; +typedef int32_t (*wsa_temp_register_read)(struct snd_soc_codec *codec, + struct wsa_temp_register *wsa_temp_reg); +struct wsa881x_tz_priv { + struct thermal_zone_device *tz_dev; + struct snd_soc_codec *codec; + struct wsa_temp_register *wsa_temp_reg; + char name[80]; + wsa_temp_register_read wsa_temp_reg_read; +}; + +int wsa881x_get_temp(struct thermal_zone_device *tz_dev, int *temp); +int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata); +void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev); +#endif diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c new file mode 100644 index 000000000000..77aea1049ca5 --- /dev/null +++ b/sound/soc/codecs/wsa881x.c @@ -0,0 +1,1439 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wsa881x.h" +#include "wsa881x-temp-sensor.h" + +#define WSA881X_NUM_RETRY 5 + +enum { + G_18DB = 0, + G_16P5DB, + G_15DB, + G_13P5DB, + G_12DB, + G_10P5DB, + G_9DB, + G_7P5DB, + G_6DB, + G_4P5DB, + G_3DB, + G_1P5DB, + G_0DB, +}; + +enum { + DISABLE = 0, + ENABLE, +}; + +enum { + SWR_DAC_PORT, + SWR_COMP_PORT, + SWR_BOOST_PORT, + SWR_VISENSE_PORT, +}; + +struct swr_port { + u8 port_id; + u8 ch_mask; + u32 ch_rate; + u8 num_ch; +}; + +enum { + WSA881X_DEV_DOWN, + WSA881X_DEV_UP, +}; + +/* + * Private data Structure for wsa881x. All parameters related to + * WSA881X codec needs to be defined here. + */ +struct wsa881x_priv { + struct regmap *regmap; + struct device *dev; + struct swr_device *swr_slave; + struct snd_soc_codec *codec; + bool comp_enable; + bool boost_enable; + bool visense_enable; + u8 pa_gain; + struct swr_port port[WSA881X_MAX_SWR_PORTS]; + int pd_gpio; + struct wsa881x_tz_priv tz_pdata; + int bg_cnt; + int clk_cnt; + int version; + struct mutex bg_lock; + struct mutex res_lock; + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; + int state; + struct delayed_work ocp_ctl_work; + struct device_node *wsa_rst_np; + int pa_mute; +}; + +#define SWR_SLV_MAX_REG_ADDR 0x390 +#define SWR_SLV_START_REG_ADDR 0x40 +#define SWR_SLV_MAX_BUF_LEN 20 +#define BYTES_PER_LINE 12 +#define SWR_SLV_RD_BUF_LEN 8 +#define SWR_SLV_WR_BUF_LEN 32 +#define SWR_SLV_MAX_DEVICES 2 + +#define WSA881X_VERSION_ENTRY_SIZE 27 +#define WSA881X_OCP_CTL_TIMER_SEC 2 +#define WSA881X_OCP_CTL_TEMP_CELSIUS 25 +#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60 + +static int wsa881x_ocp_poll_timer_sec = WSA881X_OCP_CTL_POLL_TIMER_SEC; +module_param(wsa881x_ocp_poll_timer_sec, int, 0664); +MODULE_PARM_DESC(wsa881x_ocp_poll_timer_sec, "timer for ocp ctl polling"); + +static struct wsa881x_priv *dbgwsa881x; +static struct dentry *debugfs_wsa881x_dent; +static struct dentry *debugfs_peek; +static struct dentry *debugfs_poke; +static struct dentry *debugfs_reg_dump; +static unsigned int read_data; +static unsigned int devnum; + +static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, + bool enable); + +static const char * const wsa_pa_gain_text[] = { + "G_18_DB", "G_16P5_DB", "G_15_DB", "G_13P5_DB", "G_12_DB", "G_10P5_DB", + "G_9_DB", "G_7P5_DB", "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB", + "G_0_DB" +}; + +static const struct soc_enum wsa_pa_gain_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa_pa_gain_text), wsa_pa_gain_text); + +static int wsa_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->pa_gain; + + dev_dbg(codec->dev, "%s: PA gain = 0x%x\n", __func__, wsa881x->pa_gain); + + return 0; +} + +static int wsa_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + wsa881x->pa_gain = ucontrol->value.integer.value[0]; + + return 0; +} + +static int wsa881x_get_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->pa_mute; + + return 0; +} + +static int wsa881x_set_mute(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: mute current %d, new %d\n", + __func__, wsa881x->pa_mute, value); + + if (value) + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x80, 0x00); + wsa881x->pa_mute = value; + + return 0; +} + + +static const struct snd_kcontrol_new wsa_snd_controls[] = { + SOC_ENUM_EXT("WSA PA Gain", wsa_pa_gain_enum, + wsa_pa_gain_get, wsa_pa_gain_put), + SOC_SINGLE_EXT("WSA PA Mute", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_mute, wsa881x_set_mute), +}; + +static int codec_debug_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static int get_parameters(char *buf, u32 *param1, int num_of_par) +{ + char *token; + int base, cnt; + + token = strsep(&buf, " "); + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if ((token[1] == 'x') || (token[1] == 'X')) + base = 16; + else + base = 10; + + if (kstrtou32(token, base, ¶m1[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + return 0; +} + +static ssize_t wsa881x_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, struct file *file, + char __user *buf, size_t count, loff_t pos) +{ + struct wsa881x_priv *wsa881x; + char buffer[WSA881X_VERSION_ENTRY_SIZE]; + int len; + + wsa881x = (struct wsa881x_priv *) entry->private_data; + if (!wsa881x) { + pr_err("%s: wsa881x priv is null\n", __func__); + return -EINVAL; + } + + len = snprintf(buffer, sizeof(buffer), "WSA881X-SOUNDWIRE_2_0\n"); + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops wsa881x_codec_info_ops = { + .read = wsa881x_codec_version_read, +}; + +/* + * wsa881x_codec_info_create_codec_entry - creates wsa881x module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates wsa881x module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int wsa881x_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct wsa881x_priv *wsa881x; + struct snd_soc_card *card; + char name[80]; + + if (!codec_root || !codec) + return -EINVAL; + + wsa881x = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + snprintf(name, sizeof(name), "%s.%x", "wsa881x", + (u32)wsa881x->swr_slave->addr); + + wsa881x->entry = snd_info_create_subdir(codec_root->module, + (const char *)name, + codec_root); + if (!wsa881x->entry) { + dev_dbg(codec->dev, "%s: failed to create wsa881x entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + wsa881x->entry); + if (!version_entry) { + dev_dbg(codec->dev, "%s: failed to create wsa881x version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = wsa881x; + version_entry->size = WSA881X_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &wsa881x_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + wsa881x->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(wsa881x_codec_info_create_codec_entry); + +static bool is_swr_slv_reg_readable(int reg) +{ + bool ret = true; + + if (((reg > 0x46) && (reg < 0x4A)) || + ((reg > 0x4A) && (reg < 0x50)) || + ((reg > 0x55) && (reg < 0xE0)) || + ((reg > 0xE0) && (reg < 0xF0)) || + ((reg > 0xF0) && (reg < 0x100)) || + ((reg > 0x105) && (reg < 0x120)) || + ((reg > 0x128) && (reg < 0x130)) || + ((reg > 0x138) && (reg < 0x200)) || + ((reg > 0x205) && (reg < 0x220)) || + ((reg > 0x228) && (reg < 0x230)) || + ((reg > 0x238) && (reg < 0x300)) || + ((reg > 0x305) && (reg < 0x320)) || + ((reg > 0x328) && (reg < 0x330)) || + ((reg > 0x338) && (reg < 0x400)) || + ((reg > 0x405) && (reg < 0x420))) + ret = false; + + return ret; +} + +static ssize_t wsa881x_swrslave_reg_show(char __user *ubuf, size_t count, + loff_t *ppos) +{ + int i, reg_val, len; + ssize_t total = 0; + char tmp_buf[SWR_SLV_MAX_BUF_LEN]; + + if (!ubuf || !ppos || (devnum == 0)) + return 0; + + for (i = (((int) *ppos / BYTES_PER_LINE) + SWR_SLV_START_REG_ADDR); + i <= SWR_SLV_MAX_REG_ADDR; i++) { + if (!is_swr_slv_reg_readable(i)) + continue; + swr_read(dbgwsa881x->swr_slave, devnum, + i, ®_val, 1); + len = snprintf(tmp_buf, 25, "0x%.3x: 0x%.2x\n", i, + (reg_val & 0xFF)); + if ((total + len) >= count - 1) + break; + if (copy_to_user((ubuf + total), tmp_buf, len)) { + pr_err("%s: fail to copy reg dump\n", __func__); + total = -EFAULT; + goto copy_err; + } + *ppos += len; + total += len; + } + +copy_err: + return total; +} + +static ssize_t codec_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + char lbuf[SWR_SLV_RD_BUF_LEN]; + char *access_str; + ssize_t ret_cnt; + + if (!count || !file || !ppos || !ubuf) + return -EINVAL; + + access_str = file->private_data; + if (*ppos < 0) + return -EINVAL; + + if (!strcmp(access_str, "swrslave_peek")) { + snprintf(lbuf, sizeof(lbuf), "0x%x\n", (read_data & 0xFF)); + ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf, + strnlen(lbuf, 7)); + } else if (!strcmp(access_str, "swrslave_reg_dump")) { + ret_cnt = wsa881x_swrslave_reg_show(ubuf, count, ppos); + } else { + pr_err("%s: %s not permitted to read\n", __func__, access_str); + ret_cnt = -EPERM; + } + return ret_cnt; +} + +static ssize_t codec_debug_write(struct file *filp, + const char __user *ubuf, size_t cnt, loff_t *ppos) +{ + char lbuf[SWR_SLV_WR_BUF_LEN]; + int rc; + u32 param[5]; + char *access_str; + + if (!filp || !ppos || !ubuf) + return -EINVAL; + + access_str = filp->private_data; + if (cnt > sizeof(lbuf) - 1) + return -EINVAL; + + rc = copy_from_user(lbuf, ubuf, cnt); + if (rc) + return -EFAULT; + + lbuf[cnt] = '\0'; + if (!strcmp(access_str, "swrslave_poke")) { + /* write */ + rc = get_parameters(lbuf, param, 3); + if ((param[0] <= SWR_SLV_MAX_REG_ADDR) && (param[1] <= 0xFF) && + (rc == 0)) + swr_write(dbgwsa881x->swr_slave, param[2], + param[0], ¶m[1]); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "swrslave_peek")) { + /* read */ + rc = get_parameters(lbuf, param, 2); + if ((param[0] <= SWR_SLV_MAX_REG_ADDR) && (rc == 0)) + swr_read(dbgwsa881x->swr_slave, param[1], + param[0], &read_data, 1); + else + rc = -EINVAL; + } else if (!strcmp(access_str, "swrslave_reg_dump")) { + /* reg dump */ + rc = get_parameters(lbuf, param, 1); + if ((rc == 0) && (param[0] > 0) && + (param[0] <= SWR_SLV_MAX_DEVICES)) + devnum = param[0]; + else + rc = -EINVAL; + } + if (rc == 0) + rc = cnt; + else + pr_err("%s: rc = %d\n", __func__, rc); + + return rc; +} + +static const struct file_operations codec_debug_ops = { + .open = codec_debug_open, + .write = codec_debug_write, + .read = codec_debug_read, +}; + +static const struct reg_sequence wsa881x_pre_pmu_pa[] = { + {WSA881X_SPKR_DRV_GAIN, 0x41, 0}, + {WSA881X_SPKR_MISC_CTL1, 0x01, 0}, + {WSA881X_ADC_EN_DET_TEST_I, 0x01, 0}, + {WSA881X_ADC_EN_MODU_V, 0x02, 0}, + {WSA881X_ADC_EN_DET_TEST_V, 0x10, 0}, + {WSA881X_SPKR_PWRSTG_DBG, 0xA0, 0}, +}; + +static const struct reg_sequence wsa881x_pre_pmu_pa_2_0[] = { + {WSA881X_SPKR_DRV_GAIN, 0x41, 0}, + {WSA881X_SPKR_MISC_CTL1, 0x87, 0}, +}; + +static const struct reg_sequence wsa881x_post_pmu_pa[] = { + {WSA881X_SPKR_PWRSTG_DBG, 0x00, 0}, + {WSA881X_ADC_EN_DET_TEST_V, 0x00, 0}, + {WSA881X_ADC_EN_MODU_V, 0x00, 0}, + {WSA881X_ADC_EN_DET_TEST_I, 0x00, 0}, +}; + +static const struct reg_sequence wsa881x_vi_txfe_en[] = { + {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0}, + {WSA881X_SPKR_PROT_ATEST2, 0x0A, 0}, + {WSA881X_SPKR_PROT_FE_GAIN, 0xCF, 0}, +}; + +static const struct reg_sequence wsa881x_vi_txfe_en_2_0[] = { + {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0}, + {WSA881X_SPKR_PROT_ATEST2, 0x0A, 0}, + {WSA881X_SPKR_PROT_FE_GAIN, 0x47, 0}, +}; + +static int wsa881x_boost_ctrl(struct snd_soc_codec *codec, bool enable) +{ + dev_dbg(codec->dev, "%s: enable:%d\n", __func__, enable); + if (enable) + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x80); + else + snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x00); + /* + * 1.5ms sleep is needed after boost enable/disable as per + * HW requirement + */ + usleep_range(1500, 1510); + return 0; +} + +static int wsa881x_visense_txfe_ctrl(struct snd_soc_codec *codec, bool enable, + u8 isense1_gain, u8 isense2_gain, + u8 vsense_gain) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, + "%s: enable:%d, isense1 gain: %d, isense2 gain: %d, vsense_gain %d\n", + __func__, enable, isense1_gain, isense2_gain, vsense_gain); + + if (enable) { + regmap_multi_reg_write(wsa881x->regmap, + wsa881x_vi_txfe_en_2_0, + ARRAY_SIZE(wsa881x_vi_txfe_en_2_0)); + } else { + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_VSENSE_VCM, + 0x08, 0x08); + /* + * 200us sleep is needed after visense txfe disable as per + * HW requirement. + */ + usleep_range(200, 210); + snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, + 0x01, 0x00); + } + return 0; +} + +static int wsa881x_visense_adc_ctrl(struct snd_soc_codec *codec, bool enable) +{ + + dev_dbg(codec->dev, "%s: enable:%d\n", __func__, enable); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, (0x01 << 7), + (enable << 7)); + snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, (0x01 << 7), + (enable << 7)); + return 0; +} + +static void wsa881x_bandgap_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: enable:%d, bg_count:%d\n", __func__, + enable, wsa881x->bg_cnt); + mutex_lock(&wsa881x->bg_lock); + if (enable) { + ++wsa881x->bg_cnt; + if (wsa881x->bg_cnt == 1) { + snd_soc_update_bits(codec, WSA881X_TEMP_OP, + 0x08, 0x08); + /* 400usec sleep is needed as per HW requirement */ + usleep_range(400, 410); + snd_soc_update_bits(codec, WSA881X_TEMP_OP, + 0x04, 0x04); + } + } else { + --wsa881x->bg_cnt; + if (wsa881x->bg_cnt <= 0) { + WARN_ON(wsa881x->bg_cnt < 0); + wsa881x->bg_cnt = 0; + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x00); + snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x08, 0x00); + } + } + mutex_unlock(&wsa881x->bg_lock); +} + +static void wsa881x_clk_ctrl(struct snd_soc_codec *codec, bool enable) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: enable:%d, clk_count:%d\n", __func__, + enable, wsa881x->clk_cnt); + mutex_lock(&wsa881x->res_lock); + if (enable) { + ++wsa881x->clk_cnt; + if (wsa881x->clk_cnt == 1) { + snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x01); + snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x01); + } + } else { + --wsa881x->clk_cnt; + if (wsa881x->clk_cnt <= 0) { + WARN_ON(wsa881x->clk_cnt < 0); + wsa881x->clk_cnt = 0; + snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x00); + snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x00); + } + } + mutex_unlock(&wsa881x->res_lock); +} + +static int wsa881x_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->comp_enable; + return 0; +} + +static int wsa881x_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Compander enable current %d, new %d\n", + __func__, wsa881x->comp_enable, value); + wsa881x->comp_enable = value; + return 0; +} + +static int wsa881x_get_boost(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->boost_enable; + return 0; +} + +static int wsa881x_set_boost(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Boost enable current %d, new %d\n", + __func__, wsa881x->boost_enable, value); + wsa881x->boost_enable = value; + return 0; +} + +static int wsa881x_get_visense(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = wsa881x->visense_enable; + return 0; +} + +static int wsa881x_set_visense(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: VIsense enable current %d, new %d\n", + __func__, wsa881x->visense_enable, value); + wsa881x->visense_enable = value; + return 0; +} + +static const struct snd_kcontrol_new wsa881x_snd_controls[] = { + SOC_SINGLE_EXT("COMP Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_compander, wsa881x_set_compander), + + SOC_SINGLE_EXT("BOOST Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_boost, wsa881x_set_boost), + + SOC_SINGLE_EXT("VISENSE Switch", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_visense, wsa881x_set_visense), +}; + +static const struct snd_kcontrol_new swr_dac_port[] = { + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0) +}; + +static int wsa881x_set_port(struct snd_soc_codec *codec, int port_idx, + u8 *port_id, u8 *num_ch, u8 *ch_mask, u32 *ch_rate) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + *port_id = wsa881x->port[port_idx].port_id; + *num_ch = wsa881x->port[port_idx].num_ch; + *ch_mask = wsa881x->port[port_idx].ch_mask; + *ch_rate = wsa881x->port[port_idx].ch_rate; + return 0; +} + +static int wsa881x_enable_swr_dac_port(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + u8 port_id[WSA881X_MAX_SWR_PORTS]; + u8 num_ch[WSA881X_MAX_SWR_PORTS]; + u8 ch_mask[WSA881X_MAX_SWR_PORTS]; + u32 ch_rate[WSA881X_MAX_SWR_PORTS]; + u8 num_port = 0; + + dev_dbg(codec->dev, "%s: event %d name %s\n", __func__, + event, w->name); + if (wsa881x == NULL) + return -EINVAL; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wsa881x_set_port(codec, SWR_DAC_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port]); + ++num_port; + + if (wsa881x->comp_enable) { + wsa881x_set_port(codec, SWR_COMP_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port]); + ++num_port; + } + if (wsa881x->boost_enable) { + wsa881x_set_port(codec, SWR_BOOST_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port]); + ++num_port; + } + if (wsa881x->visense_enable) { + wsa881x_set_port(codec, SWR_VISENSE_PORT, + &port_id[num_port], &num_ch[num_port], + &ch_mask[num_port], &ch_rate[num_port]); + ++num_port; + } + swr_connect_port(wsa881x->swr_slave, &port_id[0], num_port, + &ch_mask[0], &ch_rate[0], &num_ch[0]); + break; + case SND_SOC_DAPM_POST_PMU: + break; + case SND_SOC_DAPM_PRE_PMD: + break; + case SND_SOC_DAPM_POST_PMD: + port_id[num_port] = wsa881x->port[SWR_DAC_PORT].port_id; + ++num_port; + if (wsa881x->comp_enable) { + port_id[num_port] = + wsa881x->port[SWR_COMP_PORT].port_id; + ++num_port; + } + if (wsa881x->boost_enable) { + port_id[num_port] = + wsa881x->port[SWR_BOOST_PORT].port_id; + ++num_port; + } + if (wsa881x->visense_enable) { + port_id[num_port] = + wsa881x->port[SWR_VISENSE_PORT].port_id; + ++num_port; + } + swr_disconnect_port(wsa881x->swr_slave, &port_id[0], num_port); + break; + default: + break; + } + return 0; +} + +static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: %s %d boost %d visense %d\n", __func__, + w->name, event, wsa881x->boost_enable, + wsa881x->visense_enable); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wsa881x_resource_acquire(codec, ENABLE); + wsa881x_boost_ctrl(codec, ENABLE); + break; + case SND_SOC_DAPM_POST_PMD: + swr_slvdev_datapath_control(wsa881x->swr_slave, + wsa881x->swr_slave->dev_num, + false); + wsa881x_boost_ctrl(codec, DISABLE); + wsa881x_resource_acquire(codec, DISABLE); + break; + } + return 0; +} + +static int wsa881x_ramp_pa_gain(struct snd_soc_codec *codec, + int min_gain, int max_gain, int udelay) +{ + int val; + + for (val = min_gain; max_gain <= val; val--) { + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, + 0xF0, val << 4); + /* + * 1ms delay is needed for every step change in gain as per + * HW requirement. + */ + usleep_range(udelay, udelay+10); + } + return 0; +} + +static void wsa881x_ocp_ctl_work(struct work_struct *work) +{ + struct wsa881x_priv *wsa881x; + struct delayed_work *dwork; + struct snd_soc_codec *codec; + int temp_val; + + dwork = to_delayed_work(work); + wsa881x = container_of(dwork, struct wsa881x_priv, ocp_ctl_work); + + codec = wsa881x->codec; + wsa881x_get_temp(wsa881x->tz_pdata.tz_dev, &temp_val); + dev_dbg(codec->dev, " temp = %d\n", temp_val); + + if (temp_val <= WSA881X_OCP_CTL_TEMP_CELSIUS) + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x00); + else + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); + + schedule_delayed_work(&wsa881x->ocp_ctl_work, + msecs_to_jiffies(wsa881x_ocp_poll_timer_sec * 1000)); +} + +static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int min_gain, max_gain; + + dev_dbg(codec->dev, "%s: %s %d\n", __func__, w->name, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x80); + regmap_multi_reg_write(wsa881x->regmap, + wsa881x_pre_pmu_pa_2_0, + ARRAY_SIZE(wsa881x_pre_pmu_pa_2_0)); + swr_slvdev_datapath_control(wsa881x->swr_slave, + wsa881x->swr_slave->dev_num, + true); + /* Set register mode if compander is not enabled */ + if (!wsa881x->comp_enable) + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, + 0x08, 0x08); + else + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, + 0x08, 0x00); + + break; + case SND_SOC_DAPM_POST_PMU: + if (!wsa881x->comp_enable) { + max_gain = wsa881x->pa_gain; + /* + * Gain has to set incrementally in 4 steps + * as per HW sequence + */ + if (max_gain > G_4P5DB) + min_gain = G_0DB; + else + min_gain = max_gain + 3; + /* + * 1ms delay is needed before change in gain + * as per HW requirement. + */ + usleep_range(1000, 1010); + wsa881x_ramp_pa_gain(codec, min_gain, max_gain, 1000); + } + if (wsa881x->visense_enable) { + wsa881x_visense_txfe_ctrl(codec, ENABLE, + 0x00, 0x03, 0x01); + snd_soc_update_bits(codec, WSA881X_ADC_EN_SEL_IBAIS, + 0x07, 0x01); + wsa881x_visense_adc_ctrl(codec, ENABLE); + } + schedule_delayed_work(&wsa881x->ocp_ctl_work, + msecs_to_jiffies(WSA881X_OCP_CTL_TIMER_SEC * 1000)); + /* Force remove group */ + swr_remove_from_group(wsa881x->swr_slave, + wsa881x->swr_slave->dev_num); + break; + case SND_SOC_DAPM_POST_PMD: + if (wsa881x->visense_enable) { + wsa881x_visense_adc_ctrl(codec, DISABLE); + wsa881x_visense_txfe_ctrl(codec, DISABLE, + 0x00, 0x01, 0x01); + } + cancel_delayed_work_sync(&wsa881x->ocp_ctl_work); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + + SND_SOC_DAPM_MIXER_E("SWR DAC_Port", SND_SOC_NOPM, 0, 0, swr_dac_port, + ARRAY_SIZE(swr_dac_port), wsa881x_enable_swr_dac_port, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_DAC_E("RDAC", NULL, WSA881X_SPKR_DAC_CTL, 7, 0, + wsa881x_rdac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_PGA_E("SPKR PGA", WSA881X_SPKR_DRV_EN, 7, 0, NULL, 0, + wsa881x_spkr_pa_event, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("SPKR"), +}; + +static const struct snd_soc_dapm_route wsa881x_audio_map[] = { + {"SWR DAC_Port", "Switch", "IN"}, + {"RDAC", NULL, "SWR DAC_Port"}, + {"SPKR PGA", NULL, "RDAC"}, + {"SPKR", NULL, "SPKR PGA"}, +}; + +int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, u8 num_port, + unsigned int *ch_mask, unsigned int *ch_rate) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + int i; + + if (!port || !ch_mask || !ch_rate || + (num_port > WSA881X_MAX_SWR_PORTS)) { + dev_err(codec->dev, + "%s: Invalid port=%pK, ch_mask=%pK, ch_rate=%pK\n", + __func__, port, ch_mask, ch_rate); + return -EINVAL; + } + for (i = 0; i < num_port; i++) { + wsa881x->port[i].port_id = port[i]; + wsa881x->port[i].ch_mask = ch_mask[i]; + wsa881x->port[i].ch_rate = ch_rate[i]; + wsa881x->port[i].num_ch = __sw_hweight8(ch_mask[i]); + } + return 0; +} +EXPORT_SYMBOL(wsa881x_set_channel_map); + +static void wsa881x_init(struct snd_soc_codec *codec) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + wsa881x->version = snd_soc_read(codec, WSA881X_CHIP_ID1); + wsa881x_regmap_defaults(wsa881x->regmap, wsa881x->version); + /* Bring out of analog reset */ + snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x02, 0x02); + /* Bring out of digital reset */ + snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x01, 0x01); + + snd_soc_update_bits(codec, WSA881X_CLOCK_CONFIG, 0x10, 0x10); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x02, 0x02); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80); + snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06); + snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_INT, 0xFF, 0x00); + snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0xF0, 0x40); + snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0x0E, 0x0E); + snd_soc_update_bits(codec, WSA881X_BOOST_LOOP_STABILITY, + 0x03, 0x03); + snd_soc_update_bits(codec, WSA881X_BOOST_MISC2_CTL, 0xFF, 0x14); + snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, 0x80, 0x80); + snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, 0x03, 0x00); + snd_soc_update_bits(codec, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, + 0x0C, 0x04); + snd_soc_update_bits(codec, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, + 0x03, 0x00); + if (snd_soc_read(codec, WSA881X_OTP_REG_0)) + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, + 0xF0, 0x70); + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT2, + 0xF0, 0x30); + snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x08, 0x08); + snd_soc_update_bits(codec, WSA881X_BOOST_CURRENT_LIMIT, + 0x0F, 0x08); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x30, 0x30); + snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x0C, 0x00); + snd_soc_update_bits(codec, WSA881X_OTP_REG_28, 0x3F, 0x3A); + snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG1, + 0xFF, 0xB2); + snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG2, + 0xFF, 0x05); +} + +static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, + bool enable) +{ + wsa881x_clk_ctrl(codec, enable); + wsa881x_bandgap_ctrl(codec, enable); + return 0; +} + +static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec, + struct wsa_temp_register *wsa_temp_reg) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + struct swr_device *dev; + u8 retry = WSA881X_NUM_RETRY; + u8 devnum = 0; + + if (!wsa881x) { + dev_err(codec->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + dev = wsa881x->swr_slave; + if (dev && (wsa881x->state == WSA881X_DEV_DOWN)) { + while (swr_get_logical_dev_num(dev, dev->addr, &devnum) && + retry--) { + /* Retry after 1 msec delay */ + usleep_range(1000, 1100); + } + if (retry == 0) { + dev_err(codec->dev, + "%s get devnum %d for dev addr %lx failed\n", + __func__, devnum, dev->addr); + return -EINVAL; + } + } + mutex_lock(&wsa881x->res_lock); + if (!wsa881x->clk_cnt) { + regcache_mark_dirty(wsa881x->regmap); + regcache_sync(wsa881x->regmap); + } + mutex_unlock(&wsa881x->res_lock); + + wsa881x_resource_acquire(codec, ENABLE); + + snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00); + wsa_temp_reg->dmeas_msb = snd_soc_read(codec, WSA881X_TEMP_MSB); + wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, WSA881X_TEMP_LSB); + snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x01); + wsa_temp_reg->d1_msb = snd_soc_read(codec, WSA881X_OTP_REG_1); + wsa_temp_reg->d1_lsb = snd_soc_read(codec, WSA881X_OTP_REG_2); + wsa_temp_reg->d2_msb = snd_soc_read(codec, WSA881X_OTP_REG_3); + wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4); + + wsa881x_resource_acquire(codec, DISABLE); + + return 0; +} + +static int wsa881x_probe(struct snd_soc_codec *codec) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + struct swr_device *dev; + + if (!wsa881x) + return -EINVAL; + + dev = wsa881x->swr_slave; + wsa881x->codec = codec; + mutex_init(&wsa881x->bg_lock); + mutex_init(&wsa881x->res_lock); + wsa881x_init(codec); + snprintf(wsa881x->tz_pdata.name, sizeof(wsa881x->tz_pdata.name), + "%s.%x", "wsatz", (u8)dev->addr); + wsa881x->bg_cnt = 0; + wsa881x->clk_cnt = 0; + wsa881x->tz_pdata.codec = codec; + wsa881x->tz_pdata.wsa_temp_reg_read = wsa881x_temp_reg_read; + wsa881x_init_thermal(&wsa881x->tz_pdata); + snd_soc_add_codec_controls(codec, wsa_snd_controls, + ARRAY_SIZE(wsa_snd_controls)); + INIT_DELAYED_WORK(&wsa881x->ocp_ctl_work, wsa881x_ocp_ctl_work); + return 0; +} + +static int wsa881x_remove(struct snd_soc_codec *codec) +{ + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + + if (wsa881x->tz_pdata.tz_dev) + wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev); + mutex_destroy(&wsa881x->bg_lock); + mutex_destroy(&wsa881x->res_lock); + + return 0; +} + +static struct regmap *wsa881x_get_regmap(struct device *dev) +{ + struct wsa881x_priv *control = swr_get_dev_data(to_swr_device(dev)); + + if (!control) + return NULL; + + return control->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_wsa881x = { + .probe = wsa881x_probe, + .remove = wsa881x_remove, + .get_regmap = wsa881x_get_regmap, + .component_driver = { + .controls = wsa881x_snd_controls, + .num_controls = ARRAY_SIZE(wsa881x_snd_controls), + .dapm_widgets = wsa881x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets), + .dapm_routes = wsa881x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map), + }, +}; + +static int wsa881x_gpio_ctrl(struct wsa881x_priv *wsa881x, bool enable) +{ + int ret = 0; + + if (wsa881x->pd_gpio < 0) { + dev_err(wsa881x->dev, "%s: gpio is not valid %d\n", + __func__, wsa881x->pd_gpio); + return -EINVAL; + } + + if (wsa881x->wsa_rst_np) { + if (enable) + ret = msm_cdc_pinctrl_select_active_state( + wsa881x->wsa_rst_np); + else + ret = msm_cdc_pinctrl_select_sleep_state( + wsa881x->wsa_rst_np); + if (ret != 0) + dev_err(wsa881x->dev, + "%s: Failed to turn state %d; ret=%d\n", + __func__, enable, ret); + } else { + if (gpio_is_valid(wsa881x->pd_gpio)) + gpio_direction_output(wsa881x->pd_gpio, enable); + } + + return ret; +} + +static int wsa881x_gpio_init(struct swr_device *pdev) +{ + int ret = 0; + struct wsa881x_priv *wsa881x; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(&pdev->dev, "%s: gpio %d request with name %s\n", + __func__, wsa881x->pd_gpio, dev_name(&pdev->dev)); + ret = gpio_request(wsa881x->pd_gpio, dev_name(&pdev->dev)); + if (ret) { + if (ret == -EBUSY) { + /* GPIO was already requested */ + dev_dbg(&pdev->dev, + "%s: gpio %d is already set to high\n", + __func__, wsa881x->pd_gpio); + ret = 0; + } else { + dev_err(&pdev->dev, "%s: Failed to request gpio %d, err: %d\n", + __func__, wsa881x->pd_gpio, ret); + } + } + return ret; +} + +static int wsa881x_swr_probe(struct swr_device *pdev) +{ + int ret = 0; + struct wsa881x_priv *wsa881x; + u8 devnum = 0; + bool pin_state_current = false; + + wsa881x = devm_kzalloc(&pdev->dev, sizeof(struct wsa881x_priv), + GFP_KERNEL); + if (!wsa881x) + return -ENOMEM; + wsa881x->wsa_rst_np = of_parse_phandle(pdev->dev.of_node, + "qcom,spkr-sd-n-node", 0); + if (!wsa881x->wsa_rst_np) { + dev_dbg(&pdev->dev, "%s: Not using pinctrl, fallback to gpio\n", + __func__); + wsa881x->pd_gpio = of_get_named_gpio(pdev->dev.of_node, + "qcom,spkr-sd-n-gpio", 0); + if (wsa881x->pd_gpio < 0) { + dev_err(&pdev->dev, "%s: %s property is not found %d\n", + __func__, "qcom,spkr-sd-n-gpio", + wsa881x->pd_gpio); + goto err; + } + dev_dbg(&pdev->dev, "%s: reset gpio %d\n", __func__, + wsa881x->pd_gpio); + } + swr_set_dev_data(pdev, wsa881x); + + wsa881x->swr_slave = pdev; + + if (!wsa881x->wsa_rst_np) { + ret = wsa881x_gpio_init(pdev); + if (ret) + goto err; + } + if (wsa881x->wsa_rst_np) + pin_state_current = msm_cdc_pinctrl_get_state( + wsa881x->wsa_rst_np); + wsa881x_gpio_ctrl(wsa881x, true); + wsa881x->state = WSA881X_DEV_UP; + + if (!debugfs_wsa881x_dent) { + dbgwsa881x = wsa881x; + debugfs_wsa881x_dent = debugfs_create_dir( + "wsa881x_swr_slave", 0); + if (!IS_ERR(debugfs_wsa881x_dent)) { + debugfs_peek = debugfs_create_file("swrslave_peek", + S_IFREG | 0444, debugfs_wsa881x_dent, + (void *) "swrslave_peek", + &codec_debug_ops); + + debugfs_poke = debugfs_create_file("swrslave_poke", + S_IFREG | 0444, debugfs_wsa881x_dent, + (void *) "swrslave_poke", + &codec_debug_ops); + + debugfs_reg_dump = debugfs_create_file( + "swrslave_reg_dump", + S_IFREG | 0444, + debugfs_wsa881x_dent, + (void *) "swrslave_reg_dump", + &codec_debug_ops); + } + } + + /* + * Add 5msec delay to provide sufficient time for + * soundwire auto enumeration of slave devices as + * as per HW requirement. + */ + usleep_range(5000, 5010); + ret = swr_get_logical_dev_num(pdev, pdev->addr, &devnum); + if (ret) { + dev_dbg(&pdev->dev, + "%s get devnum %d for dev addr %lx failed\n", + __func__, devnum, pdev->addr); + goto dev_err; + } + pdev->dev_num = devnum; + + wsa881x->regmap = devm_regmap_init_swr(pdev, + &wsa881x_regmap_config); + if (IS_ERR(wsa881x->regmap)) { + ret = PTR_ERR(wsa881x->regmap); + dev_err(&pdev->dev, "%s: regmap_init failed %d\n", + __func__, ret); + goto dev_err; + } + + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wsa881x, + NULL, 0); + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed\n", + __func__); + goto dev_err; + } + + return 0; + +dev_err: + if (pin_state_current == false) + wsa881x_gpio_ctrl(wsa881x, false); + swr_remove_device(pdev); +err: + return ret; +} + +static int wsa881x_swr_remove(struct swr_device *pdev) +{ + struct wsa881x_priv *wsa881x; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + debugfs_remove_recursive(debugfs_wsa881x_dent); + debugfs_wsa881x_dent = NULL; + snd_soc_unregister_codec(&pdev->dev); + if (wsa881x->pd_gpio) + gpio_free(wsa881x->pd_gpio); + swr_set_dev_data(pdev, NULL); + return 0; +} + +static int wsa881x_swr_up(struct swr_device *pdev) +{ + int ret; + struct wsa881x_priv *wsa881x; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + ret = wsa881x_gpio_ctrl(wsa881x, true); + if (ret) + dev_err(&pdev->dev, "%s: Failed to enable gpio\n", __func__); + else + wsa881x->state = WSA881X_DEV_UP; + + return ret; +} + +static int wsa881x_swr_down(struct swr_device *pdev) +{ + struct wsa881x_priv *wsa881x; + int ret; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + if (delayed_work_pending(&wsa881x->ocp_ctl_work)) + cancel_delayed_work_sync(&wsa881x->ocp_ctl_work); + ret = wsa881x_gpio_ctrl(wsa881x, false); + if (ret) + dev_err(&pdev->dev, "%s: Failed to disable gpio\n", __func__); + else + wsa881x->state = WSA881X_DEV_DOWN; + + return ret; +} + +static int wsa881x_swr_reset(struct swr_device *pdev) +{ + struct wsa881x_priv *wsa881x; + u8 retry = WSA881X_NUM_RETRY; + u8 devnum = 0; + + wsa881x = swr_get_dev_data(pdev); + if (!wsa881x) { + dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); + return -EINVAL; + } + wsa881x->bg_cnt = 0; + wsa881x->clk_cnt = 0; + while (swr_get_logical_dev_num(pdev, pdev->addr, &devnum) && retry--) { + /* Retry after 1 msec delay */ + usleep_range(1000, 1100); + } + pdev->dev_num = devnum; + regcache_mark_dirty(wsa881x->regmap); + regcache_sync(wsa881x->regmap); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int wsa881x_swr_suspend(struct device *dev) +{ + dev_dbg(dev, "%s: system suspend\n", __func__); + return 0; +} + +static int wsa881x_swr_resume(struct device *dev) +{ + struct wsa881x_priv *wsa881x = swr_get_dev_data(to_swr_device(dev)); + + if (!wsa881x) { + dev_err(dev, "%s: wsa881x private data is NULL\n", __func__); + return -EINVAL; + } + dev_dbg(dev, "%s: system resume\n", __func__); + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops wsa881x_swr_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(wsa881x_swr_suspend, wsa881x_swr_resume) +}; + +static const struct swr_device_id wsa881x_swr_id[] = { + {"wsa881x", 0}, + {} +}; + +static const struct of_device_id wsa881x_swr_dt_match[] = { + { + .compatible = "qcom,wsa881x", + }, + {} +}; + +static struct swr_driver wsa881x_codec_driver = { + .driver = { + .name = "wsa881x", + .owner = THIS_MODULE, + .pm = &wsa881x_swr_pm_ops, + .of_match_table = wsa881x_swr_dt_match, + }, + .probe = wsa881x_swr_probe, + .remove = wsa881x_swr_remove, + .id_table = wsa881x_swr_id, + .device_up = wsa881x_swr_up, + .device_down = wsa881x_swr_down, + .reset_device = wsa881x_swr_reset, +}; + +static int __init wsa881x_codec_init(void) +{ + return swr_driver_register(&wsa881x_codec_driver); +} + +static void __exit wsa881x_codec_exit(void) +{ + swr_driver_unregister(&wsa881x_codec_driver); +} + +module_init(wsa881x_codec_init); +module_exit(wsa881x_codec_exit); + +MODULE_DESCRIPTION("WSA881x Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/wsa881x.h b/sound/soc/codecs/wsa881x.h new file mode 100644 index 000000000000..be234ac0cd07 --- /dev/null +++ b/sound/soc/codecs/wsa881x.h @@ -0,0 +1,34 @@ +/* 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 _WSA881X_H +#define _WSA881X_H + +#include +#include +#include +#include "wsa881x-registers.h" + +#define WSA881X_MAX_SWR_PORTS 4 + +extern int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, + u8 num_port, unsigned int *ch_mask, + unsigned int *ch_rate); + +extern const u8 wsa881x_reg_readable[WSA881X_CACHE_SIZE]; +extern struct regmap_config wsa881x_regmap_config; +extern int wsa881x_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +void wsa881x_regmap_defaults(struct regmap *regmap, u8 version); + +#endif /* _WSA881X_H */ diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig new file mode 100644 index 000000000000..c557ae06e95f --- /dev/null +++ b/sound/soc/msm/Kconfig @@ -0,0 +1,283 @@ +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_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 DOLBY_LICENSE + bool "Enable Dolby LICENSE" + depends on SND_SOC_MSM_QDSP6V2_INTF + help + To add support for dolby DAP post processing, + and retain DAP set license functionality only. + This is required by Dolby GEF implementation which needs + nothing but dolby license validation functionality in driver. + +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 SDM660_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 MSM_CDC_PINCTRL + select SND_SOC_MSM_SDW + select SND_SOC_SDM660_CDC + select SND_SOC_MSM_HDMI_CODEC_RX + select QTI_PP + select DTS_SRS_TM + select DOLBY_LICENSE + select SND_HWDEP + select MSM_ULTRASOUND + select DTS_EAGLE + select SND_SOC_SDM660_COMMON + select SND_SOC_COMPRESS + select PINCTRL_LPI + 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 SDM660_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 SND_SOC_MSM_HDMI_CODEC_RX + select MFD_CORE + select QTI_PP + select DTS_SRS_TM + select DOLBY_LICENSE + select SND_SOC_CPE + select SND_SOC_WCD_CPE + select SND_HWDEP + select MSM_ULTRASOUND + select DTS_EAGLE + select SND_SOC_SDM660_COMMON + select SND_SOC_COMPRESS + select PINCTRL_LPI + 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_MACHINE_MSM8998 + tristate "SoC Machine driver for MSM8998 boards" + select SND_SOC_WSA881X + select SND_SOC_WCD9335 + select SND_SOC_WCD934X + select SND_SOC_CPE + + help + To enable the machine driver and the + corresponding DAI-links on MSM8998. + All platform specific audio modules are + enabled here. + +config SND_SOC_MSM8998 + tristate "Sound SoC drivers to interface with DSP" + 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_MSM_HDMI_CODEC_RX + select DTS_SRS_TM + select QTI_PP + select MSM_ULTRASOUND + select DOLBY_LICENSE + 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_660 + tristate "SoC Machine driver for SDM660 boards" + depends on ARCH_SDM660 + select SND_SOC_INT_CODEC + select SND_SOC_EXT_CODEC + help + To add support for SoC audio on SDM660. + 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_MACHINE_SDM845 + tristate "SoC Machine driver for SDM845 boards" + select SND_SOC_WSA881X + select SND_SOC_WCD934X + + help + To enable the machine driver and the + corresponding DAI-links on SDM845. + All platform specific audio modules are + enabled here. + +config SND_SOC_SDM845 + tristate "SoC Machine driver for SDM845 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 DTS_SRS_TM + select QTI_PP + select MSM_ULTRASOUND + select DOLBY_DS2 + select SND_HWDEP + select DTS_EAGLE + help + To add support for SoC audio on SDM845. + This enables sound soc drivers that interfaces + with DSP. This also enables 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..caf884322d9e --- /dev/null +++ b/sound/soc/msm/Makefile @@ -0,0 +1,39 @@ +# 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_MACHINE_MSM8998) += snd-soc-msm8998.o + +# for SDM660 sound card driver +snd-soc-sdm660-common-objs := sdm660-common.o +obj-$(CONFIG_SND_SOC_SDM660_COMMON) += snd-soc-sdm660-common.o + +# for SDM660 sound card driver +snd-soc-int-codec-objs := sdm660-internal.o +obj-$(CONFIG_SND_SOC_INT_CODEC) += snd-soc-sdm660-common.o +obj-$(CONFIG_SND_SOC_INT_CODEC) += snd-soc-int-codec.o + +# for SDM660 sound card driver +snd-soc-ext-codec-objs := sdm660-external.o sdm660-ext-dai-links.o +obj-$(CONFIG_SND_SOC_EXT_CODEC) += snd-soc-sdm660-common.o +obj-$(CONFIG_SND_SOC_EXT_CODEC) += snd-soc-ext-codec.o + +# for SDM845 sound card driver +snd-soc-sdm845-objs := sdm845.o +obj-$(CONFIG_SND_SOC_MACHINE_SDM845) += snd-soc-sdm845.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..7b65dda227b9 --- /dev/null +++ b/sound/soc/msm/msm-cpe-lsm.c @@ -0,0 +1,3342 @@ +/* + * 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_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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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"); + + 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 (rc < 0) + 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: + case SNDRV_LSM_EVENT_STATUS_V3: { + struct snd_lsm_event_status *user; + struct snd_lsm_event_status_v3 *user_v3; + + dev_dbg(rtd->dev, + "%s: %s\n", + __func__, "SNDRV_LSM_EVENT_STATUS(_V3)"); + if (!arg) { + dev_err(rtd->dev, + "%s: Invalid argument to ioctl %s\n", + __func__, + "SNDRV_LSM_EVENT_STATUS(_V3)"); + return -EINVAL; + } + + /* + * 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 (cmd == SNDRV_LSM_EVENT_STATUS) { + user = arg; + 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 { + user_v3 = arg; + if (lsm_d->ev_det_pld_size > + user_v3->payload_size) { + dev_err(rtd->dev, + "%s: avail pld_bytes = %u, needed = %u\n", + __func__, + user_v3->payload_size, + lsm_d->ev_det_pld_size); + return -EINVAL; + } + /* event status timestamp not supported + * on CPE mode. Set msw and lsw to 0. + */ + user_v3->timestamp_lsw = 0; + user_v3->timestamp_msw = 0; + user_v3->status = lsm_d->ev_det_status; + user_v3->payload_size = + lsm_d->ev_det_pld_size; + memcpy(user_v3->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__); + if (cmd == SNDRV_LSM_EVENT_STATUS) { + user = arg; + user->payload_size = 0; + } else { + user_v3 = arg; + user_v3->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: + 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; + + case SNDRV_LSM_SET_PORT: { + u32 port_id = cpe->input_port_id; + + dev_dbg(rtd->dev, "%s: %s\n", __func__, "SNDRV_LSM_SET_PORT"); + rc = lsm_ops->lsm_set_port(cpe->core_handle, session, &port_id); + if (rc) { + dev_err(rtd->dev, + "%s: lsm_set_port 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, + u16 event_det_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_det_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); + /* Check if 'p_info->param_size + offset' crosses U32_MAX. */ + if (p_info->param_size > U32_MAX - offset) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + return -EINVAL; + } + 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"); + err = -EINVAL; + goto done; + } + + 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->status); + msm_cpe_process_event_status_done(lsm_d); + kfree(event_status); + } + break; + case SNDRV_LSM_EVENT_STATUS_V3: { + struct snd_lsm_event_status_v3 u_event_status; + struct snd_lsm_event_status_v3 *event_status = NULL; + int u_pld_size = 0; + + if (copy_from_user(&u_event_status, (void *)arg, + sizeof(struct snd_lsm_event_status_v3))) { + dev_err(rtd->dev, + "%s: event status copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_event_status_v3)); + 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_v3) + + 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->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"); + err = -EINVAL; + goto done; + } + + 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"); + err = -EINVAL; + goto done; + } + + if (!arg) { + dev_err(rtd->dev, + "%s: %s: No Param data to set\n", + __func__, "SET_MODULE_PARAMS"); + err = -EINVAL; + goto done; + } + + 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)); + err = -EFAULT; + goto done; + } + + 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); + err = -EINVAL; + goto done; + } + + 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); + + err = -EFAULT; + goto done; + } + + params = kzalloc(p_size, GFP_KERNEL); + if (!params) { + err = -ENOMEM; + goto done; + } + + 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); + err = -EFAULT; + goto done; + } + + 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_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; + uint32_t param_type; +}; + +struct snd_lsm_module_params_32 { + compat_uptr_t params; + u32 num_params; + u32 data_size; +}; + +enum { + 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"); + err = -EINVAL; + goto done; + } + + 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_STATUS: { + 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; + 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->status); + msm_cpe_process_event_status_done(lsm_d); + kfree(event_status); + kfree(udata_32); + } + break; + case SNDRV_LSM_EVENT_STATUS_V3: { + struct snd_lsm_event_status_v3 *event_status = NULL; + struct snd_lsm_event_status_v3 u_event_status32; + struct snd_lsm_event_status_v3 *udata_32 = NULL; + int u_pld_size = 0; + + dev_dbg(rtd->dev, + "%s: ioctl %s\n", __func__, + "SNDRV_LSM_EVENT_STATUS_V3_32"); + + if (copy_from_user(&u_event_status32, (void *)arg, + sizeof(struct snd_lsm_event_status_v3))) { + dev_err(rtd->dev, + "%s: event status copy from user failed, size %zd\n", + __func__, + sizeof(struct snd_lsm_event_status_v3)); + 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_v3) + + 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; + 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_STATUS_V3_32", + 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->timestamp_lsw = + event_status->timestamp_lsw; + udata_32->timestamp_msw = + event_status->timestamp_msw; + 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->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"); + + err = -EINVAL; + goto done; + } + + 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"); + err = -EINVAL; + goto done; + } + + 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)); + err = -EFAULT; + goto done; + } + + 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); + err = -EINVAL; + goto done; + } + + 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); + err = -EINVAL; + goto done; + } + + p_size = sizeof(struct lsm_params_info_32) * + p_data.num_params; + + params32 = kzalloc(p_size, GFP_KERNEL); + if (!params32) { + err = -ENOMEM; + goto done; + } + + p_size = sizeof(struct lsm_params_info) * p_data.num_params; + params = kzalloc(p_size, GFP_KERNEL); + if (!params) { + kfree(params32); + err = -ENOMEM; + goto done; + } + + 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); + err = -EFAULT; + goto done; + } + + 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; + } + case SNDRV_LSM_REG_SND_MODEL_V2: + case SNDRV_LSM_SET_PARAMS: + case SNDRV_LSM_SET_MODULE_PARAMS: + /* + * In ideal cases, the compat_ioctl should never be called + * with the above unlocked ioctl commands. Print error + * and return error if it does. + */ + dev_err(rtd->dev, + "%s: Invalid cmd for compat_ioctl\n", + __func__); + err = -EINVAL; + 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; + + if (!platform || !platform->component.card) { + pr_err("%s: Invalid platform or card\n", + __func__); + return -EINVAL; + } + + card = platform->component.card; + + /* Match platform to codec */ + list_for_each_entry(rtd, &card->rtd_list, list) { + 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 const 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..c319ccf43feb --- /dev/null +++ b/sound/soc/msm/msm-dai-fe.c @@ -0,0 +1,2690 @@ +/* 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 = 8, + .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 = "SLIMBUS7_HOSTLESS Playback", + .aif_name = "SLIM7_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 = "SLIMBUS7_HOSTLESS Capture", + .aif_name = "SLIM7_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 = "SLIMBUS7_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 = "USBAUDIO_HOSTLESS Playback", + .aif_name = "USBAUDIO_DL_HL", + .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_min = 8000, + .rate_max = 384000, + }, + .capture = { + .stream_name = "USBAUDIO_HOSTLESS Capture", + .aif_name = "USBAUDIO_UL_HL", + .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_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_dai_ops, + .name = "USBAUDIO_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 = 4, + .rate_min = 8000, + .rate_max = 192000, + }, + .ops = &msm_fe_dai_ops, + .name = "INT4_MI2S_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "INT3 MI2S_TX Hostless Capture", + .aif_name = "INT3_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 = "INT3_MI2S_TX_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 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .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 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .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 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .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 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .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 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .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 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .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 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .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 | + SNDRV_PCM_RATE_48000), + .formats = (SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE), + .channels_min = 1, + .channels_max = 4, + .rate_min = 16000, + .rate_max = 48000, + }, + .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 = 192000, + }, + .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, + }, + { + .playback = { + .stream_name = "MultiMedia20 Playback", + .aif_name = "MM_DL20", + .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 = "MultiMedia20 Capture", + .aif_name = "MM_UL20", + .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 = "MultiMedia20", + .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..35766b44981c --- /dev/null +++ b/sound/soc/msm/msm-pcm-hostless.c @@ -0,0 +1,82 @@ +/* Copyright (c) 2011-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 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 const 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..45c5479884a2 --- /dev/null +++ b/sound/soc/msm/msm8996.c @@ -0,0 +1,4006 @@ +/* + * 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_soc_kcontrol_codec(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_component *aux_comp; + 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 (!list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->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->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->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->id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = msm_slim_0_tx_ch; + /* For _tx3 case */ + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = msm_slim_1_tx_ch; + else if (dai_link->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->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->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->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, + .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, + .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", + .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, + .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, + .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 */ + .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", + .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, + .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", + .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 */ + .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 */ + .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 */ + .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", + .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", + .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, + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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, + .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", + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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, + .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", + .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", + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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..222c65a3182d --- /dev/null +++ b/sound/soc/msm/msm8998.c @@ -0,0 +1,7481 @@ +/* + * 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; +}; + +enum pinctrl_pin_state { + STATE_DISABLE = 0, /* All pins are in sleep state */ + STATE_MI2S_ACTIVE, /* IS2 = active, TDM = sleep */ + STATE_TDM_ACTIVE, /* IS2 = sleep, TDM = active */ +}; + +struct msm_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *mi2s_disable; + struct pinctrl_state *tdm_disable; + struct pinctrl_state *mi2s_active; + struct pinctrl_state *tdm_active; + enum pinctrl_pin_state curr_state; +}; + +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_pinctrl_info pinctrl_info; +}; + +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 const char *const pin_states[] = {"sleep", "i2s-active", + "tdm-active"}; + +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", "KHZ_32", "KHZ_44P1", + "KHZ_88P2", "KHZ_176P4"}; +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(mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_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, + .mbhc_micbias = MIC_BIAS_2, + .anc_micbias = MIC_BIAS_2, + .enable_anc_mic_detect = false, +}; + +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_176P4KHZ: + sample_rate_val = 6; + break; + + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 4; + break; + + case SAMPLING_RATE_32KHZ: + 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: 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 6: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 4: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_32KHZ; + break; + 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_get_format(int value) +{ + int format; + + 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_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_get_format_value(int format) +{ + int value; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + break; + default: + value = 0; + break; + } + return value; +} + +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_mi2s_rx_format_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_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_format_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].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_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_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_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].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +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("PRIM_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_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->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->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->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->id); + if (idx < 0) { + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); + 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_component *aux_comp; + 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}; + + /* Tavil Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch_tavil[WCD934X_RX_MAX] = {144, 145, 146, 147, 148, + 149, 150, 151}; + unsigned int tx_ch_tavil[WCD934X_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"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + + 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 LINEOUT1"); + snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2"); + } + + snd_soc_dapm_sync(dapm); + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch_tavil), + tx_ch_tavil, ARRAY_SIZE(rx_ch_tavil), + rx_ch_tavil); + } else { + 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 && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + tavil_set_spkr_mode(rtd->codec, + WCD934X_SPKR_MODE_1); + tavil_set_spkr_gain_offset(rtd->codec, + WCD934X_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 && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->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->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->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->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->id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = slim_tx_cfg[0].channels; + /* For _tx3 case */ + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = slim_tx_cfg[1].channels; + else if (dai_link->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->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->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->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; + + 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 (ret < 0) + 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__); + } + } + 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_S32_LE: + 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; + } +} + +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->id); + if (port_id < 0) { + 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_set_pinctrl(struct msm_pinctrl_info *pinctrl_info, + enum pinctrl_pin_state new_state) +{ + int ret = 0; + int curr_state = 0; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + if (pinctrl_info->pinctrl == NULL) { + pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + curr_state = pinctrl_info->curr_state; + pinctrl_info->curr_state = new_state; + pr_debug("%s: curr_state = %s new_state = %s\n", __func__, + pin_states[curr_state], pin_states[pinctrl_info->curr_state]); + + if (curr_state == pinctrl_info->curr_state) { + pr_debug("%s: Already in same state\n", __func__); + goto err; + } + + if (curr_state != STATE_DISABLE && + pinctrl_info->curr_state != STATE_DISABLE) { + pr_debug("%s: state already active cannot switch\n", __func__); + ret = -EIO; + goto err; + } + + switch (pinctrl_info->curr_state) { + case STATE_MI2S_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_active); + if (ret) { + pr_err("%s: MI2S state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_TDM_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_active); + if (ret) { + pr_err("%s: TDM state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_DISABLE: + if (curr_state == STATE_MI2S_ACTIVE) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + } else { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_disable); + } + if (ret) { + pr_err("%s: state disable failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + default: + pr_err("%s: TLMM pin state is invalid\n", __func__); + return -EINVAL; + } + +err: + return ret; +} + +static void msm_release_pinctrl(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); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info->pinctrl) { + devm_pinctrl_put(pinctrl_info->pinctrl); + pinctrl_info->pinctrl = NULL; + } +} + +static int msm_get_pinctrl(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); + struct msm_pinctrl_info *pinctrl_info = NULL; + struct pinctrl *pinctrl; + int ret; + + pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + pr_err("%s: Unable to get pinctrl handle\n", __func__); + return -EINVAL; + } + pinctrl_info->pinctrl = pinctrl; + + /* get all the states handles from Device Tree */ + pinctrl_info->mi2s_disable = pinctrl_lookup_state(pinctrl, + "quat-mi2s-sleep"); + if (IS_ERR(pinctrl_info->mi2s_disable)) { + pr_err("%s: could not get mi2s_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->mi2s_active = pinctrl_lookup_state(pinctrl, + "quat-mi2s-active"); + if (IS_ERR(pinctrl_info->mi2s_active)) { + pr_err("%s: could not get mi2s_active pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_disable = pinctrl_lookup_state(pinctrl, + "quat-tdm-sleep"); + if (IS_ERR(pinctrl_info->tdm_disable)) { + pr_err("%s: could not get tdm_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_active = pinctrl_lookup_state(pinctrl, + "quat-tdm-active"); + if (IS_ERR(pinctrl_info->tdm_active)) { + pr_err("%s: could not get tdm_active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + if (ret != 0) { + pr_err("%s: Disable TLMM pins failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + pinctrl_info->curr_state = STATE_DISABLE; + + return 0; + +err: + devm_pinctrl_put(pinctrl); + pinctrl_info->pinctrl = NULL; + return -EINVAL; +} + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + 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); + + if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) { + 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; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { + 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; + } else { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static int msm8998_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[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + slots = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + slot_width = 32; + channels = slots; + + pr_debug("%s: slot_width %d slots %d\n", __func__, slot_width, slots); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_debug("%s: slot_width %d\n", __func__, slot_width); + 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 { + pr_err("%s: invalid use case, err:%d\n", + __func__, ret); + } + +end: + return ret; +} + +static int msm8998_tdm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + 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); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); + if (ret) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret); + + return ret; +} + +static void msm8998_tdm_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret = 0; + 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); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret); + +} + +static struct snd_soc_ops msm8998_tdm_be_ops = { + .hw_params = msm8998_tdm_snd_hw_params, + .startup = msm8998_tdm_snd_startup, + .shutdown = msm8998_tdm_snd_shutdown +}; + +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; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 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_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; + } + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE); + if (ret_pinctrl) { + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + } + + /* + * 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 (ret < 0) { + 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 (ret < 0) { + pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + } +clk_off: + if (ret < 0) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (ret < 0) + 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; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + 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); + } + mutex_unlock(&mi2s_intf_conf[index].lock); + + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } +} + +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, + .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, + .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", + .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, + .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, + .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 */ + .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, + .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", + .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 */ + .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 */ + .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 */ + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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, + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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", + .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, + .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", + .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_misc_fe_dai_links[] = { + { + .name = MSM_DAILINK_NAME(ASM Loopback), + .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, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + { + .name = "USB Audio Hostless", + .stream_name = "USB Audio Hostless", + .cpu_dai_name = "USBAUDIO_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, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = MSM_DAILINK_NAME(Transcode Loopback Playback), + .stream_name = "Transcode Loopback Playback", + .cpu_dai_name = "MultiMedia14", + .platform_name = "msm-transcode-loopback", + .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 */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, + }, + { + .name = MSM_DAILINK_NAME(Transcode Loopback Capture), + .stream_name = "Transcode Loopback Capture", + .cpu_dai_name = "MultiMedia18", + .platform_name = "msm-transcode-loopback", + .dynamic = 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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA18, + }, +}; + +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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &msm8998_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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + }, + /* Slimbus VI Recording */ + { + .name = LPASS_BE_SLIMBUS_TX_VI, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-routing", + .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, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + }, + /* Slimbus VI Recording */ + { + .name = LPASS_BE_SLIMBUS_TX_VI, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-routing", + .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, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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_misc_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_misc_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, + .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, + .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, + .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, len_4; + 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_misc_fe_dai_links); + len_4 = len_3 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_4 + 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_misc_fe_dai_links, + sizeof(msm_common_misc_fe_dai_links)); + memcpy(msm_tasha_dai_links + len_3, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_tasha_dai_links + len_4, + 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_misc_fe_dai_links); + len_4 = len_3 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_4 + 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_misc_fe_dai_links, + sizeof(msm_common_misc_fe_dai_links)); + memcpy(msm_tavil_dai_links + len_3, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_tavil_dai_links + len_4, + 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")) { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "This hardware has 4 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 5 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 6 pole jack"); + } else { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + 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); + + /* Parse pinctrl info from devicetree */ + ret = msm_get_pinctrl(pdev); + if (!ret) { + pr_debug("%s: pinctrl parsing successful\n", __func__); + } else { + dev_dbg(&pdev->dev, + "%s: Parsing pinctrl failed with %d. Cannot use Ports\n", + __func__, ret); + ret = 0; + } + + 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; + } + msm_release_pinctrl(pdev); + 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 (gpio_is_valid(pdata->us_euro_gpio)) + gpio_free(pdata->us_euro_gpio); + i2s_auxpcm_deinit(); + + snd_soc_unregister_card(card); + audio_notifier_deregister("msm8998"); + 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/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile new file mode 100644 index 000000000000..da2b2c783a83 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/Makefile @@ -0,0 +1,19 @@ +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-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 msm-transcode-loopback-q6-v2.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_DOLBY_DAP) += msm-dolby-dap-config.o +obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o +obj-$(CONFIG_DOLBY_LICENSE) += 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 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..7e69a7fe28f5 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/audio_cal_utils.c @@ -0,0 +1,1030 @@ +/* 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 AFE_SIDETONE_IIR_CAL_TYPE: + size = sizeof(struct audio_cal_info_sidetone_iir); + 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 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 AFE_SIDETONE_IIR_CAL_TYPE: + size = sizeof(struct audio_cal_type_sidetone_iir); + 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 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; +} + +/** + * cal_utils_create_cal_types + * + * @num_cal_types: number of types + * @cal_type: pointer to the cal types pointer + * @info: pointer to info + * + * Returns 0 on success, EINVAL otherwise + */ +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; +} +EXPORT_SYMBOL(cal_utils_create_cal_types); + +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; +} + +/** + * cal_utils_get_only_cal_block + * + * @cal_type: pointer to the cal type + * + * Returns cal_block structure + */ +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; +} +EXPORT_SYMBOL(cal_utils_get_only_cal_block); + +/** + * cal_utils_get_only_cal_block + * + * @cal_block: pointer to cal block struct + * @user_data: pointer to user data + * + * Returns true on match + */ +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; +} +EXPORT_SYMBOL(cal_utils_match_buf_num); + +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); + + 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; + list_add_tail(&cal_block->list, &cal_type->cal_blocks); + 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_info); + cal_block->cal_info = NULL; + kfree(cal_block->client_info); + cal_block->client_info = NULL; + 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; +} + +/** + * cal_utils_alloc_cal + * + * @data_size: size of data to allocate + * @data: data pointer + * @cal_type: pointer to the cal type + * @client_info_size: client info size + * @client_info: pointer to client info + * + * Returns 0 on success, appropriate error code otherwise + */ +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; +} +EXPORT_SYMBOL(cal_utils_alloc_cal); + +/** + * cal_utils_dealloc_cal + * + * @data_size: size of data to allocate + * @data: data pointer + * @cal_type: pointer to the cal type + * + * Returns 0 on success, appropriate error code otherwise + */ +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; +} +EXPORT_SYMBOL(cal_utils_dealloc_cal); + +/** + * cal_utils_set_cal + * + * @data_size: size of data to allocate + * @data: data pointer + * @cal_type: pointer to the cal type + * @client_info_size: client info size + * @client_info: pointer to client info + * + * Returns 0 on success, appropriate error code otherwise + */ +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; +} +EXPORT_SYMBOL(cal_utils_set_cal); 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..9f082226ca35 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c @@ -0,0 +1,1378 @@ +/* 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 + +#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: + 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 && (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 && (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 && (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 && (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-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c new file mode 100644 index 000000000000..c88526564d79 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c @@ -0,0 +1,4541 @@ +/* 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 +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include "msm-qti-pp-config.h" + +#define DSP_PP_BUFFERING_IN_MSEC 25 +#define PARTIAL_DRAIN_ACK_EARLY_BY_MSEC 150 +#define MP3_OUTPUT_FRAME_SZ 1152 +#define AAC_OUTPUT_FRAME_SZ 1024 +#define AC3_OUTPUT_FRAME_SZ 1536 +#define EAC3_OUTPUT_FRAME_SZ 1536 +#define DSP_NUM_OUTPUT_FRAME_BUFFERED 2 +#define FLAC_BLK_SIZE_LIMIT 65535 + +/* Timestamp mode payload offsets */ +#define CAPTURE_META_DATA_TS_OFFSET_LSW 6 +#define CAPTURE_META_DATA_TS_OFFSET_MSW 7 + +/* decoder parameter length */ +#define DDP_DEC_MAX_NUM_PARAM 18 + +/* Default values used if user space does not set */ +#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) +#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) +#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) +#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) + +#define COMPRESSED_LR_VOL_MAX_STEPS 0x2000 +const DECLARE_TLV_DB_LINEAR(msm_compr_vol_gain, 0, + COMPRESSED_LR_VOL_MAX_STEPS); + +/* Stream id switches between 1 and 2 */ +#define NEXT_STREAM_ID(stream_id) ((stream_id & 1) + 1) + +#define STREAM_ARRAY_INDEX(stream_id) (stream_id - 1) + +#define MAX_NUMBER_OF_STREAMS 2 + +struct msm_compr_gapless_state { + bool set_next_stream_id; + int32_t stream_opened[MAX_NUMBER_OF_STREAMS]; + uint32_t initial_samples_drop; + uint32_t trailing_samples_drop; + uint32_t gapless_transition; + bool use_dsp_gapless_mode; + union snd_codec_options codec_options; +}; + +static unsigned int supported_sample_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000, + 88200, 96000, 128000, 144000, 176400, 192000, 352800, 384000, 2822400, + 5644800 +}; + +struct msm_compr_pdata { + struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX]; + 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]; +}; + +struct msm_compr_audio { + struct snd_compr_stream *cstream; + struct snd_compr_caps compr_cap; + struct snd_compr_codec_caps codec_caps; + struct snd_compr_params codec_param; + struct audio_client *audio_client; + + uint32_t codec; + uint32_t compr_passthr; + void *buffer; /* virtual address */ + phys_addr_t buffer_paddr; /* physical address */ + uint32_t app_pointer; + uint32_t buffer_size; + uint32_t byte_offset; + uint64_t copied_total; /* bytes consumed by DSP */ + 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; + + uint16_t session_id; + + uint32_t sample_rate; + uint32_t num_channels; + + /* + * convention - commands coming from the same thread + * can use the common cmd_ack var. Others (e.g drain/EOS) + * must use separate vars to track command status. + */ + uint32_t cmd_ack; + uint32_t cmd_interrupt; + uint32_t drain_ready; + uint32_t eos_ack; + + uint32_t stream_available; + uint32_t next_stream; + + uint32_t run_mode; + uint32_t start_delay_lsw; + uint32_t start_delay_msw; + + uint64_t marker_timestamp; + + struct msm_compr_gapless_state gapless_state; + + atomic_t start; + atomic_t eos; + atomic_t drain; + atomic_t xrun; + atomic_t close; + atomic_t wait_on_close; + atomic_t error; + + wait_queue_head_t eos_wait; + wait_queue_head_t drain_wait; + wait_queue_head_t close_wait; + wait_queue_head_t wait_for_stream_avail; + + spinlock_t lock; +}; + +const u32 compr_codecs[] = { + SND_AUDIOCODEC_AC3, SND_AUDIOCODEC_EAC3, SND_AUDIOCODEC_DTS, + SND_AUDIOCODEC_DSD, SND_AUDIOCODEC_TRUEHD, SND_AUDIOCODEC_IEC61937}; + +struct query_audio_effect { + uint32_t mod_id; + uint32_t parm_id; + uint32_t size; + uint32_t offset; + uint32_t device; +}; + +struct msm_compr_audio_effects { + 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 volume; + struct query_audio_effect query; +}; + +struct msm_compr_dec_params { + struct snd_dec_ddp ddp_params; +}; + +struct msm_compr_ch_map { + bool set_ch_map; + char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL]; +}; + +static int msm_compr_send_dec_params(struct snd_compr_stream *cstream, + struct msm_compr_dec_params *dec_params, + int stream_id); + +static int msm_compr_set_render_mode(struct msm_compr_audio *prtd, + uint32_t render_mode) { + int ret = -EINVAL; + struct audio_client *ac = prtd->audio_client; + + pr_debug("%s, got render mode %u\n", __func__, render_mode); + + if (render_mode == SNDRV_COMPRESS_RENDER_MODE_AUDIO_MASTER) { + render_mode = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT; + } else if (render_mode == SNDRV_COMPRESS_RENDER_MODE_STC_MASTER) { + render_mode = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC; + prtd->run_mode = ASM_SESSION_CMD_RUN_STARTIME_RUN_WITH_DELAY; + } else { + pr_err("%s, Invalid render mode %u\n", __func__, + render_mode); + ret = -EINVAL; + goto exit; + } + + ret = q6asm_send_mtmx_strtr_render_mode(ac, render_mode); + if (ret) { + pr_err("%s, Render mode can't be set error %d\n", __func__, + ret); + } +exit: + return ret; +} + +static int msm_compr_set_clk_rec_mode(struct audio_client *ac, + uint32_t clk_rec_mode) { + int ret = -EINVAL; + + pr_debug("%s, got clk rec mode %u\n", __func__, clk_rec_mode); + + if (clk_rec_mode == SNDRV_COMPRESS_CLK_REC_MODE_NONE) { + clk_rec_mode = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_NONE; + } else if (clk_rec_mode == SNDRV_COMPRESS_CLK_REC_MODE_AUTO) { + clk_rec_mode = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_AUTO; + } else { + pr_err("%s, Invalid clk rec_mode mode %u\n", __func__, + clk_rec_mode); + ret = -EINVAL; + goto exit; + } + + ret = q6asm_send_mtmx_strtr_clk_rec_mode(ac, clk_rec_mode); + if (ret) { + pr_err("%s, clk rec mode can't be set, error %d\n", __func__, + ret); + } + +exit: + return ret; +} + +static int msm_compr_set_render_window(struct audio_client *ac, + uint32_t ws_lsw, uint32_t ws_msw, + uint32_t we_lsw, uint32_t we_msw) +{ + int ret = -EINVAL; + struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window; + uint32_t param_id; + + pr_debug("%s, ws_lsw 0x%x ws_msw 0x%x we_lsw 0x%x we_ms 0x%x\n", + __func__, ws_lsw, ws_msw, we_lsw, we_msw); + + memset(&asm_mtmx_strtr_window, 0, + sizeof(struct asm_session_mtmx_strtr_param_window_v2_t)); + asm_mtmx_strtr_window.window_lsw = ws_lsw; + asm_mtmx_strtr_window.window_msw = ws_msw; + param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2; + ret = q6asm_send_mtmx_strtr_window(ac, &asm_mtmx_strtr_window, + param_id); + if (ret) { + pr_err("%s, start window can't be set error %d\n", __func__, + ret); + goto exit; + } + + asm_mtmx_strtr_window.window_lsw = we_lsw; + asm_mtmx_strtr_window.window_msw = we_msw; + param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2; + ret = q6asm_send_mtmx_strtr_window(ac, &asm_mtmx_strtr_window, + param_id); + if (ret) { + pr_err("%s, end window can't be set error %d\n", __func__, + ret); + } + +exit: + return ret; +} + +static int msm_compr_enable_adjust_session_clock(struct audio_client *ac, + bool enable) +{ + int ret; + + pr_debug("%s, enable adjust_session %d\n", __func__, enable); + + ret = q6asm_send_mtmx_strtr_enable_adjust_session_clock(ac, enable); + if (ret) + pr_err("%s, adjust session clock can't be set error %d\n", + __func__, ret); + + return ret; +} + +static int msm_compr_adjust_session_clock(struct audio_client *ac, + uint32_t adjust_session_lsw, uint32_t adjust_session_msw) +{ + int ret; + + pr_debug("%s, adjust_session_time_msw 0x%x adjust_session_time_lsw 0x%x\n", + __func__, adjust_session_msw, adjust_session_lsw); + + ret = q6asm_adjust_session_clock(ac, + adjust_session_lsw, + adjust_session_msw); + if (ret) + pr_err("%s, adjust session clock can't be set error %d\n", + __func__, ret); + + return ret; +} + +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; + uint32_t avg_vol, gain_list[VOLUME_CONTROL_MAX_CHANNELS]; + uint32_t num_channels; + struct snd_soc_pcm_runtime *rtd; + struct msm_compr_pdata *pdata; + bool use_default = true; + u8 *chmap = NULL; + + pr_debug("%s: volume_l %d volume_r %d\n", + __func__, volume_l, volume_r); + if (!cstream || !cstream->runtime) { + pr_err("%s: session not active\n", __func__); + return -EPERM; + } + rtd = cstream->private_data; + prtd = cstream->runtime->private_data; + + if (!rtd || !rtd->platform || !prtd || !prtd->audio_client) { + pr_err("%s: invalid rtd, prtd or audio client", __func__); + return rc; + } + pdata = snd_soc_platform_get_drvdata(rtd->platform); + + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No volume config for passthrough %d\n", + __func__, prtd->compr_passthr); + return rc; + } + + use_default = !(pdata->ch_map[rtd->dai_link->id]->set_ch_map); + chmap = pdata->ch_map[rtd->dai_link->id]->channel_map; + num_channels = prtd->num_channels; + + if (prtd->num_channels > 2) { + /* + * Currently the left and right gains are averaged an applied + * to all channels. This might not be desirable. But currently, + * there exists no API in userspace to send a list of gains for + * each channel either. If such an API does become available, + * the mixer control must be updated to accept more than 2 + * channel gains. + * + */ + avg_vol = (volume_l + volume_r) / 2; + rc = q6asm_set_volume(prtd->audio_client, avg_vol); + } else { + gain_list[0] = volume_l; + gain_list[1] = volume_r; + /* force sending FR/FL/FC volume for mono */ + if (prtd->num_channels == 1) { + gain_list[2] = volume_l; + num_channels = 3; + use_default = true; + } + 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); + + return rc; +} + +static int msm_compr_send_ddp_cfg(struct audio_client *ac, + struct snd_dec_ddp *ddp, + int stream_id) +{ + int i, rc; + + pr_debug("%s\n", __func__); + for (i = 0; i < ddp->params_length; i++) { + rc = q6asm_ds1_set_stream_endp_params(ac, ddp->params_id[i], + ddp->params_value[i], + stream_id); + if (rc) { + pr_err("sending params_id: %d failed\n", + ddp->params_id[i]); + return rc; + } + } + return 0; +} + +static int msm_compr_send_buffer(struct msm_compr_audio *prtd) +{ + int buffer_length; + uint64_t bytes_available; + struct audio_aio_write_param param; + struct snd_codec_metadata *buff_addr; + + if (!atomic_read(&prtd->start)) { + pr_err("%s: stream is not in started state\n", __func__); + return -EINVAL; + } + + + if (atomic_read(&prtd->xrun)) { + WARN(1, "%s called while xrun is true", __func__); + return -EPERM; + } + + pr_debug("%s: bytes_received = %llu copied_total = %llu\n", + __func__, prtd->bytes_received, prtd->copied_total); + if (prtd->first_buffer && prtd->gapless_state.use_dsp_gapless_mode && + prtd->compr_passthr == LEGACY_PCM) + q6asm_stream_send_meta_data(prtd->audio_client, + prtd->audio_client->stream_id, + prtd->gapless_state.initial_samples_drop, + prtd->gapless_state.trailing_samples_drop); + + buffer_length = prtd->codec_param.buffer.fragment_size; + bytes_available = prtd->bytes_received - prtd->copied_total; + if (bytes_available < prtd->codec_param.buffer.fragment_size) + buffer_length = bytes_available; + + if (prtd->byte_offset + buffer_length > prtd->buffer_size) { + buffer_length = (prtd->buffer_size - prtd->byte_offset); + pr_debug("%s: wrap around situation, send partial data %d now", + __func__, buffer_length); + } + + if (buffer_length) { + param.paddr = prtd->buffer_paddr + prtd->byte_offset; + WARN(prtd->byte_offset % 32 != 0, "offset %x not multiple of 32\n", + prtd->byte_offset); + } else { + param.paddr = prtd->buffer_paddr; + } + param.len = buffer_length; + if (prtd->ts_header_offset) { + buff_addr = (struct snd_codec_metadata *) + (prtd->buffer + prtd->byte_offset); + param.len = buff_addr->length; + param.msw_ts = (uint32_t) + ((buff_addr->timestamp & 0xFFFFFFFF00000000LL) >> 32); + param.lsw_ts = (uint32_t) (buff_addr->timestamp & 0xFFFFFFFFLL); + param.paddr += prtd->ts_header_offset; + param.flags = SET_TIMESTAMP; + param.metadata_len = prtd->ts_header_offset; + } else { + param.msw_ts = 0; + param.lsw_ts = 0; + param.flags = NO_TIMESTAMP; + param.metadata_len = 0; + } + param.uid = buffer_length; + param.last_buffer = prtd->last_buffer; + + pr_debug("%s: sending %d bytes to DSP byte_offset = %d\n", + __func__, param.len, prtd->byte_offset); + if (q6asm_async_write(prtd->audio_client, ¶m) < 0) { + pr_err("%s:q6asm_async_write failed\n", __func__); + } else { + prtd->bytes_sent += buffer_length; + if (prtd->first_buffer) + prtd->first_buffer = 0; + } + + 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) +{ + struct msm_compr_audio *prtd = priv; + struct snd_compr_stream *cstream; + struct audio_client *ac; + uint32_t chan_mode = 0; + uint32_t sample_rate = 0; + uint64_t bytes_available; + int stream_id; + uint32_t stream_index; + unsigned long flags; + uint64_t read_size; + uint32_t *buff_addr; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + + if (!prtd) { + pr_err("%s: prtd is NULL\n", __func__); + return; + } + cstream = prtd->cstream; + if (!cstream) { + pr_err("%s: cstream is NULL\n", __func__); + return; + } + + 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: + spin_lock_irqsave(&prtd->lock, flags); + + if (payload[3]) { + pr_err("%s: WRITE FAILED w/ err 0x%x !, paddr 0x%x, byte_offset=%d,copied_total=%llu,token=%d\n", + __func__, + payload[3], + payload[0], + prtd->byte_offset, + prtd->copied_total, token); + + if (atomic_cmpxchg(&prtd->drain, 1, 0) && + prtd->last_buffer) { + pr_debug("%s: wake up on drain\n", __func__); + prtd->drain_ready = 1; + wake_up(&prtd->drain_wait); + prtd->last_buffer = 0; + } else { + atomic_set(&prtd->start, 0); + } + } else { + pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2 offset %d, length %d\n", + 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. + */ + if (prtd->ts_header_offset) { + /* Always assume that the data will be sent to DSP on + * frame boundary. + * i.e, one frame of userspace write will result in + * one kernel write to DSP. This is needed as + * timestamp will be sent per frame. + */ + prtd->byte_offset += + prtd->codec_param.buffer.fragment_size; + prtd->copied_total += + prtd->codec_param.buffer.fragment_size; + } else { + prtd->byte_offset += token; + prtd->copied_total += token; + } + if (prtd->byte_offset >= prtd->buffer_size) + prtd->byte_offset -= prtd->buffer_size; + + snd_compr_fragment_elapsed(cstream); + + if (!atomic_read(&prtd->start)) { + /* Writes must be restarted from _copy() */ + pr_debug("write_done received while not started, treat as xrun"); + atomic_set(&prtd->xrun, 1); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + bytes_available = prtd->bytes_received - prtd->copied_total; + if (bytes_available < cstream->runtime->fragment_size) { + pr_debug("WRITE_DONE Insufficient data to send. break out\n"); + atomic_set(&prtd->xrun, 1); + + if (prtd->last_buffer) + prtd->last_buffer = 0; + if (atomic_read(&prtd->drain)) { + pr_debug("wake up on drain\n"); + prtd->drain_ready = 1; + wake_up(&prtd->drain_wait); + atomic_set(&prtd->drain, 0); + } + } else if ((bytes_available == cstream->runtime->fragment_size) + && atomic_read(&prtd->drain)) { + prtd->last_buffer = 1; + msm_compr_send_buffer(prtd); + prtd->last_buffer = 0; + } else + msm_compr_send_buffer(prtd); + + 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[CAPTURE_META_DATA_TS_OFFSET_LSW]; + buff_addr++; + /* Write the TS MSW */ + *buff_addr = payload[CAPTURE_META_DATA_TS_OFFSET_MSW]; + } + /* 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); + if (atomic_read(&prtd->eos) && + !prtd->gapless_state.set_next_stream_id) { + pr_debug("ASM_DATA_CMDRSP_EOS wake up\n"); + prtd->eos_ack = 1; + wake_up(&prtd->eos_wait); + } + atomic_set(&prtd->eos, 0); + stream_index = STREAM_ARRAY_INDEX(stream_id); + if (stream_index >= MAX_NUMBER_OF_STREAMS || + stream_index < 0) { + pr_err("%s: Invalid stream index %d", __func__, + stream_index); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + if (prtd->gapless_state.set_next_stream_id && + prtd->gapless_state.stream_opened[stream_index]) { + pr_debug("%s: CMD_CLOSE stream_id %d\n", + __func__, stream_id); + q6asm_stream_cmd_nowait(ac, CMD_CLOSE, stream_id); + atomic_set(&prtd->close, 1); + prtd->gapless_state.stream_opened[stream_index] = 0; + prtd->gapless_state.set_next_stream_id = false; + } + if (prtd->gapless_state.gapless_transition) + prtd->gapless_state.gapless_transition = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + case ASM_STREAM_PP_EVENT: + case ASM_STREAM_CMD_ENCDEC_EVENTS: + pr_debug("%s: ASM_STREAM_EVENT(0x%x)\n", __func__, opcode); + rtd = cstream->private_data; + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + return; + } + + ret = msm_adsp_inform_mixer_ctl(rtd, payload); + if (ret) { + pr_err("%s: failed to inform mixer ctrl. err = %d\n", + __func__, ret); + return; + } + break; + case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY: + case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY: { + pr_debug("ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY\n"); + chan_mode = payload[1] >> 16; + sample_rate = payload[2] >> 16; + if (prtd && (chan_mode != prtd->num_channels || + sample_rate != prtd->sample_rate)) { + prtd->num_channels = chan_mode; + prtd->sample_rate = sample_rate; + } + } + /* Fallthrough here */ + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case ASM_SESSION_CMD_RUN_V2: + /* check if the first buffer need to be sent to DSP */ + pr_debug("ASM_SESSION_CMD_RUN_V2\n"); + + /* 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; + if (bytes_available < + cstream->runtime->fragment_size) { + pr_debug("CMD_RUN_V2 Insufficient data to send. break out\n"); + atomic_set(&prtd->xrun, 1); + } else { + msm_compr_send_buffer(prtd); + } + } + + /* + * The condition below ensures playback finishes in the + * follow cornercase + * WRITE(last buffer) + * WAIT_FOR_DRAIN + * PAUSE + * WRITE_DONE(X) + * RESUME + */ + if ((prtd->copied_total == prtd->bytes_sent) && + atomic_read(&prtd->drain)) { + pr_debug("RUN ack, wake up & continue pending drain\n"); + + if (prtd->last_buffer) + prtd->last_buffer = 0; + + prtd->drain_ready = 1; + wake_up(&prtd->drain_wait); + atomic_set(&prtd->drain, 0); + } + + spin_unlock_irqrestore(&prtd->lock, flags); + break; + 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); + 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); + 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); + 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); + /* + * wakeup wait for stream avail on stream 3 + * after stream 1 ends. + */ + if (prtd->next_stream) { + pr_debug("%s:CLOSE:wakeup wait for stream\n", + __func__); + prtd->stream_available = 1; + wake_up(&prtd->wait_for_stream_avail); + prtd->next_stream = 0; + } + if (atomic_read(&prtd->close) && + atomic_read(&prtd->wait_on_close)) { + prtd->cmd_ack = 1; + wake_up(&prtd->close_wait); + } + atomic_set(&prtd->close, 0); + break; + case ASM_STREAM_CMD_REGISTER_PP_EVENTS: + pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS:", + __func__); + break; + default: + break; + } + break; + } + case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3: + pr_debug("%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3\n", + __func__); + break; + case RESET_EVENTS: + pr_err("%s: Received reset events CB, move to error state", + __func__); + spin_lock_irqsave(&prtd->lock, flags); + /* + * 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)) { + pr_debug("%s:unblock eos wait queues", __func__); + wake_up(&prtd->eos_wait); + } + spin_unlock_irqrestore(&prtd->lock, flags); + break; + default: + pr_debug("%s: Not Supported Event opcode[0x%x]\n", + __func__, opcode); + break; + } +} + +static int msm_compr_get_partial_drain_delay(int frame_sz, int sample_rate) +{ + int delay_time_ms = 0; + + delay_time_ms = ((DSP_NUM_OUTPUT_FRAME_BUFFERED * frame_sz * 1000) / + sample_rate) + DSP_PP_BUFFERING_IN_MSEC; + 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: frame_sz %d, sample_rate %d, partial drain delay %d\n", + __func__, frame_sz, sample_rate, delay_time_ms); + return delay_time_ms; +} + +static void populate_codec_list(struct msm_compr_audio *prtd) +{ + pr_debug("%s\n", __func__); + prtd->compr_cap.direction = SND_COMPRESS_PLAYBACK; + prtd->compr_cap.min_fragment_size = + COMPR_PLAYBACK_MIN_FRAGMENT_SIZE; + prtd->compr_cap.max_fragment_size = + COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; + prtd->compr_cap.min_fragments = + COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; + prtd->compr_cap.max_fragments = + COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; + prtd->compr_cap.num_codecs = 17; + prtd->compr_cap.codecs[0] = SND_AUDIOCODEC_MP3; + prtd->compr_cap.codecs[1] = SND_AUDIOCODEC_AAC; + prtd->compr_cap.codecs[2] = SND_AUDIOCODEC_AC3; + prtd->compr_cap.codecs[3] = SND_AUDIOCODEC_EAC3; + prtd->compr_cap.codecs[4] = SND_AUDIOCODEC_MP2; + prtd->compr_cap.codecs[5] = SND_AUDIOCODEC_PCM; + prtd->compr_cap.codecs[6] = SND_AUDIOCODEC_WMA; + prtd->compr_cap.codecs[7] = SND_AUDIOCODEC_WMA_PRO; + prtd->compr_cap.codecs[8] = SND_AUDIOCODEC_FLAC; + 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; + prtd->compr_cap.codecs[14] = SND_AUDIOCODEC_APTX; + prtd->compr_cap.codecs[15] = SND_AUDIOCODEC_TRUEHD; + prtd->compr_cap.codecs[16] = SND_AUDIOCODEC_IEC61937; +} + +static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream, + int stream_id, + bool use_gapless_codec_options) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct msm_compr_pdata *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + struct asm_aac_cfg aac_cfg; + struct asm_wma_cfg wma_cfg; + struct asm_wmapro_cfg wma_pro_cfg; + struct asm_flac_cfg flac_cfg; + struct asm_vorbis_cfg vorbis_cfg; + struct asm_alac_cfg alac_cfg; + struct asm_ape_cfg ape_cfg; + struct asm_dsd_cfg dsd_cfg; + struct aptx_dec_bt_addr_cfg aptx_cfg; + union snd_codec_options *codec_options; + + int ret = 0; + 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); + + if (use_gapless_codec_options) + codec_options = &(prtd->gapless_state.codec_options); + else + codec_options = &(prtd->codec_param.codec.options); + + if (!codec_options) { + pr_err("%s: codec_options is NULL\n", __func__); + return -EINVAL; + } + + switch (prtd->codec) { + case FORMAT_LINEAR_PCM: + pr_debug("SND_AUDIOCODEC_PCM\n"); + if (pdata->ch_map[rtd->dai_link->id]) { + use_default_chmap = + !(pdata->ch_map[rtd->dai_link->id]->set_ch_map); + chmap = + pdata->ch_map[rtd->dai_link->id]->channel_map; + } + + 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; + 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, + sample_word_size, + ASM_LITTLE_ENDIAN, + DEFAULT_QF); + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + + break; + case FORMAT_MP3: + pr_debug("SND_AUDIOCODEC_MP3\n"); + /* no media format block needed */ + break; + case FORMAT_MPEG4_AAC: + pr_debug("SND_AUDIOCODEC_AAC\n"); + memset(&aac_cfg, 0x0, sizeof(struct asm_aac_cfg)); + aac_cfg.aot = AAC_ENC_MODE_EAAC_P; + if (prtd->codec_param.codec.format == + SND_AUDIOSTREAMFORMAT_MP4ADTS) + aac_cfg.format = 0x0; + else if (prtd->codec_param.codec.format == + SND_AUDIOSTREAMFORMAT_MP4LATM) + aac_cfg.format = 0x04; + else + aac_cfg.format = 0x03; + aac_cfg.ch_cfg = prtd->num_channels; + aac_cfg.sample_rate = prtd->sample_rate; + ret = q6asm_stream_media_format_block_aac(prtd->audio_client, + &aac_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + break; + case FORMAT_AC3: + pr_debug("SND_AUDIOCODEC_AC3\n"); + break; + case FORMAT_EAC3: + pr_debug("SND_AUDIOCODEC_EAC3\n"); + break; + case FORMAT_WMA_V9: + pr_debug("SND_AUDIOCODEC_WMA\n"); + memset(&wma_cfg, 0x0, sizeof(struct asm_wma_cfg)); + wma_cfg.format_tag = prtd->codec_param.codec.format; + wma_cfg.ch_cfg = prtd->codec_param.codec.ch_in; + wma_cfg.sample_rate = prtd->sample_rate; + wma_cfg.avg_bytes_per_sec = codec_options->wma.avg_bit_rate/8; + wma_cfg.block_align = codec_options->wma.super_block_align; + wma_cfg.valid_bits_per_sample = + codec_options->wma.bits_per_sample; + wma_cfg.ch_mask = codec_options->wma.channelmask; + wma_cfg.encode_opt = codec_options->wma.encodeopt; + ret = q6asm_media_format_block_wma(prtd->audio_client, + &wma_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + break; + case FORMAT_WMA_V10PRO: + pr_debug("SND_AUDIOCODEC_WMA_PRO\n"); + memset(&wma_pro_cfg, 0x0, sizeof(struct asm_wmapro_cfg)); + wma_pro_cfg.format_tag = prtd->codec_param.codec.format; + wma_pro_cfg.ch_cfg = prtd->codec_param.codec.ch_in; + wma_pro_cfg.sample_rate = prtd->sample_rate; + wma_cfg.avg_bytes_per_sec = codec_options->wma.avg_bit_rate/8; + wma_pro_cfg.block_align = codec_options->wma.super_block_align; + wma_pro_cfg.valid_bits_per_sample = + codec_options->wma.bits_per_sample; + wma_pro_cfg.ch_mask = codec_options->wma.channelmask; + wma_pro_cfg.encode_opt = codec_options->wma.encodeopt; + wma_pro_cfg.adv_encode_opt = codec_options->wma.encodeopt1; + wma_pro_cfg.adv_encode_opt2 = codec_options->wma.encodeopt2; + ret = q6asm_media_format_block_wmapro(prtd->audio_client, + &wma_pro_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed\n", __func__); + break; + case FORMAT_MP2: + pr_debug("%s: SND_AUDIOCODEC_MP2\n", __func__); + break; + case FORMAT_FLAC: + pr_debug("%s: SND_AUDIOCODEC_FLAC\n", __func__); + memset(&flac_cfg, 0x0, sizeof(struct asm_flac_cfg)); + flac_cfg.ch_cfg = prtd->num_channels; + flac_cfg.sample_rate = prtd->sample_rate; + flac_cfg.stream_info_present = 1; + flac_cfg.sample_size = codec_options->flac_dec.sample_size; + flac_cfg.min_blk_size = codec_options->flac_dec.min_blk_size; + flac_cfg.max_blk_size = codec_options->flac_dec.max_blk_size; + flac_cfg.max_frame_size = + codec_options->flac_dec.max_frame_size; + flac_cfg.min_frame_size = + codec_options->flac_dec.min_frame_size; + + ret = q6asm_stream_media_format_block_flac(prtd->audio_client, + &flac_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + + break; + case FORMAT_VORBIS: + pr_debug("%s: SND_AUDIOCODEC_VORBIS\n", __func__); + memset(&vorbis_cfg, 0x0, sizeof(struct asm_vorbis_cfg)); + vorbis_cfg.bit_stream_fmt = + codec_options->vorbis_dec.bit_stream_fmt; + + ret = q6asm_stream_media_format_block_vorbis( + prtd->audio_client, &vorbis_cfg, + stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + + break; + case FORMAT_ALAC: + pr_debug("%s: SND_AUDIOCODEC_ALAC\n", __func__); + memset(&alac_cfg, 0x0, sizeof(struct asm_alac_cfg)); + alac_cfg.num_channels = prtd->num_channels; + alac_cfg.sample_rate = prtd->sample_rate; + alac_cfg.frame_length = codec_options->alac.frame_length; + alac_cfg.compatible_version = + codec_options->alac.compatible_version; + alac_cfg.bit_depth = codec_options->alac.bit_depth; + alac_cfg.pb = codec_options->alac.pb; + alac_cfg.mb = codec_options->alac.mb; + alac_cfg.kb = codec_options->alac.kb; + alac_cfg.max_run = codec_options->alac.max_run; + alac_cfg.max_frame_bytes = codec_options->alac.max_frame_bytes; + alac_cfg.avg_bit_rate = codec_options->alac.avg_bit_rate; + alac_cfg.channel_layout_tag = + codec_options->alac.channel_layout_tag; + + ret = q6asm_media_format_block_alac(prtd->audio_client, + &alac_cfg, stream_id); + if (ret < 0) + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + break; + case FORMAT_APE: + pr_debug("%s: SND_AUDIOCODEC_APE\n", __func__); + memset(&ape_cfg, 0x0, sizeof(struct asm_ape_cfg)); + ape_cfg.num_channels = prtd->num_channels; + ape_cfg.sample_rate = prtd->sample_rate; + ape_cfg.compatible_version = + codec_options->ape.compatible_version; + ape_cfg.compression_level = + codec_options->ape.compression_level; + ape_cfg.format_flags = codec_options->ape.format_flags; + ape_cfg.blocks_per_frame = codec_options->ape.blocks_per_frame; + ape_cfg.final_frame_blocks = + codec_options->ape.final_frame_blocks; + ape_cfg.total_frames = codec_options->ape.total_frames; + ape_cfg.bits_per_sample = codec_options->ape.bits_per_sample; + ape_cfg.seek_table_present = + codec_options->ape.seek_table_present; + + ret = q6asm_media_format_block_ape(prtd->audio_client, + &ape_cfg, stream_id); + + if (ret < 0) + 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; + case FORMAT_TRUEHD: + pr_debug("SND_AUDIOCODEC_TRUEHD\n"); + /* no media format block needed */ + break; + case FORMAT_IEC61937: + pr_debug("SND_AUDIOCODEC_IEC61937\n"); + ret = q6asm_media_format_block_iec(prtd->audio_client, + prtd->sample_rate, + prtd->num_channels); + if (ret < 0) + pr_err("%s: CMD IEC61937 Format block failed ret %d\n", + __func__, ret); + break; + case FORMAT_APTX: + pr_debug("SND_AUDIOCODEC_APTX\n"); + memset(&aptx_cfg, 0x0, sizeof(struct aptx_dec_bt_addr_cfg)); + ret = q6asm_stream_media_format_block_aptx_dec( + prtd->audio_client, + prtd->sample_rate, + stream_id); + if (ret >= 0) { + aptx_cfg.nap = codec_options->aptx_dec.nap; + aptx_cfg.uap = codec_options->aptx_dec.uap; + aptx_cfg.lap = codec_options->aptx_dec.lap; + q6asm_set_aptx_dec_bt_addr(prtd->audio_client, + &aptx_cfg); + } else { + pr_err("%s: CMD Format block failed ret %d\n", + __func__, ret); + } + break; + default: + pr_debug("%s, unsupported format, skip", __func__); + break; + } + return ret; +} + +static int msm_compr_init_pp_params(struct snd_compr_stream *cstream, + 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, + }; + + switch (ac->topology) { + 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; + } + return ret; +} + +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; + struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data; + uint16_t bits_per_sample = 16; + int dir = IN, ret = 0; + struct audio_client *ac = prtd->audio_client; + uint32_t stream_index; + 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: stream_id %d\n", __func__, ac->stream_id); + 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; + + if (prtd->compr_passthr != LEGACY_PCM) { + ret = q6asm_open_write_compressed(ac, prtd->codec, + prtd->compr_passthr); + if (ret < 0) { + pr_err("%s:ASM open write err[%d] for compr_type[%d]\n", + __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->id, + ac->perf_mode, + prtd->session_id, + SNDRV_PCM_STREAM_PLAYBACK, + prtd->compr_passthr); + if (ret) { + pr_err("%s: compr stream reg failed:%d\n", __func__, + ret); + return ret; + } + } else { + pr_debug("%s: stream_id %d bits_per_sample %d\n", + __func__, ac->stream_id, bits_per_sample); + ret = q6asm_stream_open_write_v4(ac, + prtd->codec, bits_per_sample, + ac->stream_id, + prtd->gapless_state.use_dsp_gapless_mode); + if (ret < 0) { + pr_err("%s:ASM open write err[%d] for compr type[%d]\n", + __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->id); + ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->id, + ac->perf_mode, + prtd->session_id, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) { + pr_err("%s: stream reg failed:%d\n", __func__, ret); + return ret; + } + } + + ret = msm_compr_set_volume(cstream, 0, 0); + if (ret < 0) + pr_err("%s : Set Volume 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_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE)); + if (ret < 0) { + pr_err("%s: Set IO mode failed\n", __func__); + return -EINVAL; + } + + 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", + 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->copied_total = 0; + prtd->app_pointer = 0; + prtd->bytes_received = 0; + prtd->bytes_sent = 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; + + ret = msm_compr_send_media_format_block(cstream, ac->stream_id, false); + if (ret < 0) + pr_err("%s, failed to send media format block\n", __func__); + + return ret; +} + +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); + + if (prtd->codec_param.codec.flags & COMPRESSED_TIMESTAMP_FLAG) { + ret = q6asm_open_read_v4(prtd->audio_client, FORMAT_LINEAR_PCM, + bits_per_sample, true); + } else { + ret = q6asm_open_read_v4(prtd->audio_client, FORMAT_LINEAR_PCM, + bits_per_sample, false); + } + 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->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; + 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->id] = cstream; + pdata->audio_effects[rtd->dai_link->id] = + kzalloc(sizeof(struct msm_compr_audio_effects), GFP_KERNEL); + if (!pdata->audio_effects[rtd->dai_link->id]) { + pr_err("%s: Could not allocate memory for effects\n", __func__); + pdata->cstream[rtd->dai_link->id] = NULL; + kfree(prtd); + return -ENOMEM; + } + pdata->dec_params[rtd->dai_link->id] = + kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL); + if (!pdata->dec_params[rtd->dai_link->id]) { + pr_err("%s: Could not allocate memory for dec params\n", + __func__); + kfree(pdata->audio_effects[rtd->dai_link->id]); + pdata->cstream[rtd->dai_link->id] = NULL; + kfree(prtd); + return -ENOMEM; + } + prtd->codec = FORMAT_MP3; + prtd->bytes_received = 0; + prtd->bytes_sent = 0; + prtd->copied_total = 0; + prtd->byte_offset = 0; + prtd->sample_rate = 44100; + prtd->num_channels = 2; + prtd->drain_ready = 0; + prtd->last_buffer = 0; + prtd->first_buffer = 1; + prtd->partial_drain_delay = 0; + prtd->next_stream = 0; + memset(&prtd->gapless_state, 0, sizeof(struct msm_compr_gapless_state)); + /* + * Update the use_dsp_gapless_mode from gapless struture with the value + * part of platform data. + */ + prtd->gapless_state.use_dsp_gapless_mode = pdata->use_dsp_gapless_mode; + + pr_debug("%s: gapless mode %d", __func__, pdata->use_dsp_gapless_mode); + + 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); + + init_waitqueue_head(&prtd->eos_wait); + init_waitqueue_head(&prtd->drain_wait); + init_waitqueue_head(&prtd->close_wait); + init_waitqueue_head(&prtd->wait_for_stream_avail); + + 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->id]); + kfree(pdata->dec_params[rtd->dai_link->id]); + pdata->cstream[rtd->dai_link->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; + msm_adsp_init_mixer_ctl_pp_event_queue(rtd); + + return 0; +} + +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->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->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; + struct snd_soc_pcm_runtime *soc_prtd; + struct msm_compr_pdata *pdata; + struct audio_client *ac; + int dir = IN, ret = 0, stream_id; + unsigned long flags; + uint32_t stream_index; + + pr_debug("%s\n", __func__); + + 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; + } + prtd->cmd_interrupt = 1; + wake_up(&prtd->drain_wait); + 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; + } + if (atomic_read(&prtd->eos)) { + ret = wait_event_timeout(prtd->eos_wait, + prtd->eos_ack, 5 * HZ); + if (!ret) + pr_err("%s: CMD_EOS failed\n", __func__); + } + if (atomic_read(&prtd->close)) { + prtd->cmd_ack = 0; + atomic_set(&prtd->wait_on_close, 1); + ret = wait_event_timeout(prtd->close_wait, + prtd->cmd_ack, 5 * HZ); + if (!ret) + pr_err("%s: CMD_CLOSE failed\n", __func__); + } + + spin_lock_irqsave(&prtd->lock, flags); + stream_id = ac->stream_id; + stream_index = STREAM_ARRAY_INDEX(NEXT_STREAM_ID(stream_id)); + + if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) && + (prtd->gapless_state.stream_opened[stream_index])) { + prtd->gapless_state.stream_opened[stream_index] = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + pr_debug(" close stream %d", NEXT_STREAM_ID(stream_id)); + q6asm_stream_cmd(ac, CMD_CLOSE, NEXT_STREAM_ID(stream_id)); + spin_lock_irqsave(&prtd->lock, flags); + } + + stream_index = STREAM_ARRAY_INDEX(stream_id); + if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0) && + (prtd->gapless_state.stream_opened[stream_index])) { + prtd->gapless_state.stream_opened[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->id] = NULL; + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id, + SNDRV_PCM_STREAM_PLAYBACK); + } + + q6asm_audio_client_buf_free_contiguous(dir, ac); + + q6asm_audio_client_free(ac); + msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd); + kfree(pdata->audio_effects[soc_prtd->dai_link->id]); + pdata->audio_effects[soc_prtd->dai_link->id] = NULL; + kfree(pdata->dec_params[soc_prtd->dai_link->id]); + pdata->dec_params[soc_prtd->dai_link->id] = NULL; + kfree(prtd); + runtime->private_data = NULL; + + 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->id] = NULL; + msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id, + SNDRV_PCM_STREAM_CAPTURE); + + q6asm_audio_client_buf_free_contiguous(dir, ac); + + q6asm_audio_client_free(ac); + + kfree(prtd); + runtime->private_data = NULL; + + 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; + + for (i = 0; i < ARRAY_SIZE(compr_codecs); i++) { + if (compr_codecs[i] == codec_id) + return true; + } + return false; +} + +/* compress stream operations */ +static int msm_compr_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + int ret = 0, frame_sz = 0; + int i, num_rates; + bool is_format_gapless = false; + + pr_debug("%s\n", __func__); + + num_rates = sizeof(supported_sample_rates)/sizeof(unsigned int); + for (i = 0; i < num_rates; i++) + if (params->codec.sample_rate == supported_sample_rates[i]) + break; + if (i == num_rates) + return -EINVAL; + + 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->codec_param. + codec.compr_passthr == COMPRESSED_PASSTHROUGH_IEC61937)) + prtd->compr_passthr = prtd->codec_param.codec.compr_passthr; + else + prtd->compr_passthr = LEGACY_PCM; + pr_debug("%s: compr_passthr = %d", __func__, prtd->compr_passthr); + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: Reset gapless mode playback for compr_type[%d]\n", + __func__, prtd->compr_passthr); + prtd->gapless_state.use_dsp_gapless_mode = 0; + if (!msm_compr_validate_codec_compr(params->codec.id)) { + pr_err("%s codec not supported in passthrough,id =%d\n", + __func__, params->codec.id); + return -EINVAL; + } + } + + switch (params->codec.id) { + case SND_AUDIOCODEC_PCM: { + pr_debug("SND_AUDIOCODEC_PCM\n"); + prtd->codec = FORMAT_LINEAR_PCM; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_MP3: { + pr_debug("SND_AUDIOCODEC_MP3\n"); + prtd->codec = FORMAT_MP3; + frame_sz = MP3_OUTPUT_FRAME_SZ; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_AAC: { + pr_debug("SND_AUDIOCODEC_AAC\n"); + prtd->codec = FORMAT_MPEG4_AAC; + frame_sz = AAC_OUTPUT_FRAME_SZ; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_AC3: { + pr_debug("SND_AUDIOCODEC_AC3\n"); + prtd->codec = FORMAT_AC3; + frame_sz = AC3_OUTPUT_FRAME_SZ; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_EAC3: { + pr_debug("SND_AUDIOCODEC_EAC3\n"); + prtd->codec = FORMAT_EAC3; + frame_sz = EAC3_OUTPUT_FRAME_SZ; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_MP2: { + pr_debug("SND_AUDIOCODEC_MP2\n"); + prtd->codec = FORMAT_MP2; + break; + } + + case SND_AUDIOCODEC_WMA: { + pr_debug("SND_AUDIOCODEC_WMA\n"); + prtd->codec = FORMAT_WMA_V9; + break; + } + + case SND_AUDIOCODEC_WMA_PRO: { + pr_debug("SND_AUDIOCODEC_WMA_PRO\n"); + prtd->codec = FORMAT_WMA_V10PRO; + break; + } + + case SND_AUDIOCODEC_FLAC: { + pr_debug("%s: SND_AUDIOCODEC_FLAC\n", __func__); + prtd->codec = FORMAT_FLAC; + /* + * DSP bufferring is based on blk size, + * consider mininum buffering to rule out any false wait + */ + frame_sz = + prtd->codec_param.codec.options.flac_dec.min_blk_size; + is_format_gapless = true; + break; + } + + case SND_AUDIOCODEC_VORBIS: { + pr_debug("%s: SND_AUDIOCODEC_VORBIS\n", __func__); + prtd->codec = FORMAT_VORBIS; + break; + } + + case SND_AUDIOCODEC_ALAC: { + pr_debug("%s: SND_AUDIOCODEC_ALAC\n", __func__); + prtd->codec = FORMAT_ALAC; + break; + } + + case SND_AUDIOCODEC_APE: { + pr_debug("%s: SND_AUDIOCODEC_APE\n", __func__); + prtd->codec = FORMAT_APE; + 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; + } + + case SND_AUDIOCODEC_TRUEHD: { + pr_debug("%s: SND_AUDIOCODEC_TRUEHD\n", __func__); + prtd->codec = FORMAT_TRUEHD; + break; + } + + case SND_AUDIOCODEC_IEC61937: { + pr_debug("%s: SND_AUDIOCODEC_IEC61937\n", __func__); + prtd->codec = FORMAT_IEC61937; + break; + } + + case SND_AUDIOCODEC_APTX: { + pr_debug("%s: SND_AUDIOCODEC_APTX\n", __func__); + prtd->codec = FORMAT_APTX; + break; + } + + default: + pr_err("codec not supported, id =%d\n", params->codec.id); + return -EINVAL; + } + + if (!is_format_gapless) + prtd->gapless_state.use_dsp_gapless_mode = false; + + prtd->partial_drain_delay = + msm_compr_get_partial_drain_delay(frame_sz, prtd->sample_rate); + + 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; +} + +static int msm_compr_drain_buffer(struct msm_compr_audio *prtd, + unsigned long *flags) +{ + int rc = 0; + + atomic_set(&prtd->drain, 1); + prtd->drain_ready = 0; + spin_unlock_irqrestore(&prtd->lock, *flags); + pr_debug("%s: wait for buffer to be drained\n", __func__); + rc = wait_event_interruptible(prtd->drain_wait, + prtd->drain_ready || + prtd->cmd_interrupt || + atomic_read(&prtd->xrun) || + atomic_read(&prtd->error)); + pr_debug("%s: out of buffer drain wait with ret %d\n", __func__, rc); + spin_lock_irqsave(&prtd->lock, *flags); + if (prtd->cmd_interrupt) { + pr_debug("%s: buffer drain interrupted by flush)\n", __func__); + rc = -EINTR; + prtd->cmd_interrupt = 0; + } + if (atomic_read(&prtd->error)) { + pr_err("%s: Got RESET EVENTS notification, return\n", + __func__); + rc = -ENETRESET; + } + return rc; +} + +static int msm_compr_wait_for_stream_avail(struct msm_compr_audio *prtd, + unsigned long *flags) +{ + int rc = 0; + + pr_debug("next session is already in opened state\n"); + prtd->next_stream = 1; + prtd->cmd_interrupt = 0; + spin_unlock_irqrestore(&prtd->lock, *flags); + /* + * Wait for stream to be available, or the wait to be interrupted by + * commands like flush or till a timeout of one second. + */ + rc = wait_event_timeout(prtd->wait_for_stream_avail, + prtd->stream_available || prtd->cmd_interrupt, 1 * HZ); + pr_err("%s:prtd->stream_available %d, prtd->cmd_interrupt %d rc %d\n", + __func__, prtd->stream_available, prtd->cmd_interrupt, rc); + + spin_lock_irqsave(&prtd->lock, *flags); + if (rc == 0) { + pr_err("%s: wait_for_stream_avail timed out\n", + __func__); + rc = -ETIMEDOUT; + } else if (prtd->cmd_interrupt == 1) { + /* + * This scenario might not happen as we do not allow + * flush in transition state. + */ + pr_debug("%s: wait_for_stream_avail interrupted\n", __func__); + prtd->cmd_interrupt = 0; + prtd->stream_available = 0; + rc = -EINTR; + } else { + prtd->stream_available = 0; + rc = 0; + } + pr_debug("%s : rc = %d", __func__, rc); + return rc; +} + +static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = cstream->private_data; + struct msm_compr_pdata *pdata = + snd_soc_platform_get_drvdata(rtd->platform); + uint32_t *volume = pdata->volume[rtd->dai_link->id]; + struct audio_client *ac = prtd->audio_client; + unsigned long fe_id = rtd->dai_link->id; + int rc = 0; + int bytes_to_write; + unsigned long flags; + int stream_id; + uint32_t stream_index; + uint16_t bits_per_sample = 16; + + spin_lock_irqsave(&prtd->lock, flags); + if (atomic_read(&prtd->error)) { + pr_err("%s Got RESET EVENTS notification, return immediately", + __func__); + spin_unlock_irqrestore(&prtd->lock, flags); + return 0; + } + spin_unlock_irqrestore(&prtd->lock, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__); + atomic_set(&prtd->start, 1); + + /* + * 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, prtd->run_mode, + prtd->start_delay_msw, prtd->start_delay_lsw); + break; + case SNDRV_PCM_TRIGGER_STOP: + spin_lock_irqsave(&prtd->lock, flags); + pr_debug("%s: SNDRV_PCM_TRIGGER_STOP transition %d\n", __func__, + 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__); + prtd->cmd_interrupt = 1; + wake_up(&prtd->wait_for_stream_avail); + prtd->next_stream = 0; + } + if (atomic_read(&prtd->eos)) { + pr_debug("%s: interrupt eos wait queues", __func__); + /* + * 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)) { + pr_debug("%s: interrupt drain wait queues", __func__); + prtd->cmd_interrupt = 1; + prtd->drain_ready = 1; + wake_up(&prtd->drain_wait); + atomic_set(&prtd->drain, 0); + } + prtd->last_buffer = 0; + prtd->cmd_ack = 0; + if (!prtd->gapless_state.gapless_transition) { + pr_debug("issue CMD_FLUSH stream_id %d\n", stream_id); + spin_unlock_irqrestore(&prtd->lock, flags); + q6asm_stream_cmd( + prtd->audio_client, CMD_FLUSH, stream_id); + spin_lock_irqsave(&prtd->lock, flags); + } else { + prtd->first_buffer = 0; + } + /* FIXME. only reset if flush was successful */ + prtd->byte_offset = 0; + prtd->copied_total = 0; + prtd->app_pointer = 0; + prtd->bytes_received = 0; + prtd->bytes_sent = 0; + prtd->marker_timestamp = 0; + + atomic_set(&prtd->xrun, 0); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + pr_debug("SNDRV_PCM_TRIGGER_PAUSE_PUSH transition %d\n", + prtd->gapless_state.gapless_transition); + if (!prtd->gapless_state.gapless_transition) { + pr_debug("issue CMD_PAUSE stream_id %d\n", + ac->stream_id); + q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id); + atomic_set(&prtd->start, 0); + } + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + pr_debug("SNDRV_PCM_TRIGGER_PAUSE_RELEASE transition %d\n", + prtd->gapless_state.gapless_transition); + if (!prtd->gapless_state.gapless_transition) { + atomic_set(&prtd->start, 1); + q6asm_run_nowait(prtd->audio_client, prtd->run_mode, + 0, 0); + } + break; + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + pr_debug("%s: SND_COMPR_TRIGGER_PARTIAL_DRAIN\n", __func__); + if (!prtd->gapless_state.use_dsp_gapless_mode) { + pr_debug("%s: set partial drain as drain\n", __func__); + cmd = SND_COMPR_TRIGGER_DRAIN; + } + case SND_COMPR_TRIGGER_DRAIN: + pr_debug("%s: SNDRV_COMPRESS_DRAIN\n", __func__); + /* Make sure all the data is sent to DSP before sending EOS */ + spin_lock_irqsave(&prtd->lock, flags); + + if (!atomic_read(&prtd->start)) { + pr_err("%s: stream is not in started state\n", + __func__); + rc = -EPERM; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + if (prtd->bytes_received > prtd->copied_total) { + pr_debug("%s: wait till all the data is sent to dsp\n", + __func__); + rc = msm_compr_drain_buffer(prtd, &flags); + if (rc || !atomic_read(&prtd->start)) { + if (rc != -ENETRESET) + rc = -EINTR; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + /* + * FIXME: Bug. + * Write(32767) + * Start + * Drain <- Indefinite wait + * sol1 : if (prtd->copied_total) then wait? + * sol2 : (prtd->cmd_interrupt || prtd->drain_ready || + * atomic_read(xrun) + */ + bytes_to_write = prtd->bytes_received + - prtd->copied_total; + WARN(bytes_to_write > runtime->fragment_size, + "last write %d cannot be > than fragment_size", + bytes_to_write); + + if (bytes_to_write > 0) { + pr_debug("%s: send %d partial bytes at the end", + __func__, bytes_to_write); + atomic_set(&prtd->xrun, 0); + prtd->last_buffer = 1; + msm_compr_send_buffer(prtd); + } + } + + if ((cmd == SND_COMPR_TRIGGER_PARTIAL_DRAIN) && + (prtd->gapless_state.set_next_stream_id)) { + /* wait for the last buffer to be returned */ + + if (prtd->last_buffer) { + pr_debug("%s: last buffer drain\n", __func__); + rc = msm_compr_drain_buffer(prtd, &flags); + if (rc || !atomic_read(&prtd->start)) { + spin_unlock_irqrestore(&prtd->lock, + flags); + break; + } + } + /* send EOS */ + prtd->eos_ack = 0; + atomic_set(&prtd->eos, 1); + pr_debug("issue CMD_EOS stream_id %d\n", ac->stream_id); + q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id); + pr_info("PARTIAL DRAIN, do not wait for EOS ack\n"); + + /* send a zero length buffer */ + atomic_set(&prtd->xrun, 0); + msm_compr_send_buffer(prtd); + + /* 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 || !atomic_read(&prtd->start)) { + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + /* sleep for additional duration partial drain */ + atomic_set(&prtd->drain, 1); + prtd->drain_ready = 0; + pr_debug("%s, additional sleep: %d\n", __func__, + prtd->partial_drain_delay); + spin_unlock_irqrestore(&prtd->lock, flags); + rc = wait_event_timeout(prtd->drain_wait, + prtd->drain_ready || prtd->cmd_interrupt, + msecs_to_jiffies(prtd->partial_drain_delay)); + pr_debug("%s: out of additional wait for low sample rate\n", + __func__); + spin_lock_irqsave(&prtd->lock, flags); + if (prtd->cmd_interrupt) { + pr_debug("%s: additional wait interrupted by flush)\n", + __func__); + rc = -EINTR; + prtd->cmd_interrupt = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + /* move to next stream and reset vars */ + pr_debug("%s: Moving to next stream in gapless\n", + __func__); + ac->stream_id = NEXT_STREAM_ID(ac->stream_id); + prtd->byte_offset = 0; + prtd->app_pointer = 0; + prtd->first_buffer = 1; + prtd->last_buffer = 0; + /* + * 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; + + /* + * Don't reset these as these vars map to + * total_bytes_transferred and total_bytes_available + * directly, only total_bytes_transferred will be + * updated in the next avail() ioctl + * prtd->copied_total = 0; + * prtd->bytes_received = 0; + */ + atomic_set(&prtd->drain, 0); + atomic_set(&prtd->xrun, 1); + pr_debug("%s: issue CMD_RUN", __func__); + q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + /* + * moving to next stream failed, so reset the gapless state + * set next stream id for the same session so that the same + * 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; + atomic_set(&prtd->eos, 1); + q6asm_stream_cmd_nowait(ac, CMD_EOS, ac->stream_id); + + spin_unlock_irqrestore(&prtd->lock, flags); + + + /* Wait indefinitely for DRAIN. Flush can also signal this*/ + rc = wait_event_interruptible(prtd->eos_wait, + (prtd->eos_ack || + prtd->cmd_interrupt || + atomic_read(&prtd->error))); + + if (rc < 0) + pr_err("%s: EOS wait failed\n", __func__); + + pr_debug("%s: SNDRV_COMPRESS_DRAIN out of wait for EOS\n", + __func__); + + if (prtd->cmd_interrupt) + rc = -EINTR; + + if (atomic_read(&prtd->error)) { + pr_err("%s: Got RESET EVENTS notification, return\n", + __func__); + rc = -ENETRESET; + } + + /*FIXME : what if a flush comes while PC is here */ + if (rc == 0) { + /* + * Failed to open second stream in DSP for gapless + * so prepare the current stream in session + * for gapless playback + */ + spin_lock_irqsave(&prtd->lock, flags); + pr_debug("%s:issue CMD_PAUSE stream_id %d", + __func__, ac->stream_id); + q6asm_stream_cmd_nowait(ac, CMD_PAUSE, ac->stream_id); + prtd->cmd_ack = 0; + spin_unlock_irqrestore(&prtd->lock, flags); + + /* + * Cache this time as last known time + */ + 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 + * total_bytes_transferred and total_bytes_available. + * Just total_bytes_transferred will be updated + * in the next avail() ioctl. + * prtd->copied_total = 0; + * prtd->bytes_received = 0; + * do not reset prtd->bytes_sent as well as the same + * session is used for gapless playback + */ + prtd->byte_offset = 0; + + prtd->app_pointer = 0; + prtd->first_buffer = 1; + prtd->last_buffer = 0; + atomic_set(&prtd->drain, 0); + atomic_set(&prtd->xrun, 1); + spin_unlock_irqrestore(&prtd->lock, flags); + + pr_debug("%s:issue CMD_FLUSH ac->stream_id %d", + __func__, ac->stream_id); + q6asm_stream_cmd(ac, CMD_FLUSH, ac->stream_id); + + q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + } + prtd->cmd_interrupt = 0; + break; + case SND_COMPR_TRIGGER_NEXT_TRACK: + if (!prtd->gapless_state.use_dsp_gapless_mode) { + pr_debug("%s: ignore trigger next track\n", __func__); + rc = 0; + break; + } + pr_debug("%s: SND_COMPR_TRIGGER_NEXT_TRACK\n", __func__); + spin_lock_irqsave(&prtd->lock, flags); + rc = 0; + /* next stream in gapless */ + stream_id = NEXT_STREAM_ID(ac->stream_id); + /* + * Wait if stream 1 has not completed before honoring next + * track for stream 3. Scenario happens if second clip is + * small and fills in one buffer so next track will be + * called immediately. + */ + stream_index = STREAM_ARRAY_INDEX(stream_id); + if (stream_index >= MAX_NUMBER_OF_STREAMS || + stream_index < 0) { + pr_err("%s: Invalid stream index: %d", __func__, + stream_index); + spin_unlock_irqrestore(&prtd->lock, flags); + rc = -EINVAL; + break; + } + + if (prtd->gapless_state.stream_opened[stream_index]) { + if (prtd->gapless_state.gapless_transition) { + rc = msm_compr_wait_for_stream_avail(prtd, + &flags); + } else { + /* + * If session is already opened break out if + * the state is not gapless transition. This + * is when seek happens after the last buffer + * is sent to the driver. Next track would be + * called again after last buffer is sent. + */ + pr_debug("next session is in opened state\n"); + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + } + spin_unlock_irqrestore(&prtd->lock, flags); + if (rc < 0) { + /* + * if return type EINTR then reset to zero. Tiny + * compress treats EINTR as error and prevents PARTIAL + * DRAIN. EINTR is not an error. wait for stream avail + * is interrupted by some other command like FLUSH. + */ + if (rc == -EINTR) { + pr_debug("%s: EINTR reset rc to 0\n", __func__); + rc = 0; + } + break; + } + + if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) + bits_per_sample = 24; + else if (prtd->codec_param.codec.format == + SNDRV_PCM_FORMAT_S32_LE) + bits_per_sample = 32; + + pr_debug("%s: open_write stream_id %d bits_per_sample %d", + __func__, stream_id, bits_per_sample); + rc = q6asm_stream_open_write_v4(prtd->audio_client, + prtd->codec, bits_per_sample, + stream_id, + prtd->gapless_state.use_dsp_gapless_mode); + if (rc < 0) { + pr_err("%s: Session out open failed for gapless\n", + __func__); + break; + } + + spin_lock_irqsave(&prtd->lock, flags); + prtd->gapless_state.stream_opened[stream_index] = 1; + prtd->gapless_state.set_next_stream_id = true; + spin_unlock_irqrestore(&prtd->lock, flags); + + rc = msm_compr_send_media_format_block(cstream, + stream_id, false); + if (rc < 0) { + pr_err("%s, failed to send media format block\n", + __func__); + break; + } + msm_compr_send_dec_params(cstream, pdata->dec_params[fe_id], + stream_id); + break; + } + + return rc; +} + +static int msm_compr_pointer(struct snd_compr_stream *cstream, + 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; + 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\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); + if (gapless_transition) + pr_debug("%s session time in gapless transition", + __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) { + + if (pdata->use_legacy_api) + rc = q6asm_get_session_time_legacy( + prtd->audio_client, &prtd->marker_timestamp); + else + 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 { + spin_unlock_irqrestore(&prtd->lock, flags); + } + timestamp = prtd->marker_timestamp; + + /* DSP returns timestamp in usec */ + pr_debug("%s: timestamp = %lld usec\n", __func__, timestamp); + timestamp *= prtd->sample_rate; + tstamp.pcm_io_frames = (snd_pcm_uframes_t)div64_u64(timestamp, 1000000); + memcpy(arg, &tstamp, sizeof(struct snd_compr_tstamp)); + + return 0; +} + +static int msm_compr_ack(struct snd_compr_stream *cstream, + size_t count) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + void *src, *dstn; + size_t copy; + unsigned long flags; + + WARN(1, "This path is untested"); + return -EINVAL; + + pr_debug("%s: count = %zd\n", __func__, count); + if (!prtd->buffer) { + pr_err("%s: Buffer is not allocated yet ??\n", __func__); + return -EINVAL; + } + src = runtime->buffer + prtd->app_pointer; + dstn = prtd->buffer + prtd->app_pointer; + if (count < prtd->buffer_size - prtd->app_pointer) { + memcpy(dstn, src, count); + prtd->app_pointer += count; + } else { + copy = prtd->buffer_size - prtd->app_pointer; + memcpy(dstn, src, copy); + memcpy(prtd->buffer, runtime->buffer, count - copy); + prtd->app_pointer = count - copy; + } + + /* + * If the stream is started and all the bytes received were + * copied to DSP, the newly received bytes should be + * sent right away + */ + spin_lock_irqsave(&prtd->lock, flags); + + if (atomic_read(&prtd->start) && + prtd->bytes_received == prtd->copied_total) { + prtd->bytes_received += count; + msm_compr_send_buffer(prtd); + } else + prtd->bytes_received += count; + + spin_unlock_irqrestore(&prtd->lock, flags); + + return 0; +} + +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; + void *dstn; + size_t copy; + uint64_t bytes_available = 0; + 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; + } + spin_unlock_irqrestore(&prtd->lock, flags); + + dstn = prtd->buffer + prtd->app_pointer; + if (count < prtd->buffer_size - prtd->app_pointer) { + if (copy_from_user(dstn, buf, count)) + return -EFAULT; + prtd->app_pointer += count; + } else { + copy = prtd->buffer_size - prtd->app_pointer; + if (copy_from_user(dstn, buf, copy)) + return -EFAULT; + if (copy_from_user(prtd->buffer, buf + copy, count - copy)) + return -EFAULT; + prtd->app_pointer = count - copy; + } + + /* + * If stream is started and there has been an xrun, + * since the available bytes fits fragment_size, copy the data + * right away. + */ + spin_lock_irqsave(&prtd->lock, flags); + prtd->bytes_received += count; + if (atomic_read(&prtd->start)) { + if (atomic_read(&prtd->xrun)) { + pr_debug("%s: in xrun, count = %zd\n", __func__, count); + bytes_available = prtd->bytes_received - + prtd->copied_total; + if (bytes_available >= runtime->fragment_size) { + pr_debug("%s: handle xrun, bytes_to_write = %llu\n", + __func__, bytes_available); + atomic_set(&prtd->xrun, 0); + msm_compr_send_buffer(prtd); + } /* else not sufficient data */ + } /* writes will continue on the next write_done */ + } + + spin_unlock_irqrestore(&prtd->lock, flags); + + 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) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_compr_audio *prtd = runtime->private_data; + int ret = 0; + + pr_debug("%s\n", __func__); + if ((arg != NULL) && (prtd != NULL)) { + memcpy(arg, &prtd->compr_cap, sizeof(struct snd_compr_caps)); + } else { + ret = -EINVAL; + pr_err("%s: arg (0x%pK), prtd (0x%pK)\n", __func__, arg, prtd); + } + + return ret; +} + +static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream, + struct snd_compr_codec_caps *codec) +{ + pr_debug("%s\n", __func__); + + switch (codec->codec) { + case SND_AUDIOCODEC_MP3: + codec->num_descriptors = 2; + codec->descriptor[0].max_ch = 2; + memcpy(codec->descriptor[0].sample_rates, + supported_sample_rates, + sizeof(supported_sample_rates)); + codec->descriptor[0].num_sample_rates = + sizeof(supported_sample_rates)/sizeof(unsigned int); + codec->descriptor[0].bit_rate[0] = 320; /* 320kbps */ + codec->descriptor[0].bit_rate[1] = 128; + codec->descriptor[0].num_bitrates = 2; + codec->descriptor[0].profiles = 0; + codec->descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO; + codec->descriptor[0].formats = 0; + break; + case SND_AUDIOCODEC_AAC: + codec->num_descriptors = 2; + codec->descriptor[1].max_ch = 2; + memcpy(codec->descriptor[1].sample_rates, + supported_sample_rates, + sizeof(supported_sample_rates)); + codec->descriptor[1].num_sample_rates = + sizeof(supported_sample_rates)/sizeof(unsigned int); + codec->descriptor[1].bit_rate[0] = 320; /* 320kbps */ + codec->descriptor[1].bit_rate[1] = 128; + codec->descriptor[1].num_bitrates = 2; + codec->descriptor[1].profiles = 0; + codec->descriptor[1].modes = 0; + codec->descriptor[1].formats = + (SND_AUDIOSTREAMFORMAT_MP4ADTS | + SND_AUDIOSTREAMFORMAT_RAW); + break; + case SND_AUDIOCODEC_AC3: + case SND_AUDIOCODEC_EAC3: + case SND_AUDIOCODEC_FLAC: + case SND_AUDIOCODEC_VORBIS: + case SND_AUDIOCODEC_ALAC: + case SND_AUDIOCODEC_APE: + case SND_AUDIOCODEC_DTS: + case SND_AUDIOCODEC_DSD: + case SND_AUDIOCODEC_TRUEHD: + case SND_AUDIOCODEC_IEC61937: + case SND_AUDIOCODEC_APTX: + break; + default: + pr_err("%s: Unsupported audio codec %d\n", + __func__, codec->codec); + return -EINVAL; + } + + return 0; +} + +static int msm_compr_set_metadata(struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + struct msm_compr_audio *prtd; + struct audio_client *ac; + pr_debug("%s\n", __func__); + + if (!metadata || !cstream) + return -EINVAL; + + prtd = cstream->runtime->private_data; + if (!prtd || !prtd->audio_client) { + pr_err("%s: prtd or audio client is NULL\n", __func__); + return -EINVAL; + } + + if (((metadata->key == SNDRV_COMPRESS_ENCODER_PADDING) || + (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY)) && + (prtd->compr_passthr != LEGACY_PCM)) { + pr_debug("%s: No trailing silence for compress_type[%d]\n", + __func__, prtd->compr_passthr); + return 0; + } + + ac = prtd->audio_client; + if (metadata->key == SNDRV_COMPRESS_ENCODER_PADDING) { + pr_debug("%s, got encoder padding %u", + __func__, metadata->value[0]); + prtd->gapless_state.trailing_samples_drop = metadata->value[0]; + } else if (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY) { + pr_debug("%s, got encoder delay %u", + __func__, metadata->value[0]); + prtd->gapless_state.initial_samples_drop = metadata->value[0]; + } else if (metadata->key == SNDRV_COMPRESS_RENDER_MODE) { + return msm_compr_set_render_mode(prtd, metadata->value[0]); + } else if (metadata->key == SNDRV_COMPRESS_CLK_REC_MODE) { + return msm_compr_set_clk_rec_mode(ac, metadata->value[0]); + } else if (metadata->key == SNDRV_COMPRESS_RENDER_WINDOW) { + return msm_compr_set_render_window( + ac, + metadata->value[0], + metadata->value[1], + metadata->value[2], + metadata->value[3]); + } else if (metadata->key == SNDRV_COMPRESS_START_DELAY) { + prtd->start_delay_lsw = metadata->value[0]; + prtd->start_delay_msw = metadata->value[1]; + } else if (metadata->key == + SNDRV_COMPRESS_ENABLE_ADJUST_SESSION_CLOCK) { + return msm_compr_enable_adjust_session_clock(ac, + metadata->value[0]); + } else if (metadata->key == SNDRV_COMPRESS_ADJUST_SESSION_CLOCK) { + return msm_compr_adjust_session_clock(ac, + metadata->value[0], + metadata->value[1]); + } + + return 0; +} + +static int msm_compr_get_metadata(struct snd_compr_stream *cstream, + struct snd_compr_metadata *metadata) +{ + struct msm_compr_audio *prtd; + struct audio_client *ac; + int ret = -EINVAL; + + pr_debug("%s\n", __func__); + + if (!metadata || !cstream || !cstream->runtime) + return ret; + + if (metadata->key != SNDRV_COMPRESS_PATH_DELAY) { + pr_err("%s, unsupported key %d\n", __func__, metadata->key); + return ret; + } + + prtd = cstream->runtime->private_data; + if (!prtd || !prtd->audio_client) { + pr_err("%s: prtd or audio client is NULL\n", __func__); + return ret; + } + + ac = prtd->audio_client; + ret = q6asm_get_path_delay(prtd->audio_client); + if (ret) { + pr_err("%s: get_path_delay failed, ret=%d\n", __func__, ret); + return ret; + } + + pr_debug("%s, path delay(in us) %u\n", __func__, ac->path_delay); + + metadata->value[0] = ac->path_delay; + + return ret; +} + + +static int msm_compr_set_next_track_param(struct snd_compr_stream *cstream, + union snd_codec_options *codec_options) +{ + struct msm_compr_audio *prtd; + struct audio_client *ac; + int ret = 0; + + if (!codec_options || !cstream) + return -EINVAL; + + prtd = cstream->runtime->private_data; + if (!prtd || !prtd->audio_client) { + pr_err("%s: prtd or audio client is NULL\n", __func__); + return -EINVAL; + } + + ac = prtd->audio_client; + + pr_debug("%s: got codec options for codec type %u", + __func__, prtd->codec); + switch (prtd->codec) { + case FORMAT_WMA_V9: + case FORMAT_WMA_V10PRO: + case FORMAT_FLAC: + case FORMAT_VORBIS: + case FORMAT_ALAC: + case FORMAT_APE: + memcpy(&(prtd->gapless_state.codec_options), + codec_options, + sizeof(union snd_codec_options)); + ret = msm_compr_send_media_format_block(cstream, + ac->stream_id, true); + if (ret < 0) { + pr_err("%s: failed to send media format block\n", + __func__); + } + break; + + default: + pr_debug("%s: Ignore sending CMD Format block\n", + __func__); + break; + } + + return ret; +} + +static int msm_compr_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + uint32_t *volume = NULL; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + + cstream = pdata->cstream[fe_id]; + volume = pdata->volume[fe_id]; + + volume[0] = ucontrol->value.integer.value[0]; + volume[1] = ucontrol->value.integer.value[1]; + pr_debug("%s: fe_id %lu left_vol %d right_vol %d\n", + __func__, fe_id, volume[0], volume[1]); + if (cstream) + msm_compr_set_volume(cstream, volume[0], volume[1]); + return 0; +} + +static int msm_compr_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + + struct msm_compr_pdata *pdata = + snd_soc_component_get_drvdata(comp); + uint32_t *volume = NULL; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id); + return -EINVAL; + } + + volume = pdata->volume[fe_id]; + pr_debug("%s: fe_id %lu\n", __func__, fe_id); + ucontrol->value.integer.value[0] = volume[0]; + ucontrol->value.integer.value[1] = volume[1]; + + return 0; +} + +static int msm_compr_audio_effects_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + int effects_module; + + pr_debug("%s\n", __func__); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_err("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No effects for compr_type[%d]\n", + __func__, prtd->compr_passthr); + return 0; + } + pr_debug("%s: Effects supported for compr_type[%d]\n", + __func__, prtd->compr_passthr); + + effects_module = *values++; + switch (effects_module) { + case VIRTUALIZER_MODULE: + pr_debug("%s: VIRTUALIZER_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_virtualizer_handler( + prtd->audio_client, + &(audio_effects->virtualizer), + values); + break; + case REVERB_MODULE: + pr_debug("%s: REVERB_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_reverb_handler(prtd->audio_client, + &(audio_effects->reverb), + values); + break; + case BASS_BOOST_MODULE: + pr_debug("%s: BASS_BOOST_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_bass_boost_handler(prtd->audio_client, + &(audio_effects->bass_boost), + values); + break; + case PBE_MODULE: + pr_debug("%s: PBE_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_pbe_handler(prtd->audio_client, + &(audio_effects->pbe), + values); + break; + case EQ_MODULE: + pr_debug("%s: EQ_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_popless_eq_handler(prtd->audio_client, + &(audio_effects->equalizer), + values); + break; + case SOFT_VOLUME_MODULE: + pr_debug("%s: SOFT_VOLUME_MODULE\n", __func__); + break; + case SOFT_VOLUME2_MODULE: + pr_debug("%s: SOFT_VOLUME2_MODULE\n", __func__); + if (msm_audio_effects_is_effmodule_supp_in_top(effects_module, + prtd->audio_client->topology)) + msm_audio_effects_volume_handler_v2(prtd->audio_client, + &(audio_effects->volume), + values, SOFT_VOLUME_INSTANCE_2); + break; + default: + pr_err("%s Invalid effects config module\n", __func__); + return -EINVAL; + } + return 0; +} + +static int msm_compr_audio_effects_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + + pr_debug("%s\n", __func__); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_err("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int msm_compr_query_audio_effect_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_err("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + if (prtd->compr_passthr != LEGACY_PCM) { + pr_err("%s: No effects for compr_type[%d]\n", + __func__, prtd->compr_passthr); + return -EPERM; + } + audio_effects->query.mod_id = (u32)*values++; + audio_effects->query.parm_id = (u32)*values++; + audio_effects->query.size = (u32)*values++; + audio_effects->query.offset = (u32)*values++; + audio_effects->query.device = (u32)*values++; + return 0; +} + +static int msm_compr_query_audio_effect_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_audio_effects *audio_effects = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + return -EINVAL; + } + cstream = pdata->cstream[fe_id]; + audio_effects = pdata->audio_effects[fe_id]; + if (!cstream || !audio_effects) { + pr_debug("%s: stream or effects inactive\n", __func__); + return -EINVAL; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set audio effects\n", __func__); + return -EINVAL; + } + values[0] = (long)audio_effects->query.mod_id; + values[1] = (long)audio_effects->query.parm_id; + values[2] = (long)audio_effects->query.size; + values[3] = (long)audio_effects->query.offset; + values[4] = (long)audio_effects->query.device; + return 0; +} + +static int msm_compr_send_dec_params(struct snd_compr_stream *cstream, + struct msm_compr_dec_params *dec_params, + int stream_id) +{ + + int rc = 0; + struct msm_compr_audio *prtd = NULL; + struct snd_dec_ddp *ddp = &dec_params->ddp_params; + + if (!cstream || !dec_params) { + pr_err("%s: stream or dec_params inactive\n", __func__); + rc = -EINVAL; + goto end; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set dec_params\n", __func__); + rc = -EINVAL; + goto end; + } + switch (prtd->codec) { + case FORMAT_MP3: + case FORMAT_MPEG4_AAC: + case FORMAT_TRUEHD: + case FORMAT_IEC61937: + case FORMAT_APTX: + pr_debug("%s: no runtime parameters for codec: %d\n", __func__, + prtd->codec); + break; + case FORMAT_AC3: + case FORMAT_EAC3: + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No DDP param for compr_type[%d]\n", + __func__, prtd->compr_passthr); + break; + } + rc = msm_compr_send_ddp_cfg(prtd->audio_client, ddp, stream_id); + if (rc < 0) + pr_err("%s: DDP CMD CFG failed %d\n", __func__, rc); + break; + default: + break; + } +end: + return rc; + +} +static int msm_compr_dec_params_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct msm_compr_dec_params *dec_params = NULL; + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd = NULL; + long *values = &(ucontrol->value.integer.value[0]); + int rc = 0; + + pr_debug("%s\n", __func__); + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds fe_id %lu\n", + __func__, fe_id); + rc = -EINVAL; + goto end; + } + + cstream = pdata->cstream[fe_id]; + dec_params = pdata->dec_params[fe_id]; + + if (!cstream || !dec_params) { + pr_err("%s: stream or dec_params inactive\n", __func__); + rc = -EINVAL; + goto end; + } + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: cannot set dec_params\n", __func__); + rc = -EINVAL; + goto end; + } + + switch (prtd->codec) { + case FORMAT_MP3: + case FORMAT_MPEG4_AAC: + case FORMAT_FLAC: + case FORMAT_VORBIS: + case FORMAT_ALAC: + case FORMAT_APE: + case FORMAT_DTS: + case FORMAT_DSD: + case FORMAT_TRUEHD: + case FORMAT_IEC61937: + case FORMAT_APTX: + pr_debug("%s: no runtime parameters for codec: %d\n", __func__, + prtd->codec); + break; + case FORMAT_AC3: + case FORMAT_EAC3: { + struct snd_dec_ddp *ddp = &dec_params->ddp_params; + int cnt; + + if (prtd->compr_passthr != LEGACY_PCM) { + pr_debug("%s: No DDP param for compr_type[%d]\n", + __func__, prtd->compr_passthr); + break; + } + + ddp->params_length = (*values++); + if (ddp->params_length > DDP_DEC_MAX_NUM_PARAM) { + pr_err("%s: invalid num of params:: %d\n", __func__, + ddp->params_length); + rc = -EINVAL; + goto end; + } + for (cnt = 0; cnt < ddp->params_length; cnt++) { + ddp->params_id[cnt] = *values++; + ddp->params_value[cnt] = *values++; + } + prtd = cstream->runtime->private_data; + if (prtd && prtd->audio_client) + rc = msm_compr_send_dec_params(cstream, dec_params, + prtd->audio_client->stream_id); + break; + } + default: + break; + } +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_compr_dec_params_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + /* dummy function */ + return 0; +} + +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; + int session_type = SESSION_TYPE_RX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +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 session_type = SESSION_TYPE_RX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + 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] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.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 session_type = SESSION_TYPE_TX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +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 session_type = SESSION_TYPE_TX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + 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] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_compr_channel_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + u64 fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + int rc = 0, i; + + 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); + rc = -EINVAL; + goto end; + } + + if (pdata->ch_map[fe_id]) { + pdata->ch_map[fe_id]->set_ch_map = true; + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + pdata->ch_map[fe_id]->channel_map[i] = + (char)(ucontrol->value.integer.value[i]); + } else { + pr_debug("%s: no memory for ch_map, default will be set\n", + __func__); + } +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_compr_channel_map_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + u64 fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + int rc = 0, i; + + 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); + rc = -EINVAL; + goto end; + } + if (pdata->ch_map[fe_id]) { + for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++) + ucontrol->value.integer.value[i] = + pdata->ch_map[fe_id]->channel_map[i]; + } +end: + pr_debug("%s: ret %d\n", __func__, rc); + return rc; +} + +static int msm_compr_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd; + int ret = 0; + struct msm_adsp_event_data *event_data = NULL; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null\n", __func__); + ret = -EINVAL; + goto done; + } + + event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data; + if ((event_data->event_type < ADSP_STREAM_PP_EVENT) || + (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) { + pr_err("%s: invalid event_type=%d", + __func__, event_data->event_type); + ret = -EINVAL; + goto done; + } + + if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= + sizeof(ucontrol->value.bytes.data)) { + pr_err("%s param length=%d exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_stream_cmd(prtd->audio_client, event_data); + if (ret < 0) + pr_err("%s: failed to send stream event cmd, err = %d\n", + __func__, ret); +done: + return ret; +} + +static int msm_compr_ion_fd_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd; + int fd; + int ret = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null\n", __func__); + ret = -EINVAL; + goto done; + } + + memcpy(&fd, ucontrol->value.bytes.data, sizeof(fd)); + ret = q6asm_send_ion_fd(prtd->audio_client, fd); + if (ret < 0) + pr_err("%s: failed to register ion fd\n", __func__); +done: + return ret; +} + +static int msm_compr_rtic_event_ack_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_compr_audio *prtd; + int ret = 0; + int param_length = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null\n", __func__); + ret = -EINVAL; + goto done; + } + + memcpy(¶m_length, ucontrol->value.bytes.data, + sizeof(param_length)); + if ((param_length + sizeof(param_length)) + >= sizeof(ucontrol->value.bytes.data)) { + pr_err("%s param length=%d exceeds limit", + __func__, param_length); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_rtic_event_ack(prtd->audio_client, + ucontrol->value.bytes.data + sizeof(param_length), + param_length); + if (ret < 0) + pr_err("%s: failed to send rtic event ack, err = %d\n", + __func__, ret); +done: + return ret; +} + +static int msm_compr_gapless_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct msm_compr_pdata *pdata = (struct msm_compr_pdata *) + snd_soc_component_get_drvdata(comp); + pdata->use_dsp_gapless_mode = ucontrol->value.integer.value[0]; + pr_debug("%s: value: %ld\n", __func__, + ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_compr_gapless_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct msm_compr_pdata *pdata = + snd_soc_component_get_drvdata(comp); + pr_debug("%s:gapless mode %d\n", __func__, pdata->use_dsp_gapless_mode); + ucontrol->value.integer.value[0] = pdata->use_dsp_gapless_mode; + + return 0; +} + +static const struct snd_kcontrol_new msm_compr_gapless_controls[] = { + SOC_SINGLE_EXT("Compress Gapless Playback", + 0, 0, 1, 0, + msm_compr_gapless_get, + msm_compr_gapless_put), +}; + +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 *) + kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + snd_soc_platform_set_drvdata(platform, pdata); + + for (i = 0; i < MSM_FRONTEND_DAI_MAX; i++) { + pdata->volume[i][0] = COMPRESSED_LR_VOL_MAX_STEPS; + pdata->volume[i][1] = COMPRESSED_LR_VOL_MAX_STEPS; + pdata->audio_effects[i] = NULL; + pdata->dec_params[i] = NULL; + pdata->cstream[i] = NULL; + pdata->ch_map[i] = NULL; + } + + 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 + * control is used to decide if dsp gapless mode needs to be enabled. + * Gapless is disabled by default. + */ + pdata->use_dsp_gapless_mode = false; + return 0; +} + +static int msm_compr_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = COMPRESSED_LR_VOL_MAX_STEPS; + return 0; +} + +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 = MAX_PP_PARAMS_SZ; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_query_audio_effect_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 128; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_dec_params_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 128; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_app_type_cfg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 5; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_channel_map_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 8; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFFFFFF; + return 0; +} + +static int msm_compr_add_volume_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Compress Playback"; + const char *deviceNo = "NN"; + const char *suffix = "Volume"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_volume_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_volume_info, + .tlv.p = msm_compr_vol_gain, + .get = msm_compr_volume_get, + .put = msm_compr_volume_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 + + strlen(suffix) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + pr_err("failed to allocate mixer ctrl str of len %d", ctl_len); + return 0; + } + snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name, + rtd->pcm->device, suffix); + fe_volume_control[0].name = mixer_str; + fe_volume_control[0].private_value = rtd->dai_link->id; + pr_debug("Registering new mixer ctl %s", mixer_str); + snd_soc_add_platform_controls(rtd->platform, fe_volume_control, + ARRAY_SIZE(fe_volume_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_audio_effects_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Audio Effects Config"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_audio_effects_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_audio_effects_config_info, + .get = msm_compr_audio_effects_config_get, + .put = msm_compr_audio_effects_config_put, + .private_value = 0, + } + }; + + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) + return 0; + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + + fe_audio_effects_config_control[0].name = mixer_str; + fe_audio_effects_config_control[0].private_value = rtd->dai_link->id; + pr_debug("Registering new mixer ctl %s\n", mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_audio_effects_config_control, + ARRAY_SIZE(fe_audio_effects_config_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_query_audio_effect_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Query Audio Effect Param"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_query_audio_effect_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_query_audio_effect_info, + .get = msm_compr_query_audio_effect_get, + .put = msm_compr_query_audio_effect_put, + .private_value = 0, + } + }; + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + pr_err("failed to allocate mixer ctrl str of len %d", ctl_len); + return 0; + } + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_query_audio_effect_control[0].name = mixer_str; + fe_query_audio_effect_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: registering new mixer ctl %s\n", __func__, mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_query_audio_effect_control, + ARRAY_SIZE(fe_query_audio_effect_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_audio_adsp_stream_cmd_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CMD; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_audio_adsp_stream_cmd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_compr_adsp_stream_cmd_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_stream_cmd_config_control[0].name = mixer_str; + fe_audio_adsp_stream_cmd_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_audio_adsp_stream_cmd_config_control, + ARRAY_SIZE(fe_audio_adsp_stream_cmd_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s. err = %d\n", + __func__, mixer_str, ret); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_compr_add_audio_adsp_stream_callback_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol *kctl; + + struct snd_kcontrol_new fe_audio_adsp_callback_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_callback_info, + .get = msm_adsp_stream_callback_get, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_callback_config_control[0].name = mixer_str; + fe_audio_adsp_callback_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_audio_adsp_callback_config_control, + ARRAY_SIZE(fe_audio_adsp_callback_config_control)); + if (ret < 0) { + pr_err("%s: failed to add ctl %s. err = %d\n", + __func__, mixer_str, ret); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl->private_data = NULL; + +free_mixer_str: + kfree(mixer_str); +done: + return ret; +} + +static int msm_compr_add_dec_runtime_params_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Audio Stream"; + const char *deviceNo = "NN"; + const char *suffix = "Dec Params"; + char *mixer_str = NULL; + int ctl_len; + struct snd_kcontrol_new fe_dec_params_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_dec_params_info, + .get = msm_compr_dec_params_get, + .put = msm_compr_dec_params_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + 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); + + fe_dec_params_control[0].name = mixer_str; + fe_dec_params_control[0].private_value = rtd->dai_link->id; + pr_debug("Registering new mixer ctl %s", mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_dec_params_control, + ARRAY_SIZE(fe_dec_params_control)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd) +{ + 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; + int ctl_len; + struct snd_kcontrol_new fe_app_type_cfg_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_app_type_cfg_info, + .put = msm_compr_playback_app_type_cfg_put, + .get = msm_compr_playback_app_type_cfg_get, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + return 0; + } + + 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->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; + + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) + return 0; + + 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->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)); + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_channel_map_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback Channel Map"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + struct msm_compr_pdata *pdata = NULL; + int ctl_len; + struct snd_kcontrol_new fe_channel_map_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_compr_channel_map_info, + .get = msm_compr_channel_map_get, + .put = msm_compr_channel_map_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s: NULL rtd\n", __func__); + return -EINVAL; + } + + pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + + ctl_len = strlen(mixer_ctl_name) + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) + return -ENOMEM; + + snprintf(mixer_str, ctl_len, "%s%d", mixer_ctl_name, rtd->pcm->device); + + fe_channel_map_control[0].name = mixer_str; + fe_channel_map_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + snd_soc_add_platform_controls(rtd->platform, + fe_channel_map_control, + ARRAY_SIZE(fe_channel_map_control)); + + pdata = snd_soc_platform_get_drvdata(rtd->platform); + pdata->ch_map[rtd->dai_link->id] = + kzalloc(sizeof(struct msm_compr_ch_map), GFP_KERNEL); + if (!pdata->ch_map[rtd->dai_link->id]) { + pr_err("%s: Could not allocate memory for channel map\n", + __func__); + kfree(mixer_str); + return -ENOMEM; + } + kfree(mixer_str); + return 0; +} + +static int msm_compr_add_io_fd_cmd_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback ION FD"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_ion_fd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_compr_ion_fd_map_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_ion_fd_config_control[0].name = mixer_str; + fe_ion_fd_config_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_ion_fd_config_control, + ARRAY_SIZE(fe_ion_fd_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s\n", __func__, mixer_str); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_compr_add_event_ack_cmd_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback Event Ack"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_event_ack_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_compr_rtic_event_ack_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_event_ack_config_control[0].name = mixer_str; + fe_event_ack_config_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_event_ack_config_control, + ARRAY_SIZE(fe_event_ack_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s\n", __func__, mixer_str); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_compr_new(struct snd_soc_pcm_runtime *rtd) +{ + int rc; + + rc = msm_compr_add_volume_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Volume Control\n", __func__); + + rc = msm_compr_add_audio_effects_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Audio Effects Control\n", + __func__); + + rc = msm_compr_add_audio_adsp_stream_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add Compr ADSP Stream Cmd Control\n", + __func__); + + rc = msm_compr_add_audio_adsp_stream_callback_control(rtd); + if (rc) + pr_err("%s: Could not add Compr ADSP Stream Callback Control\n", + __func__); + + rc = msm_compr_add_io_fd_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add Compr ion fd Control\n", + __func__); + + rc = msm_compr_add_event_ack_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add Compr event ack Control\n", + __func__); + + rc = msm_compr_add_query_audio_effect_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Query Audio Effect Control\n", + __func__); + + rc = msm_compr_add_dec_runtime_params_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Dec runtime params Control\n", + __func__); + rc = msm_compr_add_app_type_cfg_control(rtd); + if (rc) + pr_err("%s: Could not add Compr App Type Cfg Control\n", + __func__); + rc = msm_compr_add_channel_map_control(rtd); + if (rc) + pr_err("%s: Could not add Compr Channel Map Control\n", + __func__); + return 0; +} + +static struct snd_compr_ops msm_compr_ops = { + .open = msm_compr_open, + .free = msm_compr_free, + .trigger = msm_compr_trigger, + .pointer = msm_compr_pointer, + .set_params = msm_compr_set_params, + .set_metadata = msm_compr_set_metadata, + .get_metadata = msm_compr_get_metadata, + .set_next_track_param = msm_compr_set_next_track_param, + .ack = msm_compr_ack, + .copy = msm_compr_copy, + .get_caps = msm_compr_get_caps, + .get_codec_caps = msm_compr_get_codec_caps, +}; + +static struct snd_soc_platform_driver msm_soc_platform = { + .probe = msm_compr_probe, + .compr_ops = &msm_compr_ops, + .pcm_new = msm_compr_new, +}; + +static int msm_compr_dev_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_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-compress-dsp"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_compr_dt_match); + +static struct platform_driver msm_compr_driver = { + .driver = { + .name = "msm-compress-dsp", + .owner = THIS_MODULE, + .of_match_table = msm_compr_dt_match, + }, + .probe = msm_compr_dev_probe, + .remove = msm_compr_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + 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("Compress Offload platform driver"); +MODULE_LICENSE("GPL v2"); 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..deb179898b6a --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c @@ -0,0 +1,551 @@ +/* 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 int msm_dai_q6_ext_disp_drift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct afe_param_id_dev_timing_stats); + + return 0; +} + +static int msm_dai_q6_ext_disp_drift_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + struct afe_param_id_dev_timing_stats timing_stats; + struct snd_soc_dai *dai = kcontrol->private_data; + struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_err("%s: afe port not started. status_mask = %ld\n", + __func__, *dai_data->status_mask); + goto done; + } + + memset(&timing_stats, 0, sizeof(struct afe_param_id_dev_timing_stats)); + ret = afe_get_av_dev_drift(&timing_stats, dai->id); + if (ret) { + pr_err("%s: Error getting AFE Drift for port %d, err=%d\n", + __func__, dai->id, ret); + + ret = -EINVAL; + goto done; + } + + memcpy(ucontrol->value.bytes.data, (void *)&timing_stats, + sizeof(struct afe_param_id_dev_timing_stats)); +done: + return ret; +} + +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), + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "HDMI DRIFT", + .info = msm_dai_q6_ext_disp_drift_info, + .get = msm_dai_q6_ext_disp_drift_get, + }, +}; + +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), + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "DISPLAY_PORT DRIFT", + .info = msm_dai_q6_ext_disp_drift_info, + .get = msm_dai_q6_ext_disp_drift_get, + }, +}; + +/* 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 (rc < 0) + 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 (rc < 0) + 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)); + + kcontrol = &hdmi_config_controls[2]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai)); + } 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)); + + kcontrol = &display_port_config_controls[2]; + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(kcontrol, dai)); + } 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 (rc < 0) + 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_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_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_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_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..c8b01c679788 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c @@ -0,0 +1,8253 @@ +/* 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 + +#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, +}; + +enum { + SPKR_1, + SPKR_2, +}; + +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; + u16 vi_feed_mono; +}; + +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; + u32 num_group_ports; + 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 char *const mi2s_vi_feed_mono[] = { + "Left", + "Right", +}; + +static const struct soc_enum mi2s_config_enum[] = { + SOC_ENUM_SINGLE_EXT(4, mi2s_format), + SOC_ENUM_SINGLE_EXT(2, mi2s_vi_feed_mono), +}; + +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", + "Gen Compr" +}; + +static const char *const tdm_header_type[] = { + "Invalid", + "Default", + "Entertainment", +}; + +static const struct soc_enum tdm_config_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_data_format), tdm_data_format), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tdm_header_type), 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 u32 num_tdm_group_ports; + +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 (rc < 0) + dev_err(dai->dev, "fail to close PCM_RX AFE port\n"); + + rc = afe_close(aux_dai_data->tx_pid); + if (rc < 0) + 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 (rc < 0) { + 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 (rc < 0) + dev_err(dai->dev, "fail to close AUXPCM RX AFE port\n"); + rc = afe_close(aux_dai_data->tx_pid); + if (rc < 0) + 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 (rc < 0) + 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 (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + rc = afe_spdif_port_start(dai->id, &dai_data->spdif_port, + dai_data->rate); + if (rc < 0) + 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 (rc < 0) + 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 (rc < 0) + 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 (rc < 0) + 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; + } + if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + pr_err("%s: invalid rx num %d\n", __func__, rx_num); + 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; + } + if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + pr_err("%s: invalid tx num %d\n", __func__, tx_num); + 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_usb_audio_endian_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.endian = val; + pr_debug("%s: endian = 0x%x\n", __func__, + dai_data->port_config.usb_audio.endian); + } else { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int msm_dai_q6_usb_audio_endian_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.endian; + pr_debug("%s: endian = 0x%x\n", __func__, + dai_data->port_config.usb_audio.endian); + } else { + pr_err("%s: dai_data is NULL\n", __func__); + return -EINVAL; + } + + 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 int msm_dai_q6_slim_rx_drift_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct afe_param_id_dev_timing_stats); + + return 0; +} + +static int msm_dai_q6_slim_rx_drift_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + struct afe_param_id_dev_timing_stats timing_stats; + struct snd_soc_dai *dai = kcontrol->private_data; + struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); + + if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { + pr_err("%s: afe port not started. dai_data->status_mask = %ld\n", + __func__, *dai_data->status_mask); + goto done; + } + + memset(&timing_stats, 0, sizeof(struct afe_param_id_dev_timing_stats)); + ret = afe_get_av_dev_drift(&timing_stats, dai->id); + if (ret) { + pr_err("%s: Error getting AFE Drift for port %d, err=%d\n", + __func__, dai->id, ret); + + goto done; + } + + memcpy(ucontrol->value.bytes.data, (void *)&timing_stats, + sizeof(struct afe_param_id_dev_timing_stats)); +done: + return ret; +} + +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_RX endian", 0, 0, 1, 0, + msm_dai_q6_usb_audio_endian_cfg_get, + msm_dai_q6_usb_audio_endian_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), + SOC_SINGLE_EXT("USB_AUDIO_TX endian", 0, 0, 1, 0, + msm_dai_q6_usb_audio_endian_cfg_get, + msm_dai_q6_usb_audio_endian_cfg_put), +}; + +static const struct snd_kcontrol_new avd_drift_config_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_0_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_6_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIMBUS_7_RX DRIFT", + .info = msm_dai_q6_slim_rx_drift_info, + .get = msm_dai_q6_slim_rx_drift_get, + }, +}; +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)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[2], + dai)); + 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)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&usb_audio_cfg_controls[1], + 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[2], + dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&usb_audio_cfg_controls[3], + dai_data)); + break; + case SLIMBUS_0_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[0], + dai)); + break; + case SLIMBUS_6_RX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&avd_drift_config_controls[1], + dai)); + break; + } + if (rc < 0) + 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 (rc < 0) + 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 int msm_dai_q6_mi2s_vi_feed_mono_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->vi_feed_mono = value; + pr_debug("%s: value = %d\n", __func__, value); + return 0; +} + +static int msm_dai_q6_mi2s_vi_feed_mono_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->vi_feed_mono; + 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), + SOC_ENUM_EXT("INT5 MI2S TX Format", mi2s_config_enum[0], + msm_dai_q6_mi2s_format_get, + msm_dai_q6_mi2s_format_put), +}; + +static const struct snd_kcontrol_new mi2s_vi_feed_controls[] = { + SOC_ENUM_EXT("INT5 MI2S VI MONO", mi2s_config_enum[1], + msm_dai_q6_mi2s_vi_feed_mono_get, + msm_dai_q6_mi2s_vi_feed_mono_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; + const struct snd_kcontrol_new *vi_feed_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 (rc < 0) { + 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 (dai->id == MSM_INT5_MI2S) + ctrl = &mi2s_config_controls[11]; + } + + if (ctrl) { + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(ctrl, + &mi2s_dai_data->tx_dai.mi2s_dai_data)); + if (rc < 0) { + 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); + } + } + + if (dai->id == MSM_INT5_MI2S) + vi_feed_ctrl = &mi2s_vi_feed_controls[0]; + + if (vi_feed_ctrl) { + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(vi_feed_ctrl, + &mi2s_dai_data->tx_dai.mi2s_dai_data)); + + if (rc < 0) { + dev_err(dai->dev, "%s: err add TX vi feed channel 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 (rc < 0) + 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 (rc < 0) + 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 (rc < 0) + 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: + if (dai_data->vi_feed_mono == SPKR_1) + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_SD0; + else + dai_data->port_config.i2s.channel_mode = + AFE_PORT_I2S_SD1; + 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 (rc < 0) + 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 | + 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 = "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 | 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 = "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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) + 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 (rc < 0) + 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_q6_tdm_set_clk_param(u32 group_id, + struct afe_clk_set *clk_set, u32 mode) +{ + switch (group_id) { + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX: + if (mode) + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT; + else + 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 (mode) + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT; + else + 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 (mode) + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT; + else + 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 (mode) + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT; + else + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT; + break; + default: + return -EINVAL; + } + return 0; +} + +static int msm_dai_tdm_q6_probe(struct platform_device *pdev) +{ + int rc = 0; + const uint32_t *port_id_array = NULL; + uint32_t array_length = 0; + int i = 0; + int group_idx = 0; + u32 clk_mode = 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_tdm_group_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_tdm_group_ports); + + if (num_tdm_group_ports > AFE_GROUP_DEVICE_NUM_PORTS) { + dev_err(&pdev->dev, "%s Group Num Ports %d greater than Max %d\n", + __func__, num_tdm_group_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_tdm_group_ports) { + dev_err(&pdev->dev, "%s array_length is %d, expected is %zd\n", + __func__, array_length, + sizeof(uint32_t) * num_tdm_group_ports); + rc = -EINVAL; + goto rtn; + } + + for (i = 0; i < num_tdm_group_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_tdm_group_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); + + /* extract tdm clk src master/slave info into static */ + rc = of_property_read_u32(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-clk-internal", + &clk_mode); + if (rc) { + dev_err(&pdev->dev, "%s: Clk id from DT file %s\n", + __func__, "qcom,msm-cpudai-tdm-clk-internal"); + goto rtn; + } + dev_dbg(&pdev->dev, "%s: Clk id from DT file %d\n", + __func__, clk_mode); + + rc = msm_dai_q6_tdm_set_clk_param(tdm_group_cfg.group_id, + &tdm_clk_set, clk_mode); + if (rc) { + dev_err(&pdev->dev, "%s: group id not supported 0x%x\n", + __func__, tdm_group_cfg.group_id); + goto rtn; + } + + /* 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]; + + switch (value) { + case 0: + dai_data->port_cfg.tdm.data_format = AFE_LINEAR_PCM_DATA; + break; + case 1: + dai_data->port_cfg.tdm.data_format = AFE_NON_LINEAR_DATA; + break; + case 2: + dai_data->port_cfg.tdm.data_format = AFE_GENERIC_COMPRESSED; + break; + default: + pr_err("%s: data_format invalid\n", __func__); + break; + } + 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; + + 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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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 2: + cap_mask = 0x03; + break; + 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 (rc < 0) { + dev_err(dai->dev, "%s: fail to enable AFE clk 0x%x\n", + __func__, dai->id); + goto rtn; + } + + /* + * if only one port, don't do group enable as there + * is no group need for only one port + */ + if (dai_data->num_group_ports > 1) { + rc = afe_port_group_enable(group_id, + &dai_data->group_cfg, true); + if (rc < 0) { + 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, dai_data->num_group_ports); + if (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .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; + struct device_node *tdm_parent_node = NULL; + + /* 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 */ + tdm_parent_node = of_get_parent(pdev->dev.of_node); + rc = of_property_read_u32(tdm_parent_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(tdm_parent_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(tdm_parent_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(tdm_parent_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(tdm_parent_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; + /* copy static num group ports per parent node */ + dai_data->num_group_ports = num_tdm_group_ports; + + + 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..8115feef846e --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dai-slim.c @@ -0,0 +1,664 @@ +/* + * 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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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 (rc < 0) { + 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 (rc1 < 0) + 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; + } + + if (dai_data->status & DAI_STATE_PREPARED) { + dev_dbg(dai->dev, + "%s: dai id (%d) has already prepared.\n", + __func__, dai->id); + return 0; + } + + 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 (rc < 0) { + 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..b43ff15e7649 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dolby-common.h @@ -0,0 +1,267 @@ +/* + * 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.h b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h new file mode 100644 index 000000000000..6204cb15857d --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h @@ -0,0 +1,86 @@ +/* + * 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..5c9393f7e407 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c @@ -0,0 +1,2317 @@ +/* + * 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 + + +#if defined(CONFIG_DOLBY_DS2) || defined(CONFIG_DOLBY_LICENSE) + +/* 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 + param_payload_len, + 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); + } + } + /* Turn on 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 enable %d\n", + __func__, mod_list[j]); + adm_param_enable(port_id, copp_idx, + mod_list[j], + MODULE_ENABLE); + } + + /* 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__); + return -EINVAL; + } + + 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 || CONFIG_DOLBY_LICENSE */ 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..5804a64f5d48 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h @@ -0,0 +1,124 @@ +/* + * 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 + +#if defined(CONFIG_DOLBY_DS2) || defined(CONFIG_DOLBY_LICENSE) +/* 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 int msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw, + struct file *file, + bool open) +{ + return 0; +} + +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-srs-tm-config.c b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c new file mode 100644 index 000000000000..437cd979359d --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c @@ -0,0 +1,357 @@ +/* + * 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 "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..e3c75b3bd755 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h @@ -0,0 +1,41 @@ +/* + * 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..3a6cbe6937c4 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c @@ -0,0 +1,2414 @@ +/* + * 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 +#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 61440 +#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 | + SNDRV_PCM_FMTBIT_S24_LE), + .rates = (SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000), + .rate_min = 16000, + .rate_max = 48000, + .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, +}; + +/* Conventional and unconventional sample rate supported */ +static unsigned int supported_sample_rates[] = { + 16000, 48000, +}; + +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_v3 *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; + struct mutex lsm_api_lock; + int appl_cnt; + int dma_write; +}; + +enum { /* lsm session states */ + IDLE = 0, + RUNNING, +}; + +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_v3 *temp; + uint16_t status = 0; + uint16_t payload_size = 0; + uint16_t index = 0; + uint32_t event_ts_lsw = 0; + uint32_t event_ts_msw = 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; + + case LSM_SESSION_EVENT_DETECTION_STATUS_V3: + event_ts_lsw = ((uint32_t *)payload)[0]; + event_ts_msw = ((uint32_t *)payload)[1]; + status = (uint16_t)((uint8_t *)payload)[8]; + payload_size = (uint16_t)((uint8_t *)payload)[9]; + index = 10; + dev_dbg(rtd->dev, + "%s: ts_msw = %u, ts_lsw = %u, event detect status = %d payload size = %d\n", + __func__, event_ts_msw, event_ts_lsw, status, + payload_size); + break; + + default: + break; + } + + if (opcode == LSM_SESSION_EVENT_DETECTION_STATUS || + opcode == LSM_SESSION_EVENT_DETECTION_STATUS_V2 || + opcode == LSM_SESSION_EVENT_DETECTION_STATUS_V3) { + spin_lock_irqsave(&prtd->event_lock, flags); + temp = krealloc(prtd->event_status, + sizeof(struct snd_lsm_event_status_v3) + + payload_size, GFP_ATOMIC); + if (!temp) { + dev_err(rtd->dev, "%s: no memory for event status\n", + __func__); + return; + } + /* + * event status timestamp will be non-zero and valid if + * opcode is LSM_SESSION_EVENT_DETECTION_STATUS_V3 + */ + prtd->event_status = temp; + prtd->event_status->timestamp_lsw = event_ts_lsw; + prtd->event_status->timestamp_msw = event_ts_msw; + 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_set_poll_enable(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_poll_enable poll_enable; + int rc = 0; + + if (p_info->param_size != sizeof(poll_enable)) { + dev_err(rtd->dev, + "%s: Invalid param_size %d\n", + __func__, p_info->param_size); + rc = -EINVAL; + goto done; + } + + if (copy_from_user(&poll_enable, p_info->param_data, + sizeof(poll_enable))) { + dev_err(rtd->dev, + "%s: copy_from_user failed, size = %zd\n", + __func__, sizeof(poll_enable)); + rc = -EFAULT; + goto done; + } + + if (prtd->lsm_client->poll_enable == poll_enable.poll_en) { + dev_dbg(rtd->dev, + "%s: Polling for session %d already %s\n", + __func__, prtd->lsm_client->session, + (poll_enable.poll_en ? "enabled" : "disabled")); + rc = 0; + goto done; + } + + rc = q6lsm_set_one_param(prtd->lsm_client, p_info, + &poll_enable, LSM_POLLING_ENABLE); + if (!rc) { + prtd->lsm_client->poll_enable = poll_enable.poll_en; + } else { + dev_err(rtd->dev, + "%s: Failed to set poll enable, err = %d\n", + __func__, rc); + } +done: + 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; + case LSM_POLLING_ENABLE: + rc = msm_lsm_set_poll_enable(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; + struct snd_pcm_runtime *runtime; + struct lsm_priv *prtd; + 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__); + if (copy_from_user(&session_data, arg, + sizeof(session_data))) { + dev_err(rtd->dev, "%s: %s: copy_from_user failed\n", + __func__, "LSM_SET_SESSION_DATA"); + return -EFAULT; + } + + 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: + 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: + case SNDRV_LSM_EVENT_STATUS_V3: { + uint32_t ts_lsw, ts_msw; + uint16_t status = 0, payload_size = 0; + + dev_dbg(rtd->dev, "%s: Get event status\n", __func__); + atomic_set(&prtd->event_wait_stop, 0); + + /* + * Release the api lock before wait to allow + * other IOCTLs to be invoked while waiting + * for event + */ + mutex_unlock(&prtd->lsm_api_lock); + rc = wait_event_freezable(prtd->event_wait, + (cmpxchg(&prtd->event_avail, 1, 0) || + (xchg = atomic_cmpxchg(&prtd->event_wait_stop, + 1, 0)))); + mutex_lock(&prtd->lsm_api_lock); + 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) { + payload_size = prtd->event_status->payload_size; + ts_lsw = prtd->event_status->timestamp_lsw; + ts_msw = prtd->event_status->timestamp_msw; + status = prtd->event_status->status; + 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 (cmd == SNDRV_LSM_EVENT_STATUS) { + struct snd_lsm_event_status *user = arg; + + if (user->payload_size < payload_size) { + dev_dbg(rtd->dev, + "%s: provided %d bytes isn't enough, needs %d bytes\n", + __func__, user->payload_size, + payload_size); + rc = -ENOMEM; + } else { + user->status = status; + user->payload_size = payload_size; + memcpy(user->payload, + prtd->event_status->payload, + payload_size); + } + } else { + struct snd_lsm_event_status_v3 *user_v3 = arg; + + if (user_v3->payload_size < payload_size) { + dev_dbg(rtd->dev, + "%s: provided %d bytes isn't enough, needs %d bytes\n", + __func__, user_v3->payload_size, + payload_size); + rc = -ENOMEM; + } else { + user_v3->timestamp_lsw = ts_lsw; + user_v3->timestamp_msw = ts_msw; + user_v3->status = status; + user_v3->payload_size = payload_size; + memcpy(user_v3->payload, + prtd->event_status->payload, + payload_size); + } + } + if (!rc) { + 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; + + if (copy_from_user(&enable, arg, sizeof(enable))) { + dev_err(rtd->dev, "%s: %s: copy_frm_user failed\n", + __func__, "LSM_LAB_CONTROL"); + return -EFAULT; + } + + 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; + + case SNDRV_LSM_SET_PORT: + dev_dbg(rtd->dev, "%s: set LSM port\n", __func__); + rc = q6lsm_set_port_connected(prtd->lsm_client); + break; + + case SNDRV_LSM_SET_FWK_MODE_CONFIG: { + u32 mode; + + if (copy_from_user(&mode, arg, sizeof(mode))) { + dev_err(rtd->dev, "%s: %s: copy_frm_user failed\n", + __func__, "LSM_SET_FWK_MODE_CONFIG"); + return -EFAULT; + } + + dev_dbg(rtd->dev, "%s: ioctl %s, enable = %d\n", + __func__, "SNDRV_LSM_SET_FWK_MODE_CONFIG", mode); + if (prtd->lsm_client->event_mode == mode) { + dev_dbg(rtd->dev, + "%s: mode for %d already set to %d\n", + __func__, prtd->lsm_client->session, mode); + rc = 0; + } else { + dev_dbg(rtd->dev, "%s: Event mode = %d\n", + __func__, mode); + rc = q6lsm_set_fwk_mode_cfg(prtd->lsm_client, mode); + if (!rc) + prtd->lsm_client->event_mode = mode; + else + dev_err(rtd->dev, + "%s: set event mode failed %d\n", + __func__, rc); + } + 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_event_status_v3_32 { + u32 timestamp_lsw; + u32 timestamp_msw; + 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; + uint32_t param_type; +}; + +struct snd_lsm_module_params_32 { + compat_uptr_t params; + u32 num_params; + u32 data_size; +}; + +enum { + 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), + SNDRV_LSM_EVENT_STATUS_V3_32 = + _IOW('U', 0x0F, struct snd_lsm_event_status_v3_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; + + mutex_lock(&prtd->lsm_api_lock); + + switch (cmd) { + case SNDRV_LSM_EVENT_STATUS: { + struct snd_lsm_event_status *user = NULL, userarg32; + struct snd_lsm_event_status *user32 = NULL; + + if (copy_from_user(&userarg32, arg, sizeof(userarg32))) { + dev_err(rtd->dev, "%s: err copyuser ioctl %s\n", + __func__, "SNDRV_LSM_EVENT_STATUS"); + err = -EFAULT; + goto done; + } + + 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); + err = -EINVAL; + goto done; + } + + size = sizeof(*user) + userarg32.payload_size; + user = kzalloc(size, GFP_KERNEL); + if (!user) { + dev_err(rtd->dev, + "%s: Allocation failed event status size %d\n", + __func__, size); + err = -EFAULT; + goto done; + } 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 = kzalloc(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_EVENT_STATUS_V3_32: { + struct snd_lsm_event_status_v3_32 userarg32, *user32 = NULL; + struct snd_lsm_event_status_v3 *user = NULL; + + if (copy_from_user(&userarg32, arg, sizeof(userarg32))) { + dev_err(rtd->dev, "%s: err copyuser ioctl %s\n", + __func__, "SNDRV_LSM_EVENT_STATUS_V3_32"); + 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 = kzalloc(size, GFP_KERNEL); + if (!user) { + dev_err(rtd->dev, + "%s: Allocation failed event status size %d\n", + __func__, size); + return -EFAULT; + } + cmd = SNDRV_LSM_EVENT_STATUS_V3; + 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 = kzalloc(size, GFP_KERNEL); + if (!user32) { + dev_err(rtd->dev, + "%s: Allocation event user status size %d\n", + __func__, size); + err = -EFAULT; + } else { + user32->timestamp_lsw = user->timestamp_lsw; + user32->timestamp_msw = user->timestamp_msw; + 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"); + err = -EINVAL; + goto done; + } + + 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"); + err = -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"); + err = -EINVAL; + goto done; + } + + 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)); + err = -EFAULT; + goto done; + } + + 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); + err = -EINVAL; + goto done; + } + + 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); + err = -EINVAL; + goto done; + } + + p_size = sizeof(struct lsm_params_info_32) * + p_data.num_params; + + params32 = kzalloc(p_size, GFP_KERNEL); + if (!params32) { + err = -ENOMEM; + goto done; + } + + 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); + err = -ENOMEM; + goto done; + } + + 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); + err = -EFAULT; + goto done; + } + + 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; + } + case SNDRV_LSM_REG_SND_MODEL_V2: + case SNDRV_LSM_SET_PARAMS: + case SNDRV_LSM_SET_MODULE_PARAMS: + /* + * In ideal cases, the compat_ioctl should never be called + * with the above unlocked ioctl commands. Print error + * and return error if it does. + */ + dev_err(rtd->dev, + "%s: Invalid cmd for compat_ioctl\n", + __func__); + err = -EINVAL; + break; + default: + err = msm_lsm_ioctl_shared(substream, cmd, arg); + break; + } +done: + mutex_unlock(&prtd->lsm_api_lock); + 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_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; + + mutex_lock(&prtd->lsm_api_lock); + switch (cmd) { + 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"); + err = -EINVAL; + goto done; + } + + 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"); + err = -EINVAL; + goto done; + } + + pr_debug("%s: SNDRV_LSM_SET_PARAMS\n", __func__); + + 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); + + goto done; + } + + 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"); + err = -EINVAL; + goto done; + } + + 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)); + err = -EFAULT; + goto done; + } + + 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); + err = -EINVAL; + goto done; + } + + 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); + + err = -EFAULT; + goto done; + } + + params = kzalloc(p_size, GFP_KERNEL); + if (!params) { + err = -ENOMEM; + goto done; + } + + 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); + err = -EFAULT; + goto done; + } + + 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 (copy_from_user(&userarg, arg, sizeof(userarg))) { + dev_err(rtd->dev, + "%s: err copyuser event_status\n", + __func__); + err = -EFAULT; + goto done; + } + + 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); + err = -EINVAL; + goto done; + } + + size = sizeof(struct snd_lsm_event_status) + + userarg.payload_size; + user = kzalloc(size, GFP_KERNEL); + if (!user) { + dev_err(rtd->dev, + "%s: Allocation failed event status size %d\n", + __func__, size); + err = -EFAULT; + goto done; + } + 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); + goto done; + } + + case SNDRV_LSM_EVENT_STATUS_V3: { + struct snd_lsm_event_status_v3 *user = NULL; + struct snd_lsm_event_status_v3 userarg; + + dev_dbg(rtd->dev, + "%s: SNDRV_LSM_EVENT_STATUS_V3\n", __func__); + if (!arg) { + dev_err(rtd->dev, + "%s: Invalid params event_status_v3\n", + __func__); + return -EINVAL; + } + if (copy_from_user(&userarg, arg, sizeof(userarg))) { + dev_err(rtd->dev, + "%s: err copyuser event_status_v3\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_v3) + + userarg.payload_size; + user = kzalloc(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: lsm_event_v3 failed %d", __func__, err); + break; + } + + default: + err = msm_lsm_ioctl_shared(substream, cmd, arg); + break; + } +done: + mutex_unlock(&prtd->lsm_api_lock); + 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; + } + mutex_init(&prtd->lsm_api_lock); + 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; + prtd->lsm_client->session_state = IDLE; + prtd->lsm_client->poll_enable = true; + prtd->lsm_client->perf_mode = 0; + prtd->lsm_client->event_mode = LSM_EVENT_NON_TIME_STAMP_MODE; + + 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; + int ret = 0; + + 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; + } + + if (q6lsm_set_media_fmt_params(prtd->lsm_client)) + dev_dbg(rtd->dev, + "%s: failed to set lsm media fmt params\n", __func__); + + if (prtd->lsm_client->session_state == IDLE) { + ret = msm_pcm_routing_reg_phy_compr_stream( + rtd->dai_link->id, + prtd->lsm_client->perf_mode, + prtd->lsm_client->session, + SNDRV_PCM_STREAM_CAPTURE, + LISTEN); + if (ret) { + dev_err(rtd->dev, + "%s: register phy compr stream failed %d\n", + __func__, ret); + return ret; + } + } + + prtd->lsm_client->session_state = RUNNING; + prtd->lsm_client->started = false; + runtime->private_data = prtd; + return ret; +} + +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__); + } + + msm_pcm_routing_dereg_phy_stream(rtd->dai_link->id, + SNDRV_PCM_STREAM_CAPTURE); + + 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); + mutex_destroy(&prtd->lsm_api_lock); + 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_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->num_chs = params_channels(params); + hw_params->period_count = params_periods(params); + hw_params->sample_rate = params_rate(params); + if (((hw_params->sample_rate != 16000) && + (hw_params->sample_rate != 48000)) || + (hw_params->period_count == 0)) { + dev_err(rtd->dev, + "%s: Invalid Params sample rate %d period count %d\n", + __func__, hw_params->sample_rate, + hw_params->period_count); + return -EINVAL; + } + + 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 { + dev_err(rtd->dev, "%s: Invalid Format 0x%x\n", + __func__, params_format(params)); + return -EINVAL; + } + + hw_params->buf_sz = params_buffer_bytes(params) / + hw_params->period_count; + dev_dbg(rtd->dev, + "%s: channels %d sample rate %d sample size %d buffer size %d period count %d\n", + __func__, hw_params->num_chs, 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 int msm_lsm_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return 0; +} + +static int msm_lsm_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u64 fe_id = kcontrol->private_value; + int session_type = SESSION_TYPE_TX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + 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] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); +done: + return ret; +} + +static int msm_lsm_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 *mixer_ctl_name = "Listen Stream"; + const char *deviceNo = "NN"; + const char *suffix = "App Type Cfg"; + int ctl_len, ret = 0; + + ctl_len = strlen(mixer_ctl_name) + 1 + + strlen(deviceNo) + 1 + strlen(suffix) + 1; + pr_debug("%s: Listen app type cntrl add\n", __func__); + ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE, + NULL, 1, ctl_len, rtd->dai_link->id, + &app_type_info); + if (ret < 0) { + pr_err("%s: Listen 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", + mixer_ctl_name, rtd->pcm->device, suffix); + kctl->put = msm_lsm_app_type_cfg_ctl_put; + kctl->get = msm_lsm_app_type_cfg_ctl_get; + return 0; +} + +static int msm_lsm_add_controls(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + + ret = msm_lsm_add_app_type_controls(rtd); + if (ret) + pr_err("%s, add app type controls failed:%d\n", __func__, ret); + + return ret; +} + +static const 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; + int ret = 0; + + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + + ret = msm_lsm_add_controls(rtd); + if (ret) + pr_err("%s, kctl add failed:%d\n", __func__, ret); + + return ret; +} + +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..ab9b3107665f --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c @@ -0,0 +1,922 @@ +/* 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); + runtime->private_data = NULL; + 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 const 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..f4e03fe2e4cf --- /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 const 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..2f60db993895 --- /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 const 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..7ef1ca8f1ae1 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c @@ -0,0 +1,801 @@ +/* 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 "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; + +struct msm_pcm_pdata { + int perf_mode; +}; + +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; + struct msm_pcm_pdata *pdata; + + 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); + + pdata = (struct msm_pcm_pdata *) + dev_get_drvdata(rtd->platform->dev); + if (!pdata) { + dev_err(rtd->platform->dev, + "%s: platform data not populated\n", __func__); + mutex_unlock(&pcm->lock); + return -EINVAL; + } + + 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 = pdata->perf_mode; + 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->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->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->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->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 const 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->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 session_type = SESSION_TYPE_RX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +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 session_type = SESSION_TYPE_RX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + 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] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.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 session_type = SESSION_TYPE_TX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +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 session_type = SESSION_TYPE_TX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + 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] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.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->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->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) +{ + struct msm_pcm_pdata *pdata; + + dev_dbg(&pdev->dev, "%s: dev name %s\n", + __func__, dev_name(&pdev->dev)); + + pdata = kzalloc(sizeof(struct msm_pcm_pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (of_property_read_bool(pdev->dev.of_node, + "qcom,msm-pcm-loopback-low-latency")) + pdata->perf_mode = LOW_LATENCY_PCM_MODE; + else + pdata->perf_mode = LEGACY_PCM_MODE; + + dev_set_drvdata(&pdev->dev, pdata); + + 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..325d642b7d7c --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c @@ -0,0 +1,1136 @@ +/* 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 "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 unsigned long msm_pcm_fe_topology[MSM_FRONTEND_DAI_MAX]; + +/* default value is DTS (i.e read from device tree) */ +static char const *msm_pcm_fe_topology_text[] = { + "DTS", "ULL", "ULL_PP", "LL" }; + +static const struct soc_enum msm_pcm_fe_topology_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(msm_pcm_fe_topology_text), + msm_pcm_fe_topology_text), +}; + +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; + unsigned long topology; + int perf_mode; + + 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; + } + + topology = msm_pcm_fe_topology[soc_prtd->dai_link->id]; + + if (!strcmp(msm_pcm_fe_topology_text[topology], "ULL_PP")) + perf_mode = ULL_POST_PROCESSING_PCM_MODE; + else if (!strcmp(msm_pcm_fe_topology_text[topology], "ULL")) + perf_mode = ULTRA_LOW_LATENCY_PCM_MODE; + else if (!strcmp(msm_pcm_fe_topology_text[topology], "LL")) + perf_mode = LOW_LATENCY_PCM_MODE; + else + /* use the default from the device tree */ + perf_mode = pdata->perf_mode; + + + /* need to set LOW_LATENCY_PCM_MODE for capture since + * push mode does not support ULL + */ + prtd->audio_client->perf_mode = (dir == IN) ? + 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->id); + ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->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->id, + dir == IN ? + SNDRV_PCM_STREAM_PLAYBACK : + SNDRV_PCM_STREAM_CAPTURE); + kfree(prtd); + runtime->private_data = NULL; + + 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->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_pcm_fe_topology_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + const struct soc_enum *e = &msm_pcm_fe_topology_enum[0]; + + return snd_ctl_enum_info(uinfo, 1, e->items, e->texts); +} + +static int msm_pcm_fe_topology_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned long fe_id = kcontrol->private_value; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id); + return -EINVAL; + } + + pr_debug("%s: %lu topology %s\n", __func__, fe_id, + msm_pcm_fe_topology_text[msm_pcm_fe_topology[fe_id]]); + ucontrol->value.enumerated.item[0] = msm_pcm_fe_topology[fe_id]; + return 0; +} + +static int msm_pcm_fe_topology_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + unsigned long fe_id = kcontrol->private_value; + unsigned int item; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bound fe_id %lu\n", __func__, fe_id); + return -EINVAL; + } + + item = ucontrol->value.enumerated.item[0]; + if (item >= ARRAY_SIZE(msm_pcm_fe_topology_text)) { + pr_err("%s Received out of bound topology %lu\n", __func__, + fe_id); + return -EINVAL; + } + + pr_debug("%s: %lu new topology %s\n", __func__, fe_id, + msm_pcm_fe_topology_text[item]); + msm_pcm_fe_topology[fe_id] = item; + return 0; +} + +static int msm_pcm_add_fe_topology_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "PCM_Dev"; + const char *deviceNo = "NN"; + const char *topo_text = "Topology"; + char *mixer_str = NULL; + int ctl_len; + int ret; + struct snd_kcontrol_new topology_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "?", + .info = msm_pcm_fe_topology_info, + .get = msm_pcm_fe_topology_get, + .put = msm_pcm_fe_topology_put, + .private_value = 0, + }, + }; + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 + + strlen(topo_text) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) + return -ENOMEM; + + snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name, + rtd->pcm->device, topo_text); + + topology_control[0].name = mixer_str; + topology_control[0].private_value = rtd->dai_link->id; + ret = snd_soc_add_platform_controls(rtd->platform, topology_control, + ARRAY_SIZE(topology_control)); + msm_pcm_fe_topology[rtd->dai_link->id] = 0; + kfree(mixer_str); + return ret; +} + +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 session_type = SESSION_TYPE_RX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + return ret; +} + +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 session_type = SESSION_TYPE_RX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + 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] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.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 session_type = SESSION_TYPE_TX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +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 session_type = SESSION_TYPE_TX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + 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] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.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->id, + &app_type_info); + if (ret < 0) { + pr_err("%s: playback app type cntrl add failed, err: %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->id, + &app_type_info); + if (ret < 0) { + pr_err("%s: capture app type cntrl add failed, err: %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_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); + } + + ret = msm_pcm_add_fe_topology_control(rtd); + if (ret) { + pr_err("%s: Could not add pcm topology control %d\n", + __func__, ret); + } + + ret = msm_pcm_add_app_type_controls(rtd); + if (ret) { + pr_err("%s: Could not add app type controls failed %d\n", + __func__, ret); + } + + pcm->nonatomic = true; +exit: + return ret; +} + + +static const 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) { + if (!strcmp(latency_level, "ultra")) + perf_mode = ULTRA_LOW_LATENCY_PCM_MODE; + else if (!strcmp(latency_level, "ull-pp")) + perf_mode = ULL_POST_PROCESSING_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..74e99d376049 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c @@ -0,0 +1,1884 @@ +/* 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 "msm-pcm-q6-v2.h" +#include "msm-pcm-routing-v2.h" +#include "msm-qti-pp-config.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, 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 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; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + + 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 ASM_STREAM_PP_EVENT: + case ASM_STREAM_CMD_ENCDEC_EVENTS: { + pr_debug("%s: ASM_STREAM_EVENT (0x%x)\n", __func__, opcode); + if (!substream) { + pr_err("%s: substream is NULL.\n", __func__); + return; + } + + rtd = substream->private_data; + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + return; + } + + ret = msm_adsp_inform_mixer_ctl(rtd, payload); + if (ret) { + pr_err("%s: failed to inform mixer ctl. err = %d\n", + __func__, ret); + return; + } + + 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; + case ASM_STREAM_CMD_REGISTER_PP_EVENTS: + pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS:", + __func__); + 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; + uint32_t fmt_type = FORMAT_LINEAR_PCM; + 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; + } + if (prtd->compress_enable) { + fmt_type = FORMAT_GEN_COMPR; + pr_debug("%s: Compressed enabled!\n", __func__); + ret = q6asm_open_write_compressed(prtd->audio_client, fmt_type, + COMPRESSED_PASSTHROUGH_GEN); + if (ret < 0) { + pr_err("%s: q6asm_open_write_compressed failed (%d)\n", + __func__, ret); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return -ENOMEM; + } + } else { + ret = q6asm_open_write_v4(prtd->audio_client, + fmt_type, bits_per_sample); + + if (ret < 0) { + pr_err("%s: q6asm_open_write_v4 failed (%d)\n", + __func__, ret); + 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; + + if (prtd->compress_enable) { + ret = msm_pcm_routing_reg_phy_compr_stream( + soc_prtd->dai_link->id, + prtd->audio_client->perf_mode, + prtd->session_id, + SNDRV_PCM_STREAM_PLAYBACK, + COMPRESSED_PASSTHROUGH_GEN); + } else { + ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->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; + } + if (prtd->compress_enable) { + ret = q6asm_media_format_block_gen_compr( + prtd->audio_client, runtime->rate, + runtime->channels, !prtd->set_channel_map, + prtd->channel_map, bits_per_sample); + } else { + 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, false); + 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->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; + msm_adsp_init_mixer_ctl_pp_event_queue(soc_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->id, + SNDRV_PCM_STREAM_PLAYBACK); + msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd); + kfree(prtd); + runtime->private_data = NULL; + + 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->id, + SNDRV_PCM_STREAM_CAPTURE); + kfree(prtd); + runtime->private_data = NULL; + + 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 const 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_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *pcm = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_component_to_platform(pcm); + struct msm_plat_data *pdata = dev_get_drvdata(platform->dev); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + int ret = 0; + struct msm_adsp_event_data *event_data = NULL; + + if (!pdata) { + pr_err("%s pdata is NULL\n", __func__); + ret = -ENODEV; + goto done; + } + + substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s substream not found\n", __func__); + ret = -EINVAL; + goto done; + } + + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = substream->runtime->private_data; + if (prtd->audio_client == NULL) { + pr_err("%s prtd is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data; + if ((event_data->event_type < ADSP_STREAM_PP_EVENT) || + (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) { + pr_err("%s: invalid event_type=%d", + __func__, event_data->event_type); + ret = -EINVAL; + goto done; + } + + if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= + sizeof(ucontrol->value.bytes.data)) { + pr_err("%s param length=%d exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_stream_cmd(prtd->audio_client, event_data); + if (ret < 0) + pr_err("%s: failed to send stream event cmd, err = %d\n", + __func__, ret); +done: + return ret; +} + +static int msm_pcm_add_audio_adsp_stream_cmd_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CMD; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_audio_adsp_stream_cmd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_pcm_adsp_stream_cmd_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_stream_cmd_config_control[0].name = mixer_str; + fe_audio_adsp_stream_cmd_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("Registering new mixer ctl %s\n", mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_audio_adsp_stream_cmd_config_control, + ARRAY_SIZE(fe_audio_adsp_stream_cmd_config_control)); + if (ret < 0) + pr_err("%s: failed add ctl %s. err = %d\n", + __func__, mixer_str, ret); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_pcm_add_audio_adsp_stream_callback_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol *kctl; + + struct snd_kcontrol_new fe_audio_adsp_callback_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_callback_info, + .get = msm_adsp_stream_callback_get, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + pr_debug("%s: added new pcm FE with name %s, id %d, cpu dai %s, device no %d\n", + __func__, rtd->dai_link->name, rtd->dai_link->id, + rtd->dai_link->cpu_dai_name, rtd->pcm->device); + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_audio_adsp_callback_config_control[0].name = mixer_str; + fe_audio_adsp_callback_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_audio_adsp_callback_config_control, + ARRAY_SIZE(fe_audio_adsp_callback_config_control)); + if (ret < 0) { + pr_err("%s: failed to add ctl %s. err = %d\n", + __func__, mixer_str, ret); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl->private_data = NULL; + +free_mixer_str: + kfree(mixer_str); +done: + return ret; +} + +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->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_compress_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0x2000; + return 0; +} + +static int msm_pcm_compress_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_component_to_platform(comp); + struct msm_plat_data *pdata = dev_get_drvdata(platform->dev); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + + if (!pdata) { + pr_err("%s pdata is NULL\n", __func__); + return -ENODEV; + } + substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -EINVAL; + } + 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->compress_enable; + return 0; +} + +static int msm_pcm_compress_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int rc = 0; + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_component_to_platform(comp); + struct msm_plat_data *pdata = dev_get_drvdata(platform->dev); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + int compress = ucontrol->value.integer.value[0]; + + if (!pdata) { + pr_err("%s pdata is NULL\n", __func__); + return -ENODEV; + } + substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + pr_debug("%s: compress : 0x%x\n", __func__, compress); + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -EINVAL; + } + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return 0; + } + prtd = substream->runtime->private_data; + if (prtd) { + pr_debug("%s: setting compress flag to 0x%x\n", + __func__, compress); + prtd->compress_enable = compress; + } + return rc; +} + +static int msm_pcm_add_compress_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback "; + const char *mixer_ctl_end_name = " Compress"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len; + int ret = 0; + struct msm_plat_data *pdata; + struct snd_kcontrol_new pcm_compress_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_pcm_compress_ctl_info, + .get = msm_pcm_compress_ctl_get, + .put = msm_pcm_compress_ctl_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s: NULL rtd\n", __func__); + return -EINVAL; + } + + ctl_len = strlen(mixer_ctl_name) + strlen(deviceNo) + + strlen(mixer_ctl_end_name) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + + if (!mixer_str) + return -ENOMEM; + + snprintf(mixer_str, ctl_len, "%s%d%s", mixer_ctl_name, + rtd->pcm->device, mixer_ctl_end_name); + + pcm_compress_control[0].name = mixer_str; + pcm_compress_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + pdata = dev_get_drvdata(rtd->platform->dev); + if (pdata) { + if (!pdata->pcm) { + pdata->pcm = rtd->pcm; + snd_soc_add_platform_controls(rtd->platform, + pcm_compress_control, + ARRAY_SIZE + (pcm_compress_control)); + pr_debug("%s: add control success plt = %pK\n", + __func__, rtd->platform); + } + } else { + pr_err("%s: NULL pdata\n", __func__); + ret = -EINVAL; + } + kfree(mixer_str); + return ret; +} + +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 session_type = SESSION_TYPE_RX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +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 session_type = SESSION_TYPE_RX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + 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] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.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 session_type = SESSION_TYPE_TX; + int be_id = ucontrol->value.integer.value[3]; + struct msm_pcm_stream_app_type_cfg cfg_data = {0, 0, 48000}; + int ret = 0; + + cfg_data.app_type = ucontrol->value.integer.value[0]; + cfg_data.acdb_dev_id = ucontrol->value.integer.value[1]; + if (ucontrol->value.integer.value[2] != 0) + cfg_data.sample_rate = ucontrol->value.integer.value[2]; + pr_debug("%s: fe_id- %llu session_type- %d be_id- %d app_type- %d acdb_dev_id- %d sample_rate- %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.sample_rate); + ret = msm_pcm_routing_reg_stream_app_type_cfg(fe_id, session_type, + be_id, &cfg_data); + if (ret < 0) + pr_err("%s: msm_pcm_routing_reg_stream_app_type_cfg failed returned %d\n", + __func__, ret); + + return ret; +} + +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 session_type = SESSION_TYPE_TX; + int be_id = 0; + struct msm_pcm_stream_app_type_cfg cfg_data = {0}; + int ret = 0; + + ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, session_type, + &be_id, &cfg_data); + 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] = cfg_data.app_type; + ucontrol->value.integer.value[1] = cfg_data.acdb_dev_id; + ucontrol->value.integer.value[2] = cfg_data.sample_rate; + ucontrol->value.integer.value[3] = be_id; + pr_debug("%s: fedai_id %llu, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fe_id, session_type, be_id, + cfg_data.app_type, cfg_data.acdb_dev_id, cfg_data.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->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->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); + + ret = msm_pcm_add_compress_control(rtd); + if (ret) + pr_err("%s: Could not add pcm Compress Control %d\n", + __func__, ret); + + ret = msm_pcm_add_audio_adsp_stream_cmd_control(rtd); + if (ret) + pr_err("%s: Could not add pcm ADSP Stream Cmd Control\n", + __func__); + + ret = msm_pcm_add_audio_adsp_stream_callback_control(rtd); + if (ret) + pr_err("%s: Could not add pcm ADSP Stream Callback Control\n", + __func__); + + 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..3b3f0480d33c --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h @@ -0,0 +1,130 @@ +/* + * 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; + bool compress_enable; + /* 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; + struct snd_pcm *pcm; +}; + +#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..7335951522b3 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c @@ -0,0 +1,139 @@ +/* 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 "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; + 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__); +} + +#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; + 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->id < 0 || + dai_link->id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s:BE id %d invalid index\n", + __func__, dai_link->id); + return -EINVAL; + } + pr_debug("%s BE id %d\n", __func__, dai_link->id); + rc = snd_hwdep_new(runtime->card->snd_card, + msm_bedais[dai_link->id].name, + dai_link->id, &hwdep); + if (hwdep == NULL) { + pr_err("%s: hwdep intf failed to create %s- hwdep NULL\n", + __func__, msm_bedais[dai_link->id].name); + return rc; + } + if (rc < 0) { + pr_err("%s: hwdep intf failed to create %s rc %d\n", __func__, + msm_bedais[dai_link->id].name, rc); + return rc; + } + + hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_BE; + hwdep->private_data = &msm_bedais[dai_link->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 rc; +} +#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..8a0b96700329 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h @@ -0,0 +1,35 @@ +/* + * 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..d67296f238f0 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c @@ -0,0 +1,15765 @@ +/* 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 "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" + +#ifndef CONFIG_DOLBY_DAP +#undef DOLBY_ADM_COPP_TOPOLOGY_ID +#define DOLBY_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFE +#endif + +#ifndef CONFIG_DOLBY_DS2 +#undef DS2_ADM_COPP_TOPOLOGY_ID +#define DS2_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFF +#endif + +static struct mutex routing_lock; + +static struct cal_type_data *cal_data; + +static int fm_switch_enable; +static int hfp_switch_enable; +static int int0_mi2s_switch_enable; +static int int4_mi2s_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 usb_switch_enable; +static int lsm_port_index; +static int slim0_rx_aanc_fb_port; +static int msm_route_ec_ref_rx; +static int msm_ec_ref_ch = 4; +static int msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE; +static int msm_ec_ref_sampling_rate = 48000; +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; +static bool swap_ch; + +#define WEIGHT_0_DB 0x4000 +/* all the FEs which can support channel mixer */ +static struct msm_pcm_channel_mixer channel_mixer[MSM_FRONTEND_DAI_MM_SIZE]; +/* input BE for each FE */ +static int channel_input[MSM_FRONTEND_DAI_MM_SIZE][ADM_MAX_CHANNELS]; + +enum { + MADNONE, + MADAUDIO, + MADBEACON, + MADULTRASOUND, + MADSWAUDIO, +}; + +#define ADM_LSM_PORT_INDEX 9 + +#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 QUAT_MI2S_TX_TEXT "QUAT_MI2S_TX" +#define ADM_LSM_TX_TEXT "ADM_LSM_TX" +#define INT3_MI2S_TX_TEXT "INT3_MI2S_TX" + +#define LSM_FUNCTION_TEXT "LSM Function" +static const char * const lsm_port_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, QUAT_MI2S_TX_TEXT, ADM_LSM_TX_TEXT, + INT3_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}, +}; + +/* + * The be_dai_name_table is passed to HAL so that it can specify the + * BE ID for the BE it wants to enable based on the name. Thus there + * is a matching table and structure in HAL that need to be updated + * if any changes to these are made. + */ +struct msm_pcm_route_bdai_name { + unsigned int be_id; + char be_name[LPASS_BE_NAME_MAX_LENGTH]; +}; +static struct msm_pcm_route_bdai_name be_dai_name_table[MSM_BACKEND_DAI_MAX]; + +static int msm_routing_send_device_pp_params(int port_id, int copp_idx, + int fe_id); + +static int msm_routing_get_bit_width(unsigned int format) +{ + int bit_width; + + switch (format) { + case SNDRV_PCM_FORMAT_S32_LE: + bit_width = 32; + break; + 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_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_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 + * Track LSM listen sessions + */ +static struct msm_pcm_routing_fdai_data + fe_dai_map[MSM_FRONTEND_DAI_MAX][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} } }, + /* MULTIMEDIA20 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* CS_VOICE */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* VOIP */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* AFE_RX */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* AFE_TX */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* VOICE_STUB */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* VOLTE */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* DTMF_RX */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* VOICE2 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* QCHAT */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* VOLTE_STUB */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* LSM1 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* LSM2 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* LSM3 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* LSM4 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* LSM5 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* LSM6 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* LSM7 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* LSM8 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* VOICE2_STUB */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* VOWLAN */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* VOICEMMODE1 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* VOICEMMODE2 */ + {{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_MAX][2] + [MSM_BACKEND_DAI_MAX]; +static struct msm_pcm_routing_app_type_data app_type_cfg[MAX_APP_TYPES]; +static struct msm_pcm_routing_app_type_data lsm_app_type_cfg[MAX_APP_TYPES]; +static struct msm_pcm_stream_app_type_cfg + fe_dai_app_type_cfg[MSM_FRONTEND_DAI_MAX][2][MSM_BACKEND_DAI_MAX]; + +static int last_be_id_configured[MSM_FRONTEND_DAI_MAX][MAX_SESSION_TYPES]; + +/* 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; +} + +static int msm_pcm_routing_get_lsm_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 (lsm_app_type_cfg[idx].app_type == app_type) + return idx; + } + pr_debug("%s: App type not available, fallback to default\n", __func__); + return 0; +} + +static bool is_mm_lsm_fe_id(int fe_id) +{ + bool rc = true; + + if (fe_id > MSM_FRONTEND_DAI_MM_MAX_ID && + ((fe_id < MSM_FRONTEND_DAI_LSM1) || + (fe_id > MSM_FRONTEND_DAI_LSM8))) { + rc = false; + } + return rc; +} + +int msm_pcm_routing_reg_stream_app_type_cfg( + int fedai_id, int session_type, int be_id, + struct msm_pcm_stream_app_type_cfg *cfg_data) +{ + int ret = 0; + + if (cfg_data == NULL) { + pr_err("%s: Received NULL pointer for cfg_data\n", __func__); + ret = -EINVAL; + goto done; + } + + pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fedai_id, session_type, be_id, + cfg_data->app_type, cfg_data->acdb_dev_id, + cfg_data->sample_rate); + + if (!is_mm_lsm_fe_id(fedai_id)) { + pr_err("%s: Invalid machine driver ID %d\n", + __func__, fedai_id); + ret = -EINVAL; + goto done; + } + 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; + } + if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: Received out of bounds be_id %d\n", + __func__, be_id); + ret = -EINVAL; + goto done; + } + + fe_dai_app_type_cfg[fedai_id][session_type][be_id] = *cfg_data; + + /* + * Store the BE ID of the configuration information set as the latest so + * the get mixer control knows what to return. + */ + last_be_id_configured[fedai_id][session_type] = be_id; + +done: + return ret; +} +EXPORT_SYMBOL(msm_pcm_routing_reg_stream_app_type_cfg); + +/** + * msm_pcm_routing_get_stream_app_type_cfg + * + * Receives fedai_id, session_type, be_id, 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 + * be_id - Returned value, back end device id the app type config data is for + * cfg_data - Returned value, configuration data used by app type config + */ +int msm_pcm_routing_get_stream_app_type_cfg( + int fedai_id, int session_type, int *bedai_id, + struct msm_pcm_stream_app_type_cfg *cfg_data) +{ + int be_id; + int ret = 0; + + if (bedai_id == NULL) { + pr_err("%s: Received NULL pointer for backend ID\n", __func__); + ret = -EINVAL; + goto done; + } else if (cfg_data == NULL) { + pr_err("%s: NULL pointer sent for cfg_data\n", __func__); + ret = -EINVAL; + goto done; + } else if (!is_mm_lsm_fe_id(fedai_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; + } + + be_id = last_be_id_configured[fedai_id][session_type]; + if (be_id < 0 || be_id >= MSM_BACKEND_DAI_MAX) { + pr_err("%s: Invalid BE ID %d\n", __func__, be_id); + ret = -EINVAL; + goto done; + } + + *bedai_id = be_id; + *cfg_data = fe_dai_app_type_cfg[fedai_id][session_type][be_id]; + pr_debug("%s: fedai_id %d, session_type %d, be_id %d, app_type %d, acdb_dev_id %d, sample_rate %d\n", + __func__, fedai_id, session_type, *bedai_id, + cfg_data->app_type, cfg_data->acdb_dev_id, + cfg_data->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) +{ + 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)) { + return cal_block; + } + } + pr_debug("%s: Can't find topology for path %d, app %d, acdb_id %d defaulting to search by path\n", + __func__, path, app_type, acdb_id); + return msm_routing_find_topology_by_path(path); +} + +static int msm_routing_get_adm_topology(int fedai_id, int session_type, + int be_id) +{ + int topology = NULL_COPP_TOPOLOGY; + struct cal_block_data *cal_block = NULL; + int app_type = 0, acdb_dev_id = 0; + + + pr_debug("%s: fedai_id %d, session_type %d, be_id %d\n", + __func__, fedai_id, session_type, be_id); + + if (cal_data == NULL) + goto done; + + mutex_lock(&cal_data->lock); + + app_type = fe_dai_app_type_cfg[fedai_id][session_type][be_id].app_type; + acdb_dev_id = + fe_dai_app_type_cfg[fedai_id][session_type][be_id].acdb_dev_id; + + cal_block = msm_routing_find_topology(session_type, app_type, + acdb_dev_id); + 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, + uint32_t passthr_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[0]))) { + 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; + payload.app_type[num_copps] = + fe_dai_app_type_cfg + [fedai_id][sess_type][i] + .app_type; + payload.acdb_dev_id[num_copps] = + fe_dai_app_type_cfg + [fedai_id][sess_type][i] + .acdb_dev_id; + payload.sample_rate[num_copps] = + fe_dai_app_type_cfg + [fedai_id][sess_type][i] + .sample_rate; + num_copps++; + } + } + } + } + + if (num_copps) { + payload.num_copps = num_copps; + payload.session_id = fe_dai_map[fedai_id][sess_type].strm_id; + adm_matrix_map(path_type, payload, perf_mode, passthr_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[0]))) { + 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); +} + +static bool route_check_fe_id_adm_support(int fe_id) +{ + bool rc = true; + + if ((fe_id >= MSM_FRONTEND_DAI_LSM1) && + (fe_id <= MSM_FRONTEND_DAI_LSM8)) { + /* fe id is listen while port is set to afe */ + if (lsm_port_index != ADM_LSM_PORT_INDEX) { + pr_debug("%s: fe_id %d, lsm mux slim port %d\n", + __func__, fe_id, lsm_port_index); + rc = false; + } + } + + return rc; +} + +int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode, + int dspst_id, int stream_type, + uint32_t passthr_mode) +{ + int i, j, session_type, path_type, port_type, topology; + int num_copps = 0; + struct route_payload payload; + u32 channels, sample_rate; + u16 bit_width = 16; + bool is_lsm; + + 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, passthr_mode); + if (!is_mm_lsm_fe_id(fe_id)) { + /* bad ID assigned in machine driver */ + pr_err("%s: bad MM ID %d\n", __func__, fe_id); + return -EINVAL; + } + + if (!route_check_fe_id_adm_support(fe_id)) { + /* ignore adm open if not supported for fe_id */ + pr_debug("%s: No ADM support for fe id %d\n", __func__, fe_id); + return 0; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) { + session_type = SESSION_TYPE_RX; + if (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; + if (passthr_mode != LEGACY_PCM) + path_type = ADM_PATH_COMPRESSED_TX; + else + 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; + } + + is_lsm = (fe_id >= MSM_FRONTEND_DAI_LSM1) && + (fe_id <= MSM_FRONTEND_DAI_LSM8); + 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[0])) + msm_bedais[i].passthr_mode[fe_id] = 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[0]))) { + 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][i].app_type; + if (app_type && is_lsm) { + app_type_idx = + msm_pcm_routing_get_lsm_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[fe_id][session_type][i] + .sample_rate; + bit_width = + lsm_app_type_cfg[app_type_idx].bit_width; + } else 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][i].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][i].acdb_dev_id; + topology = msm_routing_get_adm_topology(fe_id, + session_type, + i); + if ((passthr_mode == COMPRESSED_PASSTHROUGH_DSD) + || (passthr_mode == + COMPRESSED_PASSTHROUGH_GEN)) + topology = COMPRESSED_PASSTHROUGH_NONE_TOPOLOGY; + pr_debug("%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; + payload.app_type[num_copps] = + fe_dai_app_type_cfg + [fe_id][session_type][i] + .app_type; + payload.acdb_dev_id[num_copps] = + fe_dai_app_type_cfg + [fe_id][session_type][i] + .acdb_dev_id; + payload.sample_rate[num_copps] = + fe_dai_app_type_cfg + [fe_id][session_type][i] + .sample_rate; + num_copps++; + } + } + if (passthr_mode != COMPRESSED_PASSTHROUGH_DSD + && passthr_mode != + COMPRESSED_PASSTHROUGH_GEN) { + msm_routing_send_device_pp_params( + msm_bedais[i].port_id, + copp_idx, fe_id); + } + } + } + if (num_copps) { + payload.num_copps = num_copps; + payload.session_id = fe_dai_map[fe_id][session_type].strm_id; + adm_matrix_map(path_type, payload, perf_mode, passthr_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; +} + +static int msm_pcm_routing_channel_mixer(int fe_id, bool perf_mode, + int dspst_id, int stream_type) +{ + int copp_idx = 0; + int sess_type = 0; + int i = 0, j = 0, be_id; + int ret = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return 0; + } + + if (!(channel_mixer[fe_id].enable)) { + pr_debug("%s: channel mixer not enabled for FE %d\n", + __func__, fe_id); + return 0; + } + + if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) + sess_type = SESSION_TYPE_RX; + else + sess_type = SESSION_TYPE_TX; + + for (i = 0; i < ADM_MAX_CHANNELS && channel_input[fe_id][i] > 0; + ++i) { + be_id = channel_input[fe_id][i] - 1; + channel_mixer[fe_id].input_channels[i] = + msm_bedais[be_id].channel; + + if ((msm_bedais[be_id].active) && + test_bit(fe_id, + &msm_bedais[be_id].fe_sessions[0])) { + unsigned long copp = + session_copp_map[fe_id][sess_type][be_id]; + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + if (test_bit(j, &copp)) { + copp_idx = j; + break; + } + } + + pr_debug("%s: fe %d, be %d, channel %d, copp %d\n", + __func__, + fe_id, be_id, msm_bedais[be_id].channel, + copp_idx); + ret = adm_programable_channel_mixer( + msm_bedais[be_id].port_id, + copp_idx, dspst_id, sess_type, + channel_mixer + fe_id, i); + } + } + + return ret; +} + +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; + uint32_t passthr_mode = LEGACY_PCM; + int ret = 0; + + 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[0]))) { + 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].passthr_mode[fedai_id] = + 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][i].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][i] + .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][i] + .acdb_dev_id; + topology = msm_routing_get_adm_topology(fedai_id, + session_type, + i); + 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; + payload.app_type[num_copps] = + fe_dai_app_type_cfg + [fedai_id][session_type] + [i].app_type; + payload.acdb_dev_id[num_copps] = + fe_dai_app_type_cfg + [fedai_id][session_type] + [i].acdb_dev_id; + payload.sample_rate[num_copps] = + fe_dai_app_type_cfg + [fedai_id][session_type] + [i].sample_rate; + num_copps++; + } + } + if ((perf_mode == LEGACY_PCM_MODE) && + (msm_bedais[i].passthr_mode[fedai_id] == + 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; + adm_matrix_map(path_type, payload, perf_mode, passthr_mode); + msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode); + } + + ret = msm_pcm_routing_channel_mixer(fedai_id, perf_mode, + dspst_id, stream_type); + mutex_unlock(&routing_lock); + return ret; +} + +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 (!is_mm_lsm_fe_id(fedai_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[0]))) { + 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].passthr_mode[fedai_id] == + 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 (!is_mm_lsm_fe_id(fe_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[0])) + 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; + uint32_t passthr_mode; + bool is_lsm; + + pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set); + + if (!is_mm_lsm_fe_id(val)) { + /* recheck FE ID in the mixer control defined in this file */ + pr_err("%s: bad MM ID\n", __func__); + return; + } + + if (!route_check_fe_id_adm_support(val)) { + /* ignore adm open if not supported for fe_id */ + pr_debug("%s: No ADM support for fe id %d\n", __func__, val); + return; + } + + passthr_mode = msm_bedais[reg].passthr_mode[val]; + if (afe_get_port_type(msm_bedais[reg].port_id) == + MSM_AFE_PORT_TYPE_RX) { + session_type = SESSION_TYPE_RX; + if (passthr_mode != LEGACY_PCM) + path_type = ADM_PATH_COMPRESSED_RX; + else + path_type = ADM_PATH_PLAYBACK; + } else { + session_type = SESSION_TYPE_TX; + if (passthr_mode != LEGACY_PCM) + path_type = ADM_PATH_COMPRESSED_TX; + else + path_type = ADM_PATH_LIVE_REC; + } + is_lsm = (val >= MSM_FRONTEND_DAI_LSM1) && + (val <= MSM_FRONTEND_DAI_LSM8); + + mutex_lock(&routing_lock); + if (set) { + if (!test_bit(val, &msm_bedais[reg].fe_sessions[0]) && + ((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[0]); + 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][reg].app_type; + if (app_type && is_lsm) { + app_type_idx = + msm_pcm_routing_get_lsm_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[val][session_type][reg] + .sample_rate; + bits_per_sample = + lsm_app_type_cfg[app_type_idx].bit_width; + } else 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][reg] + .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(val, + session_type, + reg); + acdb_dev_id = + fe_dai_app_type_cfg[val][session_type][reg].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, + passthr_mode); + if ((fdai->perf_mode == LEGACY_PCM_MODE) && + (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[0]) && + ((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[0]); + 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) && + (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, + passthr_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[0])) + 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 int msm_routing_get_listen_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[0])) + 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_listen_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; + + pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift, + ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0]) { + if (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]) { + if (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; + struct media_format_info voc_be_media_format; + + 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[0]); + else + clear_bit(val, &msm_bedais[reg].fe_sessions[0]); + + 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); + + memset(&voc_be_media_format, 0, + sizeof(struct media_format_info)); + + voc_be_media_format.port_id = msm_bedais[reg].port_id; + voc_be_media_format.num_channels = + msm_bedais[reg].channel; + voc_be_media_format.sample_rate = + msm_bedais[reg].sample_rate; + voc_be_media_format.bits_per_sample = + msm_bedais[reg].format; + /* Defaulting this to 1 for voice call usecases */ + voc_be_media_format.channel_mapping[0] = 1; + + voc_set_device_config(session_id, path_type, + &voc_be_media_format); + + 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[0])) + 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[0])) + 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[0]); + 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[0]); + 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; +} + +/* + * Return the mapping between port ID and backend ID to enable the AFE callback + * to determine the acdb_dev_id from the port id + */ +int msm_pcm_get_be_id_from_port_id(int port_id) +{ + int i; + int be_id = -EINVAL; + + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + if (msm_bedais[i].port_id == port_id) { + be_id = i; + break; + } + } + + return be_id; +} + +/* + * Return the registered dev_acdb_id given a port ID to enable identifying the + * correct AFE calibration information by comparing the header information. + */ +static int msm_pcm_get_dev_acdb_id_by_port_id(int port_id) +{ + int acdb_id = -EINVAL; + int i = 0; + int session; + int port_type = afe_get_port_type(port_id); + int be_id = msm_pcm_get_be_id_from_port_id(port_id); + + pr_debug("%s:port_id %d be_id %d, port_type 0x%x\n", + __func__, port_id, be_id, port_type); + + if (port_type == MSM_AFE_PORT_TYPE_TX) { + session = SESSION_TYPE_TX; + } else if (port_type == MSM_AFE_PORT_TYPE_RX) { + session = SESSION_TYPE_RX; + } else { + pr_err("%s: Invalid port type %d\n", __func__, port_type); + acdb_id = -EINVAL; + goto exit; + } + + if (be_id < 0) { + pr_err("%s: Error getting backend id %d\n", __func__, be_id); + goto exit; + } + + mutex_lock(&routing_lock); + i = find_first_bit(&msm_bedais[be_id].fe_sessions[0], + MSM_FRONTEND_DAI_MAX); + if (i < MSM_FRONTEND_DAI_MAX) + acdb_id = fe_dai_app_type_cfg[i][session][be_id].acdb_dev_id; + + pr_debug("%s: FE[%d] session[%d] BE[%d] acdb_id(%d)\n", + __func__, i, session, be_id, acdb_id); + mutex_unlock(&routing_lock); +exit: + return acdb_id; +} + +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_int0_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = int0_mi2s_switch_enable; + pr_debug("%s: INT0 MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_int0_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: INT0 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); + int0_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_int4_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = int4_mi2s_switch_enable; + pr_debug("%s: INT4 MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_int4_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: INT4 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); + int4_mi2s_switch_enable = ucontrol->value.integer.value[0]; + return 1; +} + +static int msm_routing_get_usb_switch_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = usb_switch_enable; + pr_debug("%s: HFP Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_usb_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: USB 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); + usb_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_port_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = lsm_port_index; + return 0; +} + +static int msm_routing_lsm_port_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + 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; + + if (mux >= e->items) { + pr_err("%s: Invalid mux value %d\n", __func__, mux); + return -EINVAL; + } + + 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; + case 8: + lsm_port = AFE_PORT_ID_QUATERNARY_MI2S_TX; + break; + case 9: + lsm_port = ADM_LSM_PORT_ID; + break; + case 10: + lsm_port = AFE_PORT_ID_INT3_MI2S_TX; + break; + default: + pr_err("Default lsm port"); + break; + } + set_lsm_port(lsm_port); + lsm_port_index = 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(lsm_port_text); i++) + if (!strnstr(kcontrol->id.name, lsm_port_text[i], + strlen(lsm_port_text[i]))) + break; + + if (i-- == ARRAY_SIZE(lsm_port_text)) { + WARN(1, "Invalid id name %s\n", kcontrol->id.name); + return -EINVAL; + } + + port_id = i * 2 + 1 + SLIMBUS_0_RX; + + /*Check for Tertiary/Quaternary/INT3 TX port*/ + if (strnstr(kcontrol->id.name, lsm_port_text[7], + strlen(lsm_port_text[7]))) + port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + + if (strnstr(kcontrol->id.name, lsm_port_text[8], + strlen(lsm_port_text[8]))) + port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + + if (strnstr(kcontrol->id.name, lsm_port_text[10], + strlen(lsm_port_text[10]))) + port_id = AFE_PORT_ID_INT3_MI2S_TX; + + 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(lsm_port_text); i++) + if (strnstr(kcontrol->id.name, lsm_port_text[i], + strlen(lsm_port_text[i]))) + break; + + if (i-- == ARRAY_SIZE(lsm_port_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/Quaternary/INT3 TX port*/ + if (strnstr(kcontrol->id.name, lsm_port_text[7], + strlen(lsm_port_text[7]))) + port_id = AFE_PORT_ID_TERTIARY_MI2S_TX; + + if (strnstr(kcontrol->id.name, lsm_port_text[8], + strlen(lsm_port_text[8]))) + port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; + + if (strnstr(kcontrol->id.name, lsm_port_text[10], + strlen(lsm_port_text[10]))) + port_id = AFE_PORT_ID_INT3_MI2S_TX; + + 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_pcm_get_channel_rule_index(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = channel_mixer[fe_id].rule; + + return 0; +} + +static int msm_pcm_put_channel_rule_index(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + channel_mixer[fe_id].rule = ucontrol->value.integer.value[0]; + + return 1; +} + +static int msm_pcm_get_out_chs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + ucontrol->value.integer.value[0] = + channel_mixer[fe_id].output_channel; + return 0; +} + +static int msm_pcm_put_out_chs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + pr_debug("%s: fe_id is %d, output channels = %d\n", __func__, + fe_id, + (unsigned int)(ucontrol->value.integer.value[0])); + channel_mixer[fe_id].output_channel = + (unsigned int)(ucontrol->value.integer.value[0]); + + return 1; +} + +static const char *const ch_mixer[] = {"Disable", "Enable"}; + +/* If new backend is added, need update this array */ +static const char *const be_name[] = { +"ZERO", "PRI_I2S_RX", "PRI_I2S_TX", "SLIM_0_RX", +"SLIM_0_TX", "HDMI_RX", "INT_BT_SCO_RX", "INT_BT_SCO_TX", +"INT_FM_RX", "INT_FM_TX", "AFE_PCM_RX", "AFE_PCM_TX", +"AUXPCM_RX", "AUXPCM_TX", "VOICE_PLAYBACK_TX", "VOICE2_PLAYBACK_TX", +"INCALL_RECORD_RX", "INCALL_RECORD_TX", "MI2S_RX", "MI2S_TX", +"SEC_I2S_RX", "SLIM_1_RX", "SLIM_1_TX", "SLIM_2_RX", +"SLIM_2_TX", "SLIM_3_RX", "SLIM_3_TX", "SLIM_4_RX", +"SLIM_4_TX", "SLIM_5_RX", "SLIM_5_TX", "SLIM_6_RX", +"SLIM_6_TX", "SLIM_7_RX", "SLIM_7_TX", "SLIM_8_RX", +"SLIM_8_TX", "EXTPROC_RX", "EXTPROC_TX", "EXPROC_EC_TX", +"QUAT_MI2S_RX", "QUAT_MI2S_TX", "SECOND_MI2S_RX", "SECOND_MI2S_TX", +"PRI_MI2S_RX", "PRI_MI2S_TX", "TERT_MI2S_RX", "TERT_MI2S_TX", +"AUDIO_I2S_RX", "SEC_AUXPCM_RX", "SEC_AUXPCM_TX", "SPDIF_RX", +"SECOND_MI2S_RX_SD1", "QUIN_MI2S_RX", "QUIN_MI2S_TX", "SENARY_MI2S_TX", +"PRI_TDM_RX_0", "PRI_TDM_TX_0", "PRI_TDM_RX_1", "PRI_TDM_TX_1", +"PRI_TDM_RX_2", "PRI_TDM_TX_2", "PRI_TDM_RX_3", "PRI_TDM_TX_3", +"PRI_TDM_RX_4", "PRI_TDM_TX_4", "PRI_TDM_RX_5", "PRI_TDM_TX_5", +"PRI_TDM_RX_6", "PRI_TDM_TX_6", "PRI_TDM_RX_7", "PRI_TDM_TX_7", +"SEC_TDM_RX_0", "SEC_TDM_TX_0", "SEC_TDM_RX_1", "SEC_TDM_TX_1", +"SEC_TDM_RX_2", "SEC_TDM_TX_2", "SEC_TDM_RX_3", "SEC_TDM_TX_3", +"SEC_TDM_RX_4", "SEC_TDM_TX_4", "SEC_TDM_RX_5", "SEC_TDM_TX_5", +"SEC_TDM_RX_6", "SEC_TDM_TX_6", "SEC_TDM_RX_7", "SEC_TDM_TX_7", +"TERT_TDM_RX_0", "TERT_TDM_TX_0", "TERT_TDM_RX_1", "TERT_TDM_TX_1", +"TERT_TDM_RX_2", "TERT_TDM_TX_2", "TERT_TDM_RX_3", "TERT_TDM_TX_3", +"TERT_TDM_RX_4", "TERT_TDM_TX_4", "TERT_TDM_RX_5", "TERT_TDM_TX_5", +"TERT_TDM_RX_6", "TERT_TDM_TX_6", "TERT_TDM_RX_7", "TERT_TDM_TX_7", +"QUAT_TDM_RX_0", "QUAT_TDM_TX_0", "QUAT_TDM_RX_1", "QUAT_TDM_TX_1", +"QUAT_TDM_RX_2", "QUAT_TDM_TX_2", "QUAT_TDM_RX_3", "QUAT_TDM_TX_3", +"QUAT_TDM_RX_4", "QUAT_TDM_TX_4", "QUAT_TDM_RX_5", "QUAT_TDM_TX_5", +"QUAT_TDM_RX_6", "QUAT_TDM_TX_6", "QUAT_TDM_RX_7", "QUAT_TDM_TX_7", +"INT_BT_A2DP_RX", "USB_RX", "USB_TX", "DISPLAY_PORT_RX", +"TERT_AUXPCM_RX", "TERT_AUXPCM_TX", "QUAT_AUXPCM_RX", "QUAT_AUXPCM_TX", +"INT0_MI2S_RX", "INT0_MI2S_TX", "INT1_MI2S_RX", "INT1_MI2S_TX", +"INT2_MI2S_RX", "INT2_MI2S_TX", "INT3_MI2S_RX", "INT3_MI2S_TX", +"INT4_MI2S_RX", "INT4_MI2S_TX", "INT5_MI2S_RX", "INT5_MI2S_TX", +"INT6_MI2S_RX", "INT6_MI2S_TX" +}; + +static SOC_ENUM_SINGLE_DECL(mm1_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, ch_mixer); +static SOC_ENUM_SINGLE_DECL(mm2_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA2, ch_mixer); +static SOC_ENUM_SINGLE_DECL(mm3_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA3, ch_mixer); +static SOC_ENUM_SINGLE_DECL(mm4_channel_mux, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA4, ch_mixer); + +static SOC_ENUM_DOUBLE_DECL(mm1_ch1_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 0, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch2_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 1, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch3_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 2, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch4_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 3, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch5_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 4, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch6_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 5, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch7_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 6, be_name); +static SOC_ENUM_DOUBLE_DECL(mm1_ch8_enum, + SND_SOC_NOPM, MSM_FRONTEND_DAI_MULTIMEDIA1, 7, be_name); + +static int msm_pcm_get_ctl_enum_info(struct snd_ctl_elem_info *uinfo, + unsigned int channels, + unsigned int items, const char *const names[]) +{ + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + + WARN(strlen(names[uinfo->value.enumerated.item]) >= + sizeof(uinfo->value.enumerated.name), + "ALSA: too long item name '%s'\n", + names[uinfo->value.enumerated.item]); + strlcpy(uinfo->value.enumerated.name, + names[uinfo->value.enumerated.item], + sizeof(uinfo->value.enumerated.name)); + return 0; +} + +static int msm_pcm_channel_mixer_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + uinfo->value.enumerated.items = ARRAY_SIZE(ch_mixer); + msm_pcm_get_ctl_enum_info(uinfo, 1, e->items, e->texts); + + return 0; +} +static int msm_pcm_channel_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_enum *) + kcontrol->private_value)->shift_l; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + pr_debug("%s: FE %d %s\n", __func__, + fe_id, + channel_mixer[fe_id].enable ? "Enabled" : "Disabled"); + ucontrol->value.enumerated.item[0] = channel_mixer[fe_id].enable; + return 0; +} + +static int msm_pcm_channel_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0; + + fe_id = ((struct soc_enum *) + kcontrol->private_value)->shift_l; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + + channel_mixer[fe_id].enable = ucontrol->value.enumerated.item[0]; + pr_debug("%s: %s FE %d\n", __func__, + channel_mixer[fe_id].enable ? "Enable" : "Disable", + fe_id); + return 0; +} + +static int msm_pcm_channel_input_be_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + + uinfo->value.enumerated.items = ARRAY_SIZE(be_name); + msm_pcm_get_ctl_enum_info(uinfo, 1, e->items, e->texts); + + return 0; +} + +static int msm_pcm_channel_input_be_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + u16 fe_id = 0, in_ch = 0; + + fe_id = e->shift_l; + in_ch = e->shift_r; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (in_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, in_ch); + return -EINVAL; + } + + channel_input[fe_id][in_ch] = ucontrol->value.enumerated.item[0]; + return 1; +} + +static int msm_pcm_channel_input_be_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + u16 fe_id = 0, in_ch = 0; + + fe_id = e->shift_l; + in_ch = e->shift_r; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (in_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, in_ch); + return -EINVAL; + } + + ucontrol->value.enumerated.item[0] = channel_input[fe_id][in_ch]; + return 1; +} + + +static int msm_pcm_channel_weight_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = ADM_MAX_CHANNELS; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = WEIGHT_0_DB; + + return 0; +} + +static int msm_pcm_channel_weight_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0, out_ch = 0; + int i, weight; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + out_ch = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->rshift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (out_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, out_ch); + return -EINVAL; + } + + pr_debug("%s: FE_ID: %d, channel weight %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld\n", + __func__, fe_id, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1], + ucontrol->value.integer.value[2], + ucontrol->value.integer.value[3], + ucontrol->value.integer.value[4], + ucontrol->value.integer.value[5], + ucontrol->value.integer.value[6], + ucontrol->value.integer.value[7]); + + for (i = 0; i < ADM_MAX_CHANNELS; ++i) { + weight = ucontrol->value.integer.value[i]; + channel_mixer[fe_id].channel_weight[out_ch][i] = weight; + pr_debug("%s: FE_ID %d, output %d input %d weight %d\n", + __func__, fe_id, out_ch, i, + channel_mixer[fe_id].channel_weight[out_ch][i]); + } + + return 0; +} + +static int msm_pcm_channel_weight_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 fe_id = 0, out_ch = 0; + int i; + + fe_id = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + out_ch = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->rshift; + if (fe_id >= MSM_FRONTEND_DAI_MM_SIZE) { + pr_err("%s: invalid FE %d\n", __func__, fe_id); + return -EINVAL; + } + if (out_ch >= ADM_MAX_CHANNELS) { + pr_err("%s: invalid input channel %d\n", __func__, out_ch); + return -EINVAL; + } + + for (i = 0; i < ADM_MAX_CHANNELS; ++i) + ucontrol->value.integer.value[i] = + channel_mixer[fe_id].channel_weight[out_ch][i]; + + pr_debug("%s: FE_ID: %d, weight %ld, %ld, %ld, %ld, %ld, %ld, %ld, %ld", + __func__, fe_id, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1], + ucontrol->value.integer.value[2], + ucontrol->value.integer.value[3], + ucontrol->value.integer.value[4], + ucontrol->value.integer.value[5], + ucontrol->value.integer.value[6], + ucontrol->value.integer.value[7]); + + return 0; +} + +static const struct snd_kcontrol_new channel_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia2 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA2, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia3 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA3, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia4 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA4, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia5 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA5, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + SOC_SINGLE_EXT("MultiMedia6 Channel Rule", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA6, 8, 0, + msm_pcm_get_channel_rule_index, + msm_pcm_put_channel_rule_index), + + SOC_SINGLE_EXT("MultiMedia1 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA1, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia2 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA2, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia3 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA3, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia4 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA4, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia5 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA5, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + SOC_SINGLE_EXT("MultiMedia6 Channels", SND_SOC_NOPM, + MSM_FRONTEND_DAI_MULTIMEDIA6, 8, 0, + msm_pcm_get_out_chs, + msm_pcm_put_out_chs), + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm1_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm2_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia3 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm3_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia4 Channel Mixer", + .info = msm_pcm_channel_mixer_info, + .get = msm_pcm_channel_mixer_get, + .put = msm_pcm_channel_mixer_put, + .private_value = (unsigned long)&(mm4_channel_mux) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel1", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 0,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel2", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 1, } + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel3", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 2,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel4", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 3,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel5", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 4,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel6", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 5,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel7", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 6,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Output Channel8", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + { .shift = MSM_FRONTEND_DAI_MULTIMEDIA1, .rshift = 7,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Output Channel1", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 0,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Output Channel2", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 1,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia2 Output Channel3", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA2, .rshift = 2,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia3 Output Channel1", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA3, .rshift = 0,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia3 Output Channel2", + .info = msm_pcm_channel_weight_info, + .get = msm_pcm_channel_weight_get, + .put = msm_pcm_channel_weight_put, + .private_value = (unsigned long)&(struct soc_multi_mixer_control) + {.shift = MSM_FRONTEND_DAI_MULTIMEDIA3, .rshift = 1,} + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel1", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch1_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel2", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch2_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel3", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch3_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel4", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch4_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel5", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch5_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel6", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch6_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel7", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch7_enum) + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .name = "MultiMedia1 Channel8", + .info = msm_pcm_channel_input_be_info, + .get = msm_pcm_channel_input_be_get, + .put = msm_pcm_channel_input_be_put, + .private_value = (unsigned long)&(mm1_ch8_enum) + }, +}; +static int msm_ec_ref_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_ec_ref_ch; + pr_debug("%s: msm_ec_ref_ch = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_ec_ref_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + msm_ec_ref_ch = ucontrol->value.integer.value[0]; + pr_debug("%s: msm_ec_ref_ch = %d\n", __func__, msm_ec_ref_ch); + adm_num_ec_ref_rx_chans(msm_ec_ref_ch); + return 0; +} + +static const char *const ec_ref_ch_text[] = {"Zero", "One", "Two", "Three", + "Four", "Five", "Six", "Seven", "Eight"}; + +static int msm_ec_ref_bit_format_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (msm_ec_ref_bit_format) { + case SNDRV_PCM_FORMAT_S24_LE: + ucontrol->value.integer.value[0] = 2; + break; + case SNDRV_PCM_FORMAT_S16_LE: + ucontrol->value.integer.value[0] = 1; + break; + default: + ucontrol->value.integer.value[0] = 0; + break; + } + pr_debug("%s: msm_ec_ref_bit_format = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_ec_ref_bit_format_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u16 bit_width = 0; + + switch (ucontrol->value.integer.value[0]) { + case 2: + msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S24_LE; + break; + case 1: + msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE; + break; + default: + msm_ec_ref_bit_format = 0; + break; + } + + if (msm_ec_ref_bit_format == SNDRV_PCM_FORMAT_S16_LE) + bit_width = 16; + else if (msm_ec_ref_bit_format == SNDRV_PCM_FORMAT_S24_LE) + bit_width = 24; + + pr_debug("%s: msm_ec_ref_bit_format = %d\n", + __func__, msm_ec_ref_bit_format); + adm_ec_ref_rx_bit_width(bit_width); + return 0; +} + +static char const *ec_ref_bit_format_text[] = {"0", "S16_LE", "S24_LE"}; + +static int msm_ec_ref_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_ec_ref_sampling_rate; + pr_debug("%s: msm_ec_ref_sampling_rate = %ld\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_ec_ref_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + msm_ec_ref_sampling_rate = 0; + break; + case 1: + msm_ec_ref_sampling_rate = 8000; + break; + case 2: + msm_ec_ref_sampling_rate = 16000; + break; + case 3: + msm_ec_ref_sampling_rate = 32000; + break; + case 4: + msm_ec_ref_sampling_rate = 44100; + break; + case 5: + msm_ec_ref_sampling_rate = 48000; + break; + case 6: + msm_ec_ref_sampling_rate = 96000; + break; + case 7: + msm_ec_ref_sampling_rate = 192000; + break; + case 8: + msm_ec_ref_sampling_rate = 384000; + break; + default: + msm_ec_ref_sampling_rate = 48000; + break; + } + pr_debug("%s: msm_ec_ref_sampling_rate = %d\n", + __func__, msm_ec_ref_sampling_rate); + adm_ec_ref_rx_sampling_rate(msm_ec_ref_sampling_rate); + return 0; +} + +static const char *const ec_ref_rate_text[] = {"0", "8000", "16000", + "32000", "44100", "48000", "96000", "192000", "384000"}; + +static const struct soc_enum msm_route_ec_ref_params_enum[] = { + SOC_ENUM_SINGLE_EXT(9, ec_ref_ch_text), + SOC_ENUM_SINGLE_EXT(3, ec_ref_bit_format_text), + SOC_ENUM_SINGLE_EXT(9, ec_ref_rate_text), +}; + +static const struct snd_kcontrol_new ec_ref_param_controls[] = { + SOC_ENUM_EXT("EC Reference Channels", msm_route_ec_ref_params_enum[0], + msm_ec_ref_ch_get, msm_ec_ref_ch_put), + SOC_ENUM_EXT("EC Reference Bit Format", msm_route_ec_ref_params_enum[1], + msm_ec_ref_bit_format_get, msm_ec_ref_bit_format_put), + SOC_ENUM_EXT("EC Reference SampleRate", msm_route_ec_ref_params_enum[2], + msm_ec_ref_rate_get, msm_ec_ref_rate_put), +}; + +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]; + 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; + case 20: + msm_route_ec_ref_rx = 20; + ec_ref_port_id = AFE_PORT_ID_INT0_MI2S_RX; + break; + case 21: + msm_route_ec_ref_rx = 21; + ec_ref_port_id = AFE_PORT_ID_INT4_MI2S_RX; + break; + case 22: + msm_route_ec_ref_rx = 22; + ec_ref_port_id = AFE_PORT_ID_INT3_MI2S_TX; + 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, + msm_route_ec_ref_rx, 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", + "INT0_MI2S_RX", "INT4_MI2S_RX", "INT3_MI2S_TX"}; + +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; + + if (mux >= e->items) { + pr_err("%s: Invalid mux value %d\n", __func__, mux); + return -EINVAL; + } + + 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_port_id(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_rx_1_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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 pri_tdm_rx_2_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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 pri_tdm_rx_3_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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 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_rx_1_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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 sec_tdm_rx_2_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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 sec_tdm_rx_3_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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 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 tert_tdm_rx_4_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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_4, + 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), + SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 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), + SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 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), + SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 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), + SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 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("PRI_TDM_TX_0", 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("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", 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("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3, + 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("SLIM_6_TX", MSM_BACKEND_DAI_SLIMBUS_6_TX, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_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("PRI_TDM_TX_0", 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("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", 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("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3, + 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("PRI_TDM_TX_0", 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("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", 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("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3, + 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("PRI_TDM_TX_0", 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("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", 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("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3, + 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("PRI_TDM_TX_0", 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("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", 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("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3, + 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("PRI_TDM_TX_0", 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("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", 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("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3, + 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("PRI_TDM_TX_0", 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("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", 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("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3, + 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 mmul9_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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_MULTIMEDIA9, 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("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_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 mmul20_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA20, 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_MULTIMEDIA20, 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_MULTIMEDIA20, 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_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 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_MULTIMEDIA20, 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_MULTIMEDIA20, 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_MULTIMEDIA20, 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_MULTIMEDIA20, 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_MULTIMEDIA20, 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_MULTIMEDIA20, 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_MULTIMEDIA20, 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_MULTIMEDIA20, 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 quat_tdm_rx_2_voice_mixer_controls[] = { + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_VOICEMMODE1, 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), + SOC_SINGLE_EXT("QUAT_TDM_TX_0_MMode1", + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 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 int0_mi2s_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_INT0_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_INT0_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_INT0_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_INT0_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_INT3_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT0_MI2S_RX, + 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_INT0_MI2S_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_INT0_MI2S_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_INT0_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new int4_mi2s_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_INT4_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_INT4_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_INT4_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_INT4_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_INT3_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT4_MI2S_RX, + 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_INT4_MI2S_RX, + MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_INT4_MI2S_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_INT4_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_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), + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_BACKEND_DAI_QUAT_TDM_TX_0, 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), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new usb_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_RX, + MSM_BACKEND_DAI_USB_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), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new pri_tdm_rx_0_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_TDM_RX_0, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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 pri_tdm_rx_1_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_TDM_RX_1, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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 pri_tdm_rx_2_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_TDM_RX_2, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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 pri_tdm_rx_3_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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_PRI_TDM_RX_3, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_PRI_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_PRI_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_PRI_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_PRI_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_PRI_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_PRI_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_PRI_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_PRI_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 sec_tdm_rx_0_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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 sec_tdm_rx_1_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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 sec_tdm_rx_2_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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 sec_tdm_rx_3_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_SEC_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_SEC_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_SEC_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_SEC_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_SEC_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_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), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_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), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + +static const struct snd_kcontrol_new lsm1_mixer_controls[] = { + SOC_SINGLE_EXT("SLIMBUS_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_4_TX", MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_5_TX", MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM1, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm2_mixer_controls[] = { + SOC_SINGLE_EXT("SLIMBUS_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_4_TX", MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_5_TX", MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM2, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm3_mixer_controls[] = { + SOC_SINGLE_EXT("SLIMBUS_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_4_TX", MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_5_TX", MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM3, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm4_mixer_controls[] = { + SOC_SINGLE_EXT("SLIMBUS_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_4_TX", MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_5_TX", MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM4, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm5_mixer_controls[] = { + SOC_SINGLE_EXT("SLIMBUS_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_4_TX", MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_5_TX", MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM5, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm6_mixer_controls[] = { + SOC_SINGLE_EXT("SLIMBUS_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_4_TX", MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_5_TX", MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM6, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm7_mixer_controls[] = { + SOC_SINGLE_EXT("SLIMBUS_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_4_TX", MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_5_TX", MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM7, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), +}; + +static const struct snd_kcontrol_new lsm8_mixer_controls[] = { + SOC_SINGLE_EXT("SLIMBUS_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_4_TX", MSM_BACKEND_DAI_SLIMBUS_4_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("SLIMBUS_5_TX", MSM_BACKEND_DAI_SLIMBUS_5_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_mixer), + SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_LSM8, 1, 0, msm_routing_get_listen_mixer, + msm_routing_put_listen_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 int0_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_int0_mi2s_switch_mixer, + msm_routing_put_int0_mi2s_switch_mixer); + +static const struct snd_kcontrol_new int4_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_int4_mi2s_switch_mixer, + msm_routing_put_int4_mi2s_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 snd_kcontrol_new hfp_slim7_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 usb_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_usb_switch_mixer, + msm_routing_put_usb_switch_mixer); + +static const struct soc_enum lsm_port_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lsm_port_text), lsm_port_text); + +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_controls[] = { + /* kcontrol of 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), + SOC_ENUM_EXT(QUAT_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + SOC_ENUM_EXT(INT3_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum, + msm_routing_lsm_func_get, msm_routing_lsm_func_put), + /* kcontrol of lsm_port */ + SOC_ENUM_EXT("LSM1 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM2 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM3 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM4 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM5 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM6 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM7 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_put), + SOC_ENUM_EXT("LSM8 Port", lsm_port_enum, + msm_routing_lsm_port_get, + msm_routing_lsm_port_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) && + (port_id != AFE_PORT_ID_INT4_MI2S_RX)) + continue; + + for_each_set_bit(i, &msm_bedais[be_index].fe_sessions[0], + 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 int msm_routing_put_app_type_gain_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int j, fe_id, be_id, port_type; + int ret = 0; + unsigned long copp; + struct msm_pcm_routing_bdai_data *bedai; + int dir = ucontrol->value.integer.value[0] ? SESSION_TYPE_TX : + SESSION_TYPE_RX; + int app_type = ucontrol->value.integer.value[1]; + int gain = (ucontrol->value.integer.value[2] + + ucontrol->value.integer.value[3])/2; + + port_type = (dir == SESSION_TYPE_RX) ? MSM_AFE_PORT_TYPE_RX : + MSM_AFE_PORT_TYPE_TX; + + mutex_lock(&routing_lock); + for (be_id = 0; be_id < MSM_BACKEND_DAI_MAX; be_id++) { + if (is_be_dai_extproc(be_id)) + continue; + + bedai = &msm_bedais[be_id]; + if (afe_get_port_type(bedai->port_id) != port_type) + continue; + + if (!bedai->active) + continue; + + for (fe_id = 0; fe_id < MSM_FRONTEND_DAI_MAX; fe_id++) { + if (!test_bit(fe_id, &bedai->fe_sessions[0])) + continue; + + if (app_type != + fe_dai_app_type_cfg[fe_id][dir][be_id].app_type) + continue; + + copp = session_copp_map[fe_id][dir][be_id]; + for (j = 0; j < MAX_COPPS_PER_PORT; j++) { + if (!test_bit(j, &copp)) + continue; + ret |= adm_set_volume(bedai->port_id, j, gain); + } + } + } + mutex_unlock(&routing_lock); + return ret ? -EINVAL : 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), + SOC_SINGLE_MULTI_EXT("App Type Gain", SND_SOC_NOPM, 0, + 0x2000, 0, 4, NULL, msm_routing_put_app_type_gain_control) +}; + +static int msm_routing_get_lsm_app_type_cfg_control( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int msm_routing_put_lsm_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++]; + + memset(lsm_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++) { + lsm_app_type_cfg[j].app_type = + ucontrol->value.integer.value[i++]; + lsm_app_type_cfg[j].sample_rate = + ucontrol->value.integer.value[i++]; + lsm_app_type_cfg[j].bit_width = + ucontrol->value.integer.value[i++]; + } + + return 0; +} + +static const struct snd_kcontrol_new lsm_app_type_cfg_controls[] = { + SOC_SINGLE_MULTI_EXT("Listen App Type Config", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, 128, msm_routing_get_lsm_app_type_cfg_control, + msm_routing_put_lsm_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 + param_payload_len, 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[0], + 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 if (!strcmp(kcontrol->id.name + strlen(prefix), + "INT3_MI2S")) { + *port_id = AFE_PORT_ID_INT3_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, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Sound Focus Voice Tx INT3_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 INT3_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 INT3_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 INT3_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 char * const int4_mi2s_rx_vi_fb_tx_mono_mux_text[] = { + "ZERO", "INT5_MI2S_TX" +}; + +static const char * const int4_mi2s_rx_vi_fb_tx_stereo_mux_text[] = { + "ZERO", "INT5_MI2S_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 int const int4_mi2s_rx_vi_fb_tx_mono_ch_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_INT5_MI2S_TX +}; + +static const int const int4_mi2s_rx_vi_fb_tx_stereo_ch_value[] = { + MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_INT5_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 soc_enum int4_mi2s_rx_vi_fb_mono_ch_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_INT4_MI2S_RX, 0, 0, + ARRAY_SIZE(int4_mi2s_rx_vi_fb_tx_mono_mux_text), + int4_mi2s_rx_vi_fb_tx_mono_mux_text, + int4_mi2s_rx_vi_fb_tx_mono_ch_value); + +static const struct soc_enum int4_mi2s_rx_vi_fb_stereo_ch_mux_enum = + SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_INT4_MI2S_RX, 0, 0, + ARRAY_SIZE(int4_mi2s_rx_vi_fb_tx_stereo_mux_text), + int4_mi2s_rx_vi_fb_tx_stereo_mux_text, + int4_mi2s_rx_vi_fb_tx_stereo_ch_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_kcontrol_new int4_mi2s_rx_vi_fb_mono_ch_mux = + SOC_DAPM_ENUM_EXT("INT4_MI2S_RX_VI_FB_MONO_CH_MUX", + int4_mi2s_rx_vi_fb_mono_ch_mux_enum, spkr_prot_get_vi_lch_port, + spkr_prot_put_vi_lch_port); + +static const struct snd_kcontrol_new int4_mi2s_rx_vi_fb_stereo_ch_mux = + SOC_DAPM_ENUM_EXT("INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", + int4_mi2s_rx_vi_fb_stereo_ch_mux_enum, spkr_prot_get_vi_rch_port, + spkr_prot_put_vi_rch_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("MM_DL20", "MultiMedia20 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_OUT("MM_UL20", "MultiMedia20 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("SLIM7_DL_HL", "SLIMBUS7_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SLIM7_UL_HL", "SLIMBUS7_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("USBAUDIO_DL_HL", "USBAUDIO_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("USBAUDIO_UL_HL", "USBAUDIO_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("INT3_MI2S_UL_HL", + "INT3 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("INT5_MI2S_TX", "INT5 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("INT0_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &int0_mi2s_rx_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("INT4_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &int4_mi2s_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), + SND_SOC_DAPM_SWITCH("HFP_SLIM7_UL_HL", SND_SOC_NOPM, 0, 0, + &hfp_slim7_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("USB_DL_HL", SND_SOC_NOPM, 0, 0, + &usb_switch_mixer_controls), + + /* 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_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_1_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_2_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_3_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_3_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_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_1_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_2_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_3_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_3_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("TERT_TDM_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + tert_tdm_rx_4_mixer_controls, + ARRAY_SIZE(tert_tdm_rx_4_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("MultiMedia9 Mixer", SND_SOC_NOPM, 0, 0, + mmul9_mixer_controls, ARRAY_SIZE(mmul9_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("MultiMedia20 Mixer", SND_SOC_NOPM, 0, 0, + mmul20_mixer_controls, ARRAY_SIZE(mmul20_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("QUAT_TDM_RX_2_Voice Mixer", + SND_SOC_NOPM, 0, 0, + quat_tdm_rx_2_voice_mixer_controls, + ARRAY_SIZE(quat_tdm_rx_2_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("PRI_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_0_port_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_1_port_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_1_port_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_2_port_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_2_port_mixer_controls)), + SND_SOC_DAPM_MIXER("PRI_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0, + pri_tdm_rx_3_port_mixer_controls, + ARRAY_SIZE(pri_tdm_rx_3_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_0_port_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_1_port_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_1_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_2_port_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_2_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SEC_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0, + sec_tdm_rx_3_port_mixer_controls, + ARRAY_SIZE(sec_tdm_rx_3_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("INT0_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + int0_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(int0_mi2s_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("INT4_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + int4_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(int4_mi2s_rx_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)), + SND_SOC_DAPM_MIXER("USB_AUDIO_RX Port Mixer", + SND_SOC_NOPM, 0, 0, usb_rx_port_mixer_controls, + ARRAY_SIZE(usb_rx_port_mixer_controls)), + /* lsm mixer definitions */ + SND_SOC_DAPM_MIXER("LSM1 Mixer", SND_SOC_NOPM, 0, 0, + lsm1_mixer_controls, ARRAY_SIZE(lsm1_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM2 Mixer", SND_SOC_NOPM, 0, 0, + lsm2_mixer_controls, ARRAY_SIZE(lsm2_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM3 Mixer", SND_SOC_NOPM, 0, 0, + lsm3_mixer_controls, ARRAY_SIZE(lsm3_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM4 Mixer", SND_SOC_NOPM, 0, 0, + lsm4_mixer_controls, ARRAY_SIZE(lsm4_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM5 Mixer", SND_SOC_NOPM, 0, 0, + lsm5_mixer_controls, ARRAY_SIZE(lsm5_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM6 Mixer", SND_SOC_NOPM, 0, 0, + lsm6_mixer_controls, ARRAY_SIZE(lsm6_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM7 Mixer", SND_SOC_NOPM, 0, 0, + lsm7_mixer_controls, ARRAY_SIZE(lsm7_mixer_controls)), + SND_SOC_DAPM_MIXER("LSM8 Mixer", SND_SOC_NOPM, 0, 0, + lsm8_mixer_controls, ARRAY_SIZE(lsm8_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("INT4_MI2S_RX_VI_FB_MONO_CH_MUX", SND_SOC_NOPM, 0, 0, + &int4_mi2s_rx_vi_fb_mono_ch_mux), + SND_SOC_DAPM_MUX("INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", SND_SOC_NOPM, 0, 0, + &int4_mi2s_rx_vi_fb_stereo_ch_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", "MultiMedia2", "MM_DL2"}, + {"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", "MultiMedia6", "MM_DL6"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SLIMBUS_7_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"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"}, + {"MultiMedia18 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia8 Mixer", "INT3_MI2S_TX", "INT3_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_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1 Audio Mixer"}, + + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2 Audio Mixer"}, + + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"PRI_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3 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_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1 Audio Mixer"}, + + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2 Audio Mixer"}, + + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"SEC_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3 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"}, + + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"TERT_TDM_RX_4 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"TERT_TDM_RX_4", NULL, "TERT_TDM_RX_4 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 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 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"}, + + {"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"}, + + {"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 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"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 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"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 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"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_6_TX", "SLIMBUS_6_TX"}, + {"MultiMedia2 Mixer", "SLIM_1_TX", "SLIMBUS_1_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"}, + {"MultiMedia2 Mixer", "SEC_MI2S_TX", "SEC_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"}, + {"MultiMedia6 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + + {"MultiMedia1 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia1 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia1 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia1 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia1 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia1 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia1 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia1 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"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", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia2 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia2 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia2 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia2 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia2 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia2 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia2 Mixer", "SEC_TDM_TX_3", "SEC_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", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia3 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia3 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia3 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia3 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia3 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia3 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia3 Mixer", "SEC_TDM_TX_3", "SEC_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", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia4 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia4 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia4 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia4 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia4 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia4 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia4 Mixer", "SEC_TDM_TX_3", "SEC_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", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia5 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia5 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia5 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia5 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia5 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia5 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia5 Mixer", "SEC_TDM_TX_3", "SEC_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", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia6 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia6 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia6 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia6 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia6 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia6 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia6 Mixer", "SEC_TDM_TX_3", "SEC_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", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia8 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia8 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia8 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia8 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia8 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia8 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia8 Mixer", "SEC_TDM_TX_3", "SEC_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"}, + + {"MultiMedia9 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia9 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia9 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia9 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia9 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia9 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia9 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia9 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + + {"MultiMedia20 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia20 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"MultiMedia20 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia20 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia20 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia20 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia20 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia20 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia20 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia20 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia20 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia20 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia20 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia20 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia20 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia20 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia20 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia20 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia20 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia20 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_UL9", NULL, "MultiMedia9 Mixer"}, + {"MM_UL17", NULL, "MultiMedia17 Mixer"}, + {"MM_UL18", NULL, "MultiMedia18 Mixer"}, + {"MM_UL19", NULL, "MultiMedia19 Mixer"}, + {"MM_UL20", NULL, "MultiMedia20 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"}, + + {"QUAT_TDM_RX_2_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2_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"}, + {"VoWLAN_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_Tx Mixer", "QUAT_TDM_TX_0_MMode1", "QUAT_TDM_TX_0"}, + {"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 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM1 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM1 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM1 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM1 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"LSM1_UL_HL", NULL, "LSM1 Mixer"}, + + {"LSM2 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM2 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM2 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM2 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM2 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM2 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"LSM2_UL_HL", NULL, "LSM2 Mixer"}, + + + {"LSM3 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM3 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM3 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM3 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM3 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM3 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM3 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"LSM3_UL_HL", NULL, "LSM3 Mixer"}, + + + {"LSM4 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM4 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM4 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM4 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM4 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM4 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM4 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM4 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"LSM4_UL_HL", NULL, "LSM4 Mixer"}, + + {"LSM5 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM5 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM5 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM5 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM5 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"LSM5 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"LSM5_UL_HL", NULL, "LSM5 Mixer"}, + + {"LSM6 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM6 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM6 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM6 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM6 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM6_UL_HL", NULL, "LSM6 Mixer"}, + + {"LSM7 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM7 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM7 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM7 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM7 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM7 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM7_UL_HL", NULL, "LSM7 Mixer"}, + + {"LSM8 Mixer", "SLIMBUS_0_TX", "SLIMBUS_0_TX"}, + {"LSM8 Mixer", "SLIMBUS_1_TX", "SLIMBUS_1_TX"}, + {"LSM8 Mixer", "SLIMBUS_3_TX", "SLIMBUS_3_TX"}, + {"LSM8 Mixer", "SLIMBUS_4_TX", "SLIMBUS_4_TX"}, + {"LSM8 Mixer", "SLIMBUS_5_TX", "SLIMBUS_5_TX"}, + {"LSM8 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"LSM8_UL_HL", NULL, "LSM8 Mixer"}, + + + {"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"}, + {"SLIM7_UL_HL", NULL, "HFP_SLIM7_UL_HL"}, + {"HFP_SLIM7_UL_HL", "Switch", "SLIMBUS_7_TX"}, + {"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"}, + {"AUX_PCM_RX", NULL, "INTHFP_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"}, + + /* connect to INT4_MI2S_DL_HL since same pcm_id */ + {"INT0_MI2S_RX_DL_HL", "Switch", "INT4_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"}, + {"INT3_MI2S_UL_HL", NULL, "INT3_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_UL_HL", NULL, "SEC_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_TX_1_UL_HL", NULL, "PRI_TDM_TX_1"}, + {"PRI_TDM_TX_2_UL_HL", NULL, "PRI_TDM_TX_2"}, + {"PRI_TDM_TX_3_UL_HL", NULL, "PRI_TDM_TX_3"}, + {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0_DL_HL"}, + {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1_DL_HL"}, + {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2_DL_HL"}, + {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3_DL_HL"}, + {"SEC_TDM_TX_0_UL_HL", NULL, "SEC_TDM_TX_0"}, + {"SEC_TDM_TX_1_UL_HL", NULL, "SEC_TDM_TX_1"}, + {"SEC_TDM_TX_2_UL_HL", NULL, "SEC_TDM_TX_2"}, + {"SEC_TDM_TX_3_UL_HL", NULL, "SEC_TDM_TX_3"}, + {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0_DL_HL"}, + {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1_DL_HL"}, + {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2_DL_HL"}, + {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3_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_1_DL_HL"}, + {"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2_DL_HL"}, + {"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3_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"}, + + {"PRI_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"PRI_TDM_RX_0 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Port Mixer"}, + + {"PRI_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"PRI_TDM_RX_1 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1 Port Mixer"}, + + {"PRI_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"PRI_TDM_RX_2 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2 Port Mixer"}, + + {"PRI_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"PRI_TDM_RX_3 Port Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3 Port Mixer"}, + + {"SEC_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"SEC_TDM_RX_0 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Port Mixer"}, + + {"SEC_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"SEC_TDM_RX_1 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1 Port Mixer"}, + + {"SEC_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"SEC_TDM_RX_2 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2 Port Mixer"}, + + {"SEC_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"SEC_TDM_RX_3 Port Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3 Port Mixer"}, + + {"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"}, + + {"INT0_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"INT0_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"INT0_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"INT0_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"INT0_MI2S_RX", NULL, "INT0_MI2S_RX Port Mixer"}, + + {"INT4_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, + {"INT4_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"INT4_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"INT4_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX 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"}, + {"USB_AUDIO_RX Port Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"USB_AUDIO_RX", NULL, "USB_AUDIO_RX Port Mixer"}, + {"USB_DL_HL", "Switch", "USBAUDIO_DL_HL"}, + {"USB_AUDIO_RX", NULL, "USB_DL_HL"}, + {"USBAUDIO_UL_HL", NULL, "USB_AUDIO_TX"}, + + + {"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 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"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 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_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 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_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 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_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 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_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, "PRI_TDM_RX_1"}, + {"BE_OUT", NULL, "PRI_TDM_RX_2"}, + {"BE_OUT", NULL, "PRI_TDM_RX_3"}, + {"BE_OUT", NULL, "SEC_TDM_RX_0"}, + {"BE_OUT", NULL, "SEC_TDM_RX_1"}, + {"BE_OUT", NULL, "SEC_TDM_RX_2"}, + {"BE_OUT", NULL, "SEC_TDM_RX_3"}, + {"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, "TERT_TDM_RX_4"}, + {"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"}, + {"INT5_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"}, + {"INT4_MI2S_RX_VI_FB_MONO_CH_MUX", "INT5_MI2S_TX", "INT5_MI2S_TX"}, + {"INT4_MI2S_RX_VI_FB_STEREO_CH_MUX", "INT5_MI2S_TX", "INT5_MI2S_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"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_VI_FB_MONO_CH_MUX"}, + {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_VI_FB_STEREO_CH_MUX"}, + {"PRI_TDM_TX_0", NULL, "BE_IN"}, + {"PRI_TDM_TX_1", NULL, "BE_IN"}, + {"PRI_TDM_TX_2", NULL, "BE_IN"}, + {"PRI_TDM_TX_3", NULL, "BE_IN"}, + {"SEC_TDM_TX_0", NULL, "BE_IN"}, + {"SEC_TDM_TX_1", NULL, "BE_IN"}, + {"SEC_TDM_TX_2", NULL, "BE_IN"}, + {"SEC_TDM_TX_3", 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->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->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[0], MSM_FRONTEND_DAI_MAX) { + if (!is_mm_lsm_fe_id(i)) + continue; + 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->passthr_mode[i] == LEGACY_PCM)) + msm_pcm_routing_deinit_pp(bedai->port_id, + topology); + } + } + + bedai->active = 0; + bedai->sample_rate = 0; + bedai->channel = 0; + for (i = 0; i < MSM_FRONTEND_DAI_MAX; i++) { + if (bedai->passthr_mode[i] != LISTEN) + bedai->passthr_mode[i] = LEGACY_PCM; + } + 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->id; + int i, path_type, topology; + int session_type = INVALID_SESSION; + 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; + struct media_format_info voc_be_media_format; + bool is_lsm; + + 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]; + + 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[0], MSM_FRONTEND_DAI_MAX) { + if (!(is_mm_lsm_fe_id(i) && + route_check_fe_id_adm_support(i))) + continue; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (bedai->passthr_mode[i] != 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; + } + + is_lsm = (i >= MSM_FRONTEND_DAI_LSM1) && + (i <= MSM_FRONTEND_DAI_LSM8); + 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][be_id].app_type; + if (app_type && is_lsm) { + app_type_idx = + msm_pcm_routing_get_lsm_app_type_idx(app_type); + sample_rate = + fe_dai_app_type_cfg[i][session_type][be_id] + .sample_rate; + bits_per_sample = + lsm_app_type_cfg[app_type_idx].bit_width; + } else 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] + [be_id].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][be_id].acdb_dev_id; + topology = msm_routing_get_adm_topology(i, session_type, + be_id); + 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, + bedai->passthr_mode[i]); + if ((fdai->perf_mode == LEGACY_PCM_MODE) && + (bedai->passthr_mode[i] == LEGACY_PCM)) + msm_pcm_routing_cfg_pp(bedai->port_id, copp_idx, + topology, channels); + } + } + + for_each_set_bit(i, &bedai->fe_sessions[0], MSM_FRONTEND_DAI_MAX) { + session_id = msm_pcm_routing_get_voc_sessionid(i); + if (session_id) { + pr_debug("%s voice session_id: 0x%x\n", __func__, + session_id); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + voc_path_type = RX_PATH; + else + voc_path_type = TX_PATH; + + voc_set_route_flag(session_id, voc_path_type, 1); + + memset(&voc_be_media_format, 0, + sizeof(struct media_format_info)); + + voc_be_media_format.port_id = bedai->port_id; + voc_be_media_format.num_channels = bedai->channel; + voc_be_media_format.sample_rate = bedai->sample_rate; + voc_be_media_format.bits_per_sample = bedai->format; + /* Defaulting this to 1 for voice call usecases */ + voc_be_media_format.channel_mapping[0] = 1; + + voc_set_device_config(session_id, voc_path_type, + &voc_be_media_format); + + if (voc_get_route_flag(session_id, RX_PATH) && + voc_get_route_flag(session_id, TX_PATH)) + voc_enable_device(session_id); + } + } + + /* Check if backend is an external ec ref port and set as needed */ + if (unlikely(bedai->port_id == voc_get_ext_ec_ref_port_id())) { + + memset(&voc_be_media_format, 0, + sizeof(struct media_format_info)); + + /* Get format info for ec ref port from msm_bedais[] */ + voc_be_media_format.port_id = bedai->port_id; + voc_be_media_format.num_channels = bedai->channel; + voc_be_media_format.bits_per_sample = bedai->format; + voc_be_media_format.sample_rate = bedai->sample_rate; + /* Defaulting this to 1 for voice call usecases */ + voc_be_media_format.channel_mapping[0] = 1; + voc_set_ext_ec_ref_media_fmt_info(&voc_be_media_format); + pr_debug("%s: EC Ref media format info set to port_id=%d, num_channels=%d, bits_per_sample=%d, sample_rate=%d\n", + __func__, voc_be_media_format.port_id, + voc_be_media_format.num_channels, + voc_be_media_format.bits_per_sample, + voc_be_media_format.sample_rate); + } + +done: + mutex_unlock(&routing_lock); + + return 0; +} + +static int msm_routing_send_device_pp_params(int port_id, int copp_idx, + int fe_id) +{ + int index, topo_id, be_idx; + unsigned long pp_config = 0; + bool mute_on; + int latency; + bool compr_passthr_mode = true; + + 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; + } + + if ((msm_bedais[be_idx].passthr_mode[fe_id] == LEGACY_PCM) || + (msm_bedais[be_idx].passthr_mode[fe_id] == LISTEN)) + compr_passthr_mode = false; + + 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) && compr_passthr_mode) + 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) && compr_passthr_mode) + 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; + bool compr_passthr_mode = true; + + 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[0], + MSM_FRONTEND_DAI_MM_SIZE) { + if ((msm_bedais[be_idx].passthr_mode[i] == LEGACY_PCM) || + (msm_bedais[be_idx].passthr_mode[i] == LISTEN)) + compr_passthr_mode = false; + + 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].passthr_mode[i]); + 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) && compr_passthr_mode) + 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) && compr_passthr_mode) + 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 int msm_aptx_dec_license_control_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = + core_get_license_status(ASM_MEDIA_FMT_APTX); + pr_debug("%s: status %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_aptx_dec_license_control_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t status = 0; + + status = core_set_license(ucontrol->value.integer.value[0], + APTX_CLASSIC_DEC_LICENSE_ID); + pr_debug("%s: status %d\n", __func__, status); + return status; +} + +static const struct snd_kcontrol_new aptx_dec_license_controls[] = { + SOC_SINGLE_EXT("APTX Dec License", SND_SOC_NOPM, 0, + 0xFFFF, 0, msm_aptx_dec_license_control_get, + msm_aptx_dec_license_control_put), +}; + +static int msm_routing_be_dai_name_table_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(be_dai_name_table); + return 0; +} + +static int msm_routing_be_dai_name_table_tlv_get(struct snd_kcontrol *kcontrol, + unsigned int __user *bytes, + unsigned int size) +{ + int i; + int ret; + + if (size < sizeof(be_dai_name_table)) { + pr_err("%s: invalid size %d requested, returning\n", + __func__, size); + ret = -EINVAL; + goto done; + } + + /* + * Fill be_dai_name_table from msm_bedais table to reduce code changes + * needed when adding new backends + */ + for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) { + be_dai_name_table[i].be_id = i; + strlcpy(be_dai_name_table[i].be_name, + msm_bedais[i].name, + LPASS_BE_NAME_MAX_LENGTH); + } + + ret = copy_to_user(bytes, &be_dai_name_table, + sizeof(be_dai_name_table)); + if (ret) { + pr_err("%s: failed to copy be_dai_name_table\n", __func__); + ret = -EFAULT; + } + +done: + return ret; +} + +static const struct snd_kcontrol_new + msm_routing_be_dai_name_table_mixer_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, + .info = msm_routing_be_dai_name_table_info, + .name = "Backend DAI Name Table", + .tlv.c = snd_soc_bytes_tlv_callback, + .private_value = (unsigned long) &(struct soc_bytes_ext) { + .max = sizeof(be_dai_name_table), + .get = msm_routing_be_dai_name_table_tlv_get, + } + }, +}; + +static int msm_routing_stereo_channel_reverse_control_get( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = swap_ch; + pr_debug("%s: Swap channel value: %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_stereo_channel_reverse_control_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i, idx, be_index, port_id; + int ret = 0; + unsigned long copp; + + pr_debug("%s Swap channel value:%ld\n", __func__, + ucontrol->value.integer.value[0]); + + swap_ch = ucontrol->value.integer.value[0]; + + mutex_lock(&routing_lock); + 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; + + for_each_set_bit(i, &msm_bedais[be_index].fe_sessions[0], + MSM_FRONTEND_DAI_MM_SIZE) { + copp = session_copp_map[i][SESSION_TYPE_RX][be_index]; + for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) { + if (!test_bit(idx, &copp)) + continue; + + pr_debug("%s: swap channel control of portid:%d, coppid:%d\n", + __func__, port_id, idx); + ret = adm_swap_speaker_channels( + port_id, idx, + msm_bedais[be_index].sample_rate, + swap_ch); + if (ret) { + pr_err("%s:Swap_channel failed, err=%d\n", + __func__, ret); + goto done; + } + } + } + } +done: + mutex_unlock(&routing_lock); + return ret; +} + +static const struct snd_kcontrol_new stereo_channel_reverse_control[] = { + SOC_SINGLE_EXT("Swap channel", SND_SOC_NOPM, 0, + 1, 0, msm_routing_stereo_channel_reverse_control_get, + msm_routing_stereo_channel_reverse_control_put), +}; + +static const 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_controls, + ARRAY_SIZE(lsm_controls)); + + 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, lsm_app_type_cfg_controls, + ARRAY_SIZE(lsm_app_type_cfg_controls)); + + snd_soc_add_platform_controls(platform, + stereo_to_custom_stereo_controls, + ARRAY_SIZE(stereo_to_custom_stereo_controls)); + + snd_soc_add_platform_controls(platform, ec_ref_param_controls, + ARRAY_SIZE(ec_ref_param_controls)); + + snd_soc_add_platform_controls(platform, channel_mixer_controls, + ARRAY_SIZE(channel_mixer_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)); + + snd_soc_add_platform_controls(platform, + msm_routing_be_dai_name_table_mixer_controls, + ARRAY_SIZE(msm_routing_be_dai_name_table_mixer_controls)); + + 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)); + + snd_soc_add_platform_controls(platform, aptx_dec_license_controls, + ARRAY_SIZE(aptx_dec_license_controls)); + snd_soc_add_platform_controls(platform, stereo_channel_reverse_control, + ARRAY_SIZE(stereo_channel_reverse_control)); + 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[0])) + return msm_bedais[i].active; + } + return 0; +} + +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__); + + afe_set_routing_callback( + (routing_cb)msm_pcm_get_dev_acdb_id_by_port_id); + + memset(&be_dai_name_table, 0, sizeof(be_dai_name_table)); + memset(&last_be_id_configured, 0, sizeof(last_be_id_configured)); + + 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(); + memset(&be_dai_name_table, 0, sizeof(be_dai_name_table)); + mutex_destroy(&routing_lock); + 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..19e726001d25 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h @@ -0,0 +1,486 @@ +/* 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_PCM_ROUTING_H +#define _MSM_PCM_ROUTING_H +#include + +/* + * These names are used by HAL to specify the BE. If any changes are + * made to the string names or the max name length corresponding + * changes need to be made in the HAL to ensure they still match. + */ +#define LPASS_BE_NAME_MAX_LENGTH 24 +#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_TX_VI "SLIMBUS_TX_VI" +#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_MULTIMEDIA20, + 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_MULTIMEDIA20 + 1) +#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA20 + +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 MAX_SESSION_TYPES 2 +#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 +#define BE_DAI_FE_SESSIONS_IDX_MAX 2 + +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 */ + + /* Front-end sessions */ + unsigned long fe_sessions[BE_DAI_FE_SESSIONS_IDX_MAX]; + /* + * 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 passthr_mode[MSM_FRONTEND_DAI_MAX]; + 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); + +int msm_pcm_routing_reg_stream_app_type_cfg( + int fedai_id, int session_type, int be_id, + struct msm_pcm_stream_app_type_cfg *cfg_data); +int msm_pcm_routing_get_stream_app_type_cfg( + int fedai_id, int session_type, int *be_id, + struct msm_pcm_stream_app_type_cfg *cfg_data); +#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..654806e9795e --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c @@ -0,0 +1,781 @@ +/* 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-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 (!strncmp("VoLTE", substream->pcm->id, 5)) { + voice = &voice_info[VOLTE_SESSION_INDEX]; + pr_debug("%s: Open VoLTE Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strncmp("Voice2", substream->pcm->id, 6)) { + voice = &voice_info[VOICE2_SESSION_INDEX]; + pr_debug("%s: Open Voice2 Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strncmp("QCHAT", substream->pcm->id, 5)) { + voice = &voice_info[QCHAT_SESSION_INDEX]; + pr_debug("%s: Open QCHAT Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strncmp("VoWLAN", substream->pcm->id, 6)) { + voice = &voice_info[VOWLAN_SESSION_INDEX]; + pr_debug("%s: Open VoWLAN Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strncmp("VoiceMMode1", substream->pcm->id, 11)) { + voice = &voice_info[VOICEMMODE1_INDEX]; + pr_debug("%s: Open VoiceMMode1 Substream Id=%s\n", + __func__, substream->pcm->id); + } else if (!strncmp("VoiceMMode2", substream->pcm->id, 11)) { + 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_sidetone_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret; + long value = ucontrol->value.integer.value[0]; + bool sidetone_enable = value; + uint32_t session_id = ALL_SESSION_VSID; + + if (value < 0) { + pr_err("%s: Invalid arguments sidetone enable %ld\n", + __func__, value); + ret = -EINVAL; + return ret; + } + ret = voc_set_afe_sidetone(session_id, sidetone_enable); + pr_debug("%s: AFE Sidetone enable=%d session_id=0x%x ret=%d\n", + __func__, sidetone_enable, session_id, ret); + return ret; +} + +static int msm_voice_sidetone_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = voc_get_afe_sidetone(); + return 0; +} + +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, + }, + SOC_SINGLE_MULTI_EXT("Voice Sidetone Enable", SND_SOC_NOPM, 0, 1, 0, 1, + msm_voice_sidetone_get, msm_voice_sidetone_put), +}; + +static const 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; + const char *is_destroy_cvd = "qcom,destroy-cvd"; + + 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); + + 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..02225f0dcef4 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c @@ -0,0 +1,1715 @@ +/* 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); + if (ret) { + pr_err("%s: copy from user failed %d\n", + __func__, ret); + return -EFAULT; + } + buf_node->frame.pktlen = count; + } else { + ret = copy_from_user(&buf_node->frame, + buf, count); + if (ret) { + pr_err("%s: copy from user failed %d\n", + __func__, ret); + return -EFAULT; + } + 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 const 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..a885e1e3aa86 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c @@ -0,0 +1,1407 @@ +/* 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, +}; + +/* 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_icc_vol_control; +static const DECLARE_TLV_DB_LINEAR(icc_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 int msm_multichannel_ec_primary_mic_ch; + +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 + param_payload_len, 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_7_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_7_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_afe_slimbus_7_lb_vol_ctrl; + return 0; +} + +static int msm_qti_pp_set_slimbus_7_lb_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = afe_loopback_gain(SLIMBUS_7_TX, + ucontrol->value.integer.value[0]); + + if (ret) + pr_err("%s: failed to set LB vol for SLIMBUS_7_TX, err %d\n", + __func__, ret); + else + msm_afe_slimbus_7_lb_vol_ctrl = + ucontrol->value.integer.value[0]; + + return ret; +} + +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_icc_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_route_icc_vol_control; + return 0; +} + +static int msm_qti_pp_set_icc_vol_mixer(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + adm_set_mic_gain(AFE_PORT_ID_QUATERNARY_TDM_TX, + adm_get_default_copp_idx(AFE_PORT_ID_QUATERNARY_TDM_TX), + ucontrol->value.integer.value[0]); + msm_route_icc_vol_control = ucontrol->value.integer.value[0]; + return 0; +} + +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] = {0}; + 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; +} + +int msm_adsp_init_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_kcontrol *kctl; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, + rtd->pcm->device); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + if (kctl->private_data != NULL) { + pr_err("%s: kctl_prtd is not NULL at initialization.\n", + __func__); + return -EINVAL; + } + + kctl_prtd = kzalloc(sizeof(struct dsp_stream_callback_prtd), + GFP_KERNEL); + if (!kctl_prtd) { + ret = -ENOMEM; + goto done; + } + + spin_lock_init(&kctl_prtd->prtd_spin_lock); + INIT_LIST_HEAD(&kctl_prtd->event_queue); + kctl_prtd->event_count = 0; + kctl->private_data = kctl_prtd; + +done: + return ret; +} + +int msm_adsp_clean_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_kcontrol *kctl; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct dsp_stream_callback_list *node, *n; + unsigned long spin_flags; + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, + rtd->pcm->device); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + kctl_prtd = (struct dsp_stream_callback_prtd *) + kctl->private_data; + if (kctl_prtd != NULL) { + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + /* clean the queue */ + list_for_each_entry_safe(node, n, + &kctl_prtd->event_queue, list) { + list_del(&node->list); + kctl_prtd->event_count--; + pr_debug("%s: %d remaining events after del.\n", + __func__, kctl_prtd->event_count); + kfree(node); + } + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + } + + kfree(kctl_prtd); + kctl->private_data = NULL; + +done: + return ret; +} + +int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, + uint32_t *payload) +{ + /* adsp pp event notifier */ + struct snd_kcontrol *kctl; + struct snd_ctl_elem_value control; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct dsp_stream_callback_list *new_event; + struct dsp_stream_callback_list *oldest_event; + unsigned long spin_flags; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + struct msm_adsp_event_data *event_data = NULL; + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + struct snd_ctl_elem_info kctl_info; + + if (!rtd || !payload) { + pr_err("%s: %s is NULL\n", __func__, + (!rtd) ? "rtd" : "payload"); + ret = -EINVAL; + goto done; + } + + if (rtd->card->snd_card == NULL) { + pr_err("%s: snd_card is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_ATOMIC); + if (!mixer_str) { + ret = -EINVAL; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, + rtd->pcm->device); + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + kfree(mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl.\n", __func__); + ret = -EINVAL; + goto done; + } + + event_data = (struct msm_adsp_event_data *)payload; + kctl->info(kctl, &kctl_info); + if (sizeof(struct msm_adsp_event_data) + + event_data->payload_len > kctl_info.count) { + pr_err("%s: payload length exceeds limit of %u bytes.\n", + __func__, kctl_info.count); + ret = -EINVAL; + goto done; + } + + kctl_prtd = (struct dsp_stream_callback_prtd *) + kctl->private_data; + if (kctl_prtd == NULL) { + /* queue is not initialized */ + ret = -EINVAL; + pr_err("%s: event queue is not initialized.\n", __func__); + goto done; + } + + new_event = kzalloc(sizeof(struct dsp_stream_callback_list) + + event_data->payload_len, + GFP_ATOMIC); + if (new_event == NULL) { + ret = -ENOMEM; + goto done; + } + memcpy((void *)&new_event->event, (void *)payload, + event_data->payload_len + + sizeof(struct msm_adsp_event_data)); + + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + while (kctl_prtd->event_count >= DSP_STREAM_CALLBACK_QUEUE_SIZE) { + pr_info("%s: queue of size %d is full. delete oldest one.\n", + __func__, DSP_STREAM_CALLBACK_QUEUE_SIZE); + oldest_event = list_first_entry(&kctl_prtd->event_queue, + struct dsp_stream_callback_list, list); + pr_info("%s: event deleted: type %d length %d\n", + __func__, oldest_event->event.event_type, + oldest_event->event.payload_len); + list_del(&oldest_event->list); + kctl_prtd->event_count--; + kfree(oldest_event); + } + + list_add_tail(&new_event->list, &kctl_prtd->event_queue); + kctl_prtd->event_count++; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + + control.id = kctl->id; + snd_ctl_notify(rtd->card->snd_card, + SNDRV_CTL_EVENT_MASK_INFO, + &control.id); + +done: + return ret; +} + +int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = + sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data); + + return 0; +} + +int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + uint32_t payload_size = 0; + struct dsp_stream_callback_list *oldest_event; + unsigned long spin_flags; + struct dsp_stream_callback_prtd *kctl_prtd = NULL; + int ret = 0; + + kctl_prtd = (struct dsp_stream_callback_prtd *) + kcontrol->private_data; + if (kctl_prtd == NULL) { + pr_err("%s: ASM Stream PP event queue is not initialized.\n", + __func__); + ret = -EINVAL; + goto done; + } + + spin_lock_irqsave(&kctl_prtd->prtd_spin_lock, spin_flags); + pr_debug("%s: %d events in queue.\n", __func__, kctl_prtd->event_count); + if (list_empty(&kctl_prtd->event_queue)) { + pr_err("%s: ASM Stream PP event queue is empty.\n", __func__); + ret = -EINVAL; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + goto done; + } + + oldest_event = list_first_entry(&kctl_prtd->event_queue, + struct dsp_stream_callback_list, list); + list_del(&oldest_event->list); + kctl_prtd->event_count--; + spin_unlock_irqrestore(&kctl_prtd->prtd_spin_lock, spin_flags); + + payload_size = oldest_event->event.payload_len; + pr_debug("%s: event fetched: type %d length %d\n", + __func__, oldest_event->event.event_type, + oldest_event->event.payload_len); + memcpy(ucontrol->value.bytes.data, &oldest_event->event, + sizeof(struct msm_adsp_event_data) + payload_size); + kfree(oldest_event); + +done: + return ret; +} + +int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = + sizeof(((struct snd_ctl_elem_value *)0)->value.bytes.data); + + return 0; +} + +static int msm_multichannel_ec_primary_mic_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int copp_idx = 0; + int port_id = AFE_PORT_ID_QUATERNARY_TDM_TX; + + msm_multichannel_ec_primary_mic_ch = ucontrol->value.integer.value[0]; + pr_debug("%s: msm_multichannel_ec_primary_mic_ch = %u\n", + __func__, msm_multichannel_ec_primary_mic_ch); + copp_idx = adm_get_default_copp_idx(port_id); + if ((copp_idx < 0) || (copp_idx > MAX_COPPS_PER_PORT)) { + pr_err("%s : no active copp to query multichannel ec copp_idx: %u\n", + __func__, copp_idx); + return -EINVAL; + } + adm_send_set_multichannel_ec_primary_mic_ch(port_id, copp_idx, + msm_multichannel_ec_primary_mic_ch); + + return ret; +} + +static int msm_multichannel_ec_primary_mic_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = msm_multichannel_ec_primary_mic_ch; + pr_debug("%s: msm_multichannel_ec_primary_mic_ch = %lu\n", + __func__, ucontrol->value.integer.value[0]); + return 0; +} + +static const struct snd_kcontrol_new msm_multichannel_ec_controls[] = { + SOC_SINGLE_EXT("Multichannel EC Primary Mic Ch", SND_SOC_NOPM, 0, + 0xFFFFFFFF, 0, msm_multichannel_ec_primary_mic_ch_get, + msm_multichannel_ec_primary_mic_ch_put), +}; + +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_7_lb_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("SLIMBUS_7 LOOPBACK Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, + msm_qti_pp_get_slimbus_7_lb_vol_mixer, + msm_qti_pp_set_slimbus_7_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 int_icc_vol_mixer_controls[] = { + SOC_SINGLE_EXT_TLV("Internal ICC Volume", SND_SOC_NOPM, 0, + INT_RX_VOL_GAIN, 0, msm_qti_pp_get_icc_vol_mixer, + msm_qti_pp_set_icc_vol_mixer, icc_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_7_lb_vol_mixer_controls, + ARRAY_SIZE(slimbus_7_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, int_icc_vol_mixer_controls, + ARRAY_SIZE(int_icc_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)); + + snd_soc_add_platform_controls(platform, msm_multichannel_ec_controls, + ARRAY_SIZE(msm_multichannel_ec_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..01a06a4965ef --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h @@ -0,0 +1,54 @@ +/* + * 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 +int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, + uint32_t *payload); +int msm_adsp_init_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd); +int msm_adsp_clean_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd); +int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +#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/msm-transcode-loopback-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c new file mode 100644 index 000000000000..b1bb27248714 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c @@ -0,0 +1,971 @@ +/* Copyright (c) 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 "msm-pcm-routing-v2.h" +#include "msm-qti-pp-config.h" + +#define LOOPBACK_SESSION_MAX_NUM_STREAMS 2 + +static DEFINE_MUTEX(transcode_loopback_session_lock); + +struct trans_loopback_pdata { + struct snd_compr_stream *cstream[MSM_FRONTEND_DAI_MAX]; +}; + +struct loopback_stream { + struct snd_compr_stream *cstream; + uint32_t codec_format; + bool start; +}; + +enum loopback_session_state { + /* One or both streams not opened */ + LOOPBACK_SESSION_CLOSE = 0, + /* Loopback streams opened */ + LOOPBACK_SESSION_READY, + /* Loopback streams opened and formats configured */ + LOOPBACK_SESSION_START, + /* Trigger issued on either of streams when in START state */ + LOOPBACK_SESSION_RUN +}; + +struct msm_transcode_loopback { + struct loopback_stream source; + struct loopback_stream sink; + + struct snd_compr_caps source_compr_cap; + struct snd_compr_caps sink_compr_cap; + + uint32_t instance; + uint32_t num_streams; + int session_state; + + struct mutex lock; + + int session_id; + struct audio_client *audio_client; +}; + +/* Transcode loopback global info struct */ +static struct msm_transcode_loopback transcode_info; + +static void loopback_event_handler(uint32_t opcode, + uint32_t token, uint32_t *payload, void *priv) +{ + struct msm_transcode_loopback *trans = + (struct msm_transcode_loopback *)priv; + struct snd_soc_pcm_runtime *rtd; + struct snd_compr_stream *cstream; + struct audio_client *ac; + int stream_id; + int ret; + + if (!trans || !payload) { + pr_err("%s: rtd or payload is NULL\n", __func__); + return; + } + + cstream = trans->source.cstream; + ac = trans->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); + + switch (opcode) { + case ASM_STREAM_CMD_ENCDEC_EVENTS: + case ASM_IEC_61937_MEDIA_FMT_EVENT: + pr_debug("%s: ASM_IEC_61937_MEDIA_FMT_EVENT\n", __func__); + rtd = cstream->private_data; + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + return; + } + + ret = msm_adsp_inform_mixer_ctl(rtd, payload); + if (ret) { + pr_err("%s: failed to inform mixer ctrl. err = %d\n", + __func__, ret); + return; + } + break; + case APR_BASIC_RSP_RESULT: { + switch (payload[0]) { + case ASM_SESSION_CMD_RUN_V2: + pr_debug("%s: ASM_SESSION_CMD_RUN_V2:", __func__); + pr_debug("token 0x%x, stream id %d\n", 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); + break; + default: + break; + } + break; + } + default: + pr_debug("%s: Not Supported Event opcode[0x%x]\n", + __func__, opcode); + break; + } +} + +static void populate_codec_list(struct msm_transcode_loopback *trans, + struct snd_compr_stream *cstream) +{ + struct snd_compr_caps compr_cap; + + pr_debug("%s\n", __func__); + + memset(&compr_cap, 0, sizeof(struct snd_compr_caps)); + + if (cstream->direction == SND_COMPRESS_CAPTURE) { + compr_cap.direction = SND_COMPRESS_CAPTURE; + compr_cap.num_codecs = 3; + compr_cap.codecs[0] = SND_AUDIOCODEC_PCM; + compr_cap.codecs[1] = SND_AUDIOCODEC_AC3; + compr_cap.codecs[2] = SND_AUDIOCODEC_EAC3; + memcpy(&trans->source_compr_cap, &compr_cap, + sizeof(struct snd_compr_caps)); + } + + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + compr_cap.direction = SND_COMPRESS_PLAYBACK; + compr_cap.num_codecs = 1; + compr_cap.codecs[0] = SND_AUDIOCODEC_PCM; + memcpy(&trans->sink_compr_cap, &compr_cap, + sizeof(struct snd_compr_caps)); + } +} + +static int msm_transcode_loopback_open(struct snd_compr_stream *cstream) +{ + int ret = 0; + struct snd_compr_runtime *runtime; + struct snd_soc_pcm_runtime *rtd; + struct msm_transcode_loopback *trans = &transcode_info; + struct trans_loopback_pdata *pdata; + + if (cstream == NULL) { + pr_err("%s: Invalid substream\n", __func__); + return -EINVAL; + } + runtime = cstream->runtime; + rtd = snd_pcm_substream_chip(cstream); + pdata = snd_soc_platform_get_drvdata(rtd->platform); + pdata->cstream[rtd->dai_link->id] = cstream; + + mutex_lock(&trans->lock); + if (trans->num_streams > LOOPBACK_SESSION_MAX_NUM_STREAMS) { + pr_err("msm_transcode_open failed..invalid stream\n"); + ret = -EINVAL; + goto exit; + } + + if (cstream->direction == SND_COMPRESS_CAPTURE) { + if (trans->source.cstream == NULL) { + trans->source.cstream = cstream; + trans->num_streams++; + } else { + pr_err("%s: capture stream already opened\n", + __func__); + ret = -EINVAL; + goto exit; + } + } else if (cstream->direction == SND_COMPRESS_PLAYBACK) { + if (trans->sink.cstream == NULL) { + trans->sink.cstream = cstream; + trans->num_streams++; + } else { + pr_debug("%s: playback stream already opened\n", + __func__); + ret = -EINVAL; + goto exit; + } + } + + pr_debug("%s: num stream%d, stream name %s\n", __func__, + trans->num_streams, cstream->name); + + populate_codec_list(trans, cstream); + + if (trans->num_streams == LOOPBACK_SESSION_MAX_NUM_STREAMS) { + pr_debug("%s: Moving loopback session to READY state %d\n", + __func__, trans->session_state); + trans->session_state = LOOPBACK_SESSION_READY; + } + + runtime->private_data = trans; + if (trans->num_streams == 1) + msm_adsp_init_mixer_ctl_pp_event_queue(rtd); +exit: + mutex_unlock(&trans->lock); + return ret; +} + +static void stop_transcoding(struct msm_transcode_loopback *trans) +{ + struct snd_soc_pcm_runtime *soc_pcm_rx; + struct snd_soc_pcm_runtime *soc_pcm_tx; + + if (trans->audio_client != NULL) { + q6asm_cmd(trans->audio_client, CMD_CLOSE); + + if (trans->sink.cstream != NULL) { + soc_pcm_rx = trans->sink.cstream->private_data; + msm_pcm_routing_dereg_phy_stream( + soc_pcm_rx->dai_link->id, + SND_COMPRESS_PLAYBACK); + } + if (trans->source.cstream != NULL) { + soc_pcm_tx = trans->source.cstream->private_data; + msm_pcm_routing_dereg_phy_stream( + soc_pcm_tx->dai_link->id, + SND_COMPRESS_CAPTURE); + } + q6asm_audio_client_free(trans->audio_client); + trans->audio_client = NULL; + } +} + +static int msm_transcode_loopback_free(struct snd_compr_stream *cstream) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_transcode_loopback *trans = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(cstream); + int ret = 0; + + mutex_lock(&trans->lock); + + pr_debug("%s: Transcode loopback end:%d, streams %d\n", __func__, + cstream->direction, trans->num_streams); + trans->num_streams--; + stop_transcoding(trans); + + if (cstream->direction == SND_COMPRESS_PLAYBACK) + memset(&trans->sink, 0, sizeof(struct loopback_stream)); + else if (cstream->direction == SND_COMPRESS_CAPTURE) + memset(&trans->source, 0, sizeof(struct loopback_stream)); + + trans->session_state = LOOPBACK_SESSION_CLOSE; + if (trans->num_streams == 1) + msm_adsp_clean_mixer_ctl_pp_event_queue(rtd); + mutex_unlock(&trans->lock); + return ret; +} + +static int msm_transcode_loopback_trigger(struct snd_compr_stream *cstream, + int cmd) +{ + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_transcode_loopback *trans = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + + if (trans->session_state == LOOPBACK_SESSION_START) { + pr_debug("%s: Issue Loopback session %d RUN\n", + __func__, trans->instance); + q6asm_run_nowait(trans->audio_client, 0, 0, 0); + trans->session_state = LOOPBACK_SESSION_RUN; + } + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_STOP: + pr_debug("%s: Issue Loopback session %d STOP\n", __func__, + trans->instance); + if (trans->session_state == LOOPBACK_SESSION_RUN) + q6asm_cmd_nowait(trans->audio_client, CMD_PAUSE); + trans->session_state = LOOPBACK_SESSION_START; + break; + + default: + break; + } + return 0; +} + +static int msm_transcode_loopback_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *codec_param) +{ + + struct snd_compr_runtime *runtime = cstream->runtime; + struct msm_transcode_loopback *trans = runtime->private_data; + struct snd_soc_pcm_runtime *soc_pcm_rx; + struct snd_soc_pcm_runtime *soc_pcm_tx; + uint32_t bit_width = 16; + int ret = 0; + + if (trans == NULL) { + pr_err("%s: Invalid param\n", __func__); + return -EINVAL; + } + + mutex_lock(&trans->lock); + + if (cstream->direction == SND_COMPRESS_PLAYBACK) { + if (codec_param->codec.id == SND_AUDIOCODEC_PCM) { + trans->sink.codec_format = + FORMAT_LINEAR_PCM; + switch (codec_param->codec.format) { + case SNDRV_PCM_FORMAT_S32_LE: + bit_width = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: + bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + bit_width = 24; + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: + bit_width = 16; + break; + } + } else { + pr_debug("%s: unknown sink codec\n", __func__); + ret = -EINVAL; + goto exit; + } + trans->sink.start = true; + } + + if (cstream->direction == SND_COMPRESS_CAPTURE) { + switch (codec_param->codec.id) { + case SND_AUDIOCODEC_PCM: + pr_debug("Source SND_AUDIOCODEC_PCM\n"); + trans->source.codec_format = + FORMAT_LINEAR_PCM; + break; + case SND_AUDIOCODEC_AC3: + pr_debug("Source SND_AUDIOCODEC_AC3\n"); + trans->source.codec_format = + FORMAT_AC3; + break; + case SND_AUDIOCODEC_EAC3: + pr_debug("Source SND_AUDIOCODEC_EAC3\n"); + trans->source.codec_format = + FORMAT_EAC3; + break; + default: + pr_debug("%s: unknown source codec\n", __func__); + ret = -EINVAL; + goto exit; + } + trans->source.start = true; + } + + pr_debug("%s: trans->source.start %d trans->sink.start %d trans->source.cstream %pK trans->sink.cstream %pK trans->session_state %d\n", + __func__, trans->source.start, trans->sink.start, + trans->source.cstream, trans->sink.cstream, + trans->session_state); + + if ((trans->session_state == LOOPBACK_SESSION_READY) && + trans->source.start && trans->sink.start) { + pr_debug("%s: Moving loopback session to start state\n", + __func__); + trans->session_state = LOOPBACK_SESSION_START; + } + + if (trans->session_state == LOOPBACK_SESSION_START) { + if (trans->audio_client != NULL) { + pr_debug("%s: ASM client already opened, closing\n", + __func__); + stop_transcoding(trans); + } + + trans->audio_client = q6asm_audio_client_alloc( + (app_cb)loopback_event_handler, trans); + if (!trans->audio_client) { + pr_err("%s: Could not allocate memory\n", __func__); + ret = -EINVAL; + goto exit; + } + pr_debug("%s: ASM client allocated, callback %pK\n", __func__, + loopback_event_handler); + trans->session_id = trans->audio_client->session; + trans->audio_client->perf_mode = false; + ret = q6asm_open_transcode_loopback(trans->audio_client, + bit_width, + trans->source.codec_format, + trans->sink.codec_format); + if (ret < 0) { + pr_err("%s: Session transcode loopback open failed\n", + __func__); + q6asm_audio_client_free(trans->audio_client); + trans->audio_client = NULL; + goto exit; + } + + pr_debug("%s: Starting ADM open for loopback\n", __func__); + soc_pcm_rx = trans->sink.cstream->private_data; + soc_pcm_tx = trans->source.cstream->private_data; + if (trans->source.codec_format != FORMAT_LINEAR_PCM) + msm_pcm_routing_reg_phy_compr_stream( + soc_pcm_tx->dai_link->id, + trans->audio_client->perf_mode, + trans->session_id, + SNDRV_PCM_STREAM_CAPTURE, + true); + else + msm_pcm_routing_reg_phy_stream( + soc_pcm_tx->dai_link->id, + trans->audio_client->perf_mode, + trans->session_id, + SNDRV_PCM_STREAM_CAPTURE); + + msm_pcm_routing_reg_phy_stream( + soc_pcm_rx->dai_link->id, + trans->audio_client->perf_mode, + trans->session_id, + SNDRV_PCM_STREAM_PLAYBACK); + pr_debug("%s: Successfully opened ADM sessions\n", __func__); + } +exit: + mutex_unlock(&trans->lock); + return ret; +} + +static int msm_transcode_loopback_get_caps(struct snd_compr_stream *cstream, + struct snd_compr_caps *arg) +{ + struct snd_compr_runtime *runtime; + struct msm_transcode_loopback *trans; + + if (!arg || !cstream) { + pr_err("%s: Invalid arguments\n", __func__); + return -EINVAL; + } + + runtime = cstream->runtime; + trans = runtime->private_data; + pr_debug("%s\n", __func__); + if (cstream->direction == SND_COMPRESS_CAPTURE) + memcpy(arg, &trans->source_compr_cap, + sizeof(struct snd_compr_caps)); + else + memcpy(arg, &trans->sink_compr_cap, + sizeof(struct snd_compr_caps)); + return 0; +} + +static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_transcode_loopback *prtd; + int ret = 0; + struct msm_adsp_event_data *event_data = NULL; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null.\n", __func__); + ret = -EINVAL; + goto done; + } + + event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data; + if ((event_data->event_type < ADSP_STREAM_PP_EVENT) || + (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) { + pr_err("%s: invalid event_type=%d", + __func__, event_data->event_type); + ret = -EINVAL; + goto done; + } + + if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= + sizeof(ucontrol->value.bytes.data)) { + pr_err("%s param length=%d exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_stream_cmd(prtd->audio_client, event_data); + if (ret < 0) + pr_err("%s: failed to send stream event cmd, err = %d\n", + __func__, ret); +done: + return ret; +} + +static int msm_transcode_ion_fd_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_transcode_loopback *prtd; + int fd; + int ret = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received out of bounds invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null\n", __func__); + ret = -EINVAL; + goto done; + } + + memcpy(&fd, ucontrol->value.bytes.data, sizeof(fd)); + ret = q6asm_send_ion_fd(prtd->audio_client, fd); + if (ret < 0) + pr_err("%s: failed to register ion fd\n", __func__); +done: + return ret; +} + +static int msm_transcode_rtic_event_ack_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + unsigned long fe_id = kcontrol->private_value; + struct trans_loopback_pdata *pdata = (struct trans_loopback_pdata *) + snd_soc_component_get_drvdata(comp); + struct snd_compr_stream *cstream = NULL; + struct msm_transcode_loopback *prtd; + int ret = 0; + int param_length = 0; + + if (fe_id >= MSM_FRONTEND_DAI_MAX) { + pr_err("%s Received invalid fe_id %lu\n", + __func__, fe_id); + ret = -EINVAL; + goto done; + } + + cstream = pdata->cstream[fe_id]; + if (cstream == NULL) { + pr_err("%s cstream is null\n", __func__); + ret = -EINVAL; + goto done; + } + + prtd = cstream->runtime->private_data; + if (!prtd) { + pr_err("%s: prtd is null\n", __func__); + ret = -EINVAL; + goto done; + } + + if (prtd->audio_client == NULL) { + pr_err("%s: audio_client is null\n", __func__); + ret = -EINVAL; + goto done; + } + + memcpy(¶m_length, ucontrol->value.bytes.data, + sizeof(param_length)); + if ((param_length + sizeof(param_length)) + >= sizeof(ucontrol->value.bytes.data)) { + pr_err("%s param length=%d exceeds limit", + __func__, param_length); + ret = -EINVAL; + goto done; + } + + ret = q6asm_send_rtic_event_ack(prtd->audio_client, + ucontrol->value.bytes.data + sizeof(param_length), + param_length); + if (ret < 0) + pr_err("%s: failed to send rtic event ack, err = %d\n", + __func__, ret); +done: + return ret; +} + +static int msm_transcode_stream_cmd_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CMD; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_loopback_stream_cmd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_transcode_stream_cmd_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_loopback_stream_cmd_config_control[0].name = mixer_str; + fe_loopback_stream_cmd_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_loopback_stream_cmd_config_control, + ARRAY_SIZE(fe_loopback_stream_cmd_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s. err = %d\n", + __func__, mixer_str, ret); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_transcode_stream_callback_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = DSP_STREAM_CALLBACK; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol *kctl; + + struct snd_kcontrol_new fe_loopback_callback_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_callback_info, + .get = msm_adsp_stream_callback_get, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s: rtd is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_loopback_callback_config_control[0].name = mixer_str; + fe_loopback_callback_config_control[0].private_value = + rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_loopback_callback_config_control, + ARRAY_SIZE(fe_loopback_callback_config_control)); + if (ret < 0) { + pr_err("%s: failed to add ctl %s. err = %d\n", + __func__, mixer_str, ret); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl = snd_soc_card_get_kcontrol(rtd->card, mixer_str); + if (!kctl) { + pr_err("%s: failed to get kctl %s.\n", __func__, mixer_str); + ret = -EINVAL; + goto free_mixer_str; + } + + kctl->private_data = NULL; +free_mixer_str: + kfree(mixer_str); +done: + return ret; +} + +static int msm_transcode_add_ion_fd_cmd_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback ION FD"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_ion_fd_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_transcode_ion_fd_map_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_ion_fd_config_control[0].name = mixer_str; + fe_ion_fd_config_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_ion_fd_config_control, + ARRAY_SIZE(fe_ion_fd_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s\n", __func__, mixer_str); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_transcode_add_event_ack_cmd_control( + struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "Playback Event Ack"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new fe_event_ack_config_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_adsp_stream_cmd_info, + .put = msm_transcode_rtic_event_ack_put, + .private_value = 0, + } + }; + + if (!rtd) { + pr_err("%s NULL rtd\n", __func__); + ret = -EINVAL; + goto done; + } + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + fe_event_ack_config_control[0].name = mixer_str; + fe_event_ack_config_control[0].private_value = rtd->dai_link->id; + pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); + ret = snd_soc_add_platform_controls(rtd->platform, + fe_event_ack_config_control, + ARRAY_SIZE(fe_event_ack_config_control)); + if (ret < 0) + pr_err("%s: failed to add ctl %s\n", __func__, mixer_str); + + kfree(mixer_str); +done: + return ret; +} + +static int msm_transcode_loopback_new(struct snd_soc_pcm_runtime *rtd) +{ + int rc; + + rc = msm_transcode_stream_cmd_control(rtd); + if (rc) + pr_err("%s: ADSP Stream Cmd Control open failed\n", __func__); + + rc = msm_transcode_stream_callback_control(rtd); + if (rc) + pr_err("%s: ADSP Stream callback Control open failed\n", + __func__); + + rc = msm_transcode_add_ion_fd_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add transcode ion fd Control\n", + __func__); + + rc = msm_transcode_add_event_ack_cmd_control(rtd); + if (rc) + pr_err("%s: Could not add transcode event ack Control\n", + __func__); + + return 0; +} + +static struct snd_compr_ops msm_transcode_loopback_ops = { + .open = msm_transcode_loopback_open, + .free = msm_transcode_loopback_free, + .trigger = msm_transcode_loopback_trigger, + .set_params = msm_transcode_loopback_set_params, + .get_caps = msm_transcode_loopback_get_caps, +}; + + +static int msm_transcode_loopback_probe(struct snd_soc_platform *platform) +{ + struct trans_loopback_pdata *pdata = NULL; + + pr_debug("%s\n", __func__); + pdata = (struct trans_loopback_pdata *) + kzalloc(sizeof(struct trans_loopback_pdata), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + snd_soc_platform_set_drvdata(platform, pdata); + return 0; +} + +static struct snd_soc_platform_driver msm_soc_platform = { + .probe = msm_transcode_loopback_probe, + .compr_ops = &msm_transcode_loopback_ops, + .pcm_new = msm_transcode_loopback_new, +}; + +static int msm_transcode_dev_probe(struct platform_device *pdev) +{ + + pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev)); + if (pdev->dev.of_node) + dev_set_name(&pdev->dev, "%s", "msm-transcode-loopback"); + + return snd_soc_register_platform(&pdev->dev, + &msm_soc_platform); +} + +static int msm_transcode_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_transcode_loopback_dt_match[] = { + {.compatible = "qcom,msm-transcode-loopback"}, + {} +}; +MODULE_DEVICE_TABLE(of, msm_transcode_loopback_dt_match); + +static struct platform_driver msm_transcode_loopback_driver = { + .driver = { + .name = "msm-transcode-loopback", + .owner = THIS_MODULE, + .of_match_table = msm_transcode_loopback_dt_match, + }, + .probe = msm_transcode_dev_probe, + .remove = msm_transcode_remove, +}; + +static int __init msm_soc_platform_init(void) +{ + memset(&transcode_info, 0, sizeof(struct msm_transcode_loopback)); + mutex_init(&transcode_info.lock); + return platform_driver_register(&msm_transcode_loopback_driver); +} +module_init(msm_soc_platform_init); + +static void __exit msm_soc_platform_exit(void) +{ + mutex_destroy(&transcode_info.lock); + platform_driver_unregister(&msm_transcode_loopback_driver); +} +module_exit(msm_soc_platform_exit); + +MODULE_DESCRIPTION("Transcode loopback platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c new file mode 100644 index 000000000000..15906054f2b8 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6adm.c @@ -0,0 +1,4860 @@ +/* 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 "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 + +#ifndef CONFIG_DOLBY_DAP +#undef DOLBY_ADM_COPP_TOPOLOGY_ID +#define DOLBY_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFE +#endif + +#ifndef CONFIG_DOLBY_DS2 +#undef DS2_ADM_COPP_TOPOLOGY_ID +#define DS2_ADM_COPP_TOPOLOGY_ID 0xFFFFFFFF +#endif + +/* 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; + int num_ec_ref_rx_chans; + int ec_ref_rx_bit_width; + int ec_ref_rx_sampling_rate; +}; + +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 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; +} + +static int adm_populate_channel_weight(u16 *ptr, + struct msm_pcm_channel_mixer *ch_mixer, + int channel_index) +{ + u16 i, j, start_index = 0; + + if (channel_index > ch_mixer->output_channel) { + pr_err("%s: channel index %d is larger than output_channel %d\n", + __func__, channel_index, ch_mixer->output_channel); + return -EINVAL; + } + + for (i = 0; i < ch_mixer->output_channel; i++) { + pr_debug("%s: weight for output %d:", __func__, i); + for (j = 0; j < ADM_MAX_CHANNELS; j++) + pr_debug(" %d", + ch_mixer->channel_weight[i][j]); + pr_debug("\n"); + } + + for (i = 0; i < channel_index; ++i) + start_index += ch_mixer->input_channels[i]; + + for (i = 0; i < ch_mixer->output_channel; ++i) { + for (j = start_index; + j < start_index + + ch_mixer->input_channels[channel_index]; j++) { + *ptr = ch_mixer->channel_weight[i][j]; + pr_debug("%s: ptr[%d][%d] = %d\n", + __func__, i, j, *ptr); + ptr++; + } + } + + return 0; +} + +/* + * adm_programable_channel_mixer + * + * Receives port_id, copp_idx, session_id, session_type, ch_mixer + * and channel_index to send ADM command to mix COPP data. + * + * port_id - Passed value, port_id for which backend is wanted + * copp_idx - Passed value, copp_idx for which COPP is wanted + * session_id - Passed value, session_id for which session is needed + * session_type - Passed value, session_type for RX or TX + * ch_mixer - Passed value, ch_mixer for which channel mixer config is needed + * channel_index - Passed value, channel_index for which channel is needed + */ +int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id, + int session_type, + struct msm_pcm_channel_mixer *ch_mixer, + int channel_index) +{ + struct adm_cmd_set_pspd_mtmx_strtr_params_v5 *adm_params = NULL; + struct adm_param_data_v5 data_v5; + int ret = 0, port_idx, sz = 0, param_size = 0; + u16 *adm_pspd_params; + u16 *ptr; + int index = 0; + + pr_debug("%s: port_id = %d\n", __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 %#x\n", __func__, port_id); + return -EINVAL; + } + /* + * First 8 bytes are 4 bytes as rule number, 2 bytes as output + * channel and 2 bytes as input channel. + * 2 * ch_mixer->output_channel means output channel mapping. + * 2 * ch_mixer->input_channels[channel_index]) means input + * channel mapping. + * 2 * ch_mixer->input_channels[channel_index] * + * ch_mixer->output_channel) means the channel mixer weighting + * coefficients. + * param_size needs to be a multiple of 4 bytes. + */ + + param_size = 2 * (4 + ch_mixer->output_channel + + ch_mixer->input_channels[channel_index] + + ch_mixer->input_channels[channel_index] * + ch_mixer->output_channel); + roundup(param_size, 4); + + sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) + + sizeof(struct default_chmixer_param_id_coeff) + + sizeof(struct adm_param_data_v5) + param_size; + pr_debug("%s: sz = %d\n", __func__, sz); + adm_params = kzalloc(sz, GFP_KERNEL); + if (!adm_params) + return -ENOMEM; + + adm_params->payload_addr_lsw = 0; + adm_params->payload_addr_msw = 0; + adm_params->mem_map_handle = 0; + adm_params->direction = session_type; + adm_params->sessionid = session_id; + pr_debug("%s: copp_id = %d, session id %d\n", __func__, + atomic_read(&this_adm.copp.id[port_idx][copp_idx]), + session_id); + adm_params->deviceid = atomic_read( + &this_adm.copp.id[port_idx][copp_idx]); + adm_params->reserved = 0; + + data_v5.module_id = MTMX_MODULE_ID_DEFAULT_CHMIXER; + data_v5.param_id = DEFAULT_CHMIXER_PARAM_ID_COEFF; + data_v5.reserved = 0; + data_v5.param_size = param_size; + adm_params->payload_size = + sizeof(struct default_chmixer_param_id_coeff) + + sizeof(struct adm_param_data_v5) + data_v5.param_size; + adm_pspd_params = (u16 *)((u8 *)adm_params + + sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5)); + memcpy(adm_pspd_params, &data_v5, sizeof(data_v5)); + + adm_pspd_params = (u16 *)((u8 *)adm_params + + sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) + + sizeof(data_v5)); + + adm_pspd_params[0] = ch_mixer->rule; + adm_pspd_params[2] = ch_mixer->output_channel; + adm_pspd_params[3] = ch_mixer->input_channels[channel_index]; + index = 4; + + if (ch_mixer->output_channel == 1) { + adm_pspd_params[index] = PCM_CHANNEL_FC; + } else if (ch_mixer->output_channel == 2) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + } else if (ch_mixer->output_channel == 3) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + } else if (ch_mixer->output_channel == 4) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LS; + adm_pspd_params[index + 3] = PCM_CHANNEL_RS; + } else if (ch_mixer->output_channel == 5) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + adm_pspd_params[index + 3] = PCM_CHANNEL_LS; + adm_pspd_params[index + 4] = PCM_CHANNEL_RS; + } else if (ch_mixer->output_channel == 6) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + } else if (ch_mixer->output_channel == 8) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + adm_pspd_params[index + 6] = PCM_CHANNEL_LB; + adm_pspd_params[index + 7] = PCM_CHANNEL_RB; + } + + index = index + ch_mixer->output_channel; + if (ch_mixer->input_channels[channel_index] == 1) { + adm_pspd_params[index] = PCM_CHANNEL_FC; + } else if (ch_mixer->input_channels[channel_index] == 2) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + } else if (ch_mixer->input_channels[channel_index] == 3) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + } else if (ch_mixer->input_channels[channel_index] == 4) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LS; + adm_pspd_params[index + 3] = PCM_CHANNEL_RS; + } else if (ch_mixer->input_channels[channel_index] == 5) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_FC; + adm_pspd_params[index + 3] = PCM_CHANNEL_LS; + adm_pspd_params[index + 4] = PCM_CHANNEL_RS; + } else if (ch_mixer->input_channels[channel_index] == 6) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + } else if (ch_mixer->input_channels[channel_index] == 8) { + adm_pspd_params[index] = PCM_CHANNEL_FL; + adm_pspd_params[index + 1] = PCM_CHANNEL_FR; + adm_pspd_params[index + 2] = PCM_CHANNEL_LFE; + adm_pspd_params[index + 3] = PCM_CHANNEL_FC; + adm_pspd_params[index + 4] = PCM_CHANNEL_LS; + adm_pspd_params[index + 5] = PCM_CHANNEL_RS; + adm_pspd_params[index + 6] = PCM_CHANNEL_LB; + adm_pspd_params[index + 7] = PCM_CHANNEL_RB; + } + + index = index + ch_mixer->input_channels[channel_index]; + ret = adm_populate_channel_weight(&adm_pspd_params[index], + ch_mixer, channel_index); + if (!ret) { + pr_err("%s: fail to get channel weight with error %d\n", + __func__, ret); + 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_PSPD_MTMX_STRTR_PARAMS_V5; + 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->reserved = 0; + + ptr = (u16 *)adm_params; + for (index = 0; index < (sz / 2); index++) + pr_debug("%s: adm_params[%d] = 0x%x\n", + __func__, index, (unsigned int)ptr[index]); + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], 0); + ret = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params); + if (ret < 0) { + pr_err("%s: Set params failed port %d rc %d\n", __func__, + port_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + 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 port = %d\n", + __func__, port_id); + ret = -ETIMEDOUT; + goto fail_cmd; + } + ret = 0; +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 rc = 0, i = 0; + int port_idx, idx; + int *params_data = (int *)params; + uint64_t sz = 0; + + 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 = (uint64_t)sizeof(struct adm_cmd_get_pp_params_v5) + + (uint64_t)params_length; + /* + * Check if the value of "sz" (which is ultimately assigned to + * "hdr.pkt_size") crosses U16_MAX. + */ + if (sz > U16_MAX) { + pr_err("%s: Invalid params_length\n", __func__); + return -EINVAL; + } + 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: + case ADM_CMD_DEVICE_OPEN_V6: + 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: + case ADM_CMDRSP_DEVICE_OPEN_V6: { + 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_arrange_mch_ep2_map(struct adm_cmd_device_open_v6 *open_v6, + int channel_mode) +{ + int rc = 0; + + memset(open_v6->dev_channel_mapping_eid2, 0, + PCM_FORMAT_MAX_NUM_CHANNEL); + + if (channel_mode == 1) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FC; + } else if (channel_mode == 2) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + } else if (channel_mode == 3) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_FC; + } else if (channel_mode == 4) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LS; + open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_RS; + } else if (channel_mode == 5) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_FC; + open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_LS; + open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_RS; + } else if (channel_mode == 6) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LFE; + open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_FC; + open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_LS; + open_v6->dev_channel_mapping_eid2[5] = PCM_CHANNEL_RS; + } else if (channel_mode == 8) { + open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL; + open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR; + open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LFE; + open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_FC; + open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_LS; + open_v6->dev_channel_mapping_eid2[5] = PCM_CHANNEL_RS; + open_v6->dev_channel_mapping_eid2[6] = PCM_CHANNEL_LB; + open_v6->dev_channel_mapping_eid2[7] = PCM_CHANNEL_RB; + } else { + pr_err("%s: invalid num_chan %d\n", __func__, + channel_mode); + rc = -EINVAL; + } + + 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; + struct adm_cmd_device_open_v6 open_v6; + 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); + + 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 = 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 = DEFAULT_COPP_TOPOLOGY; + } else { + if ((path == ADM_PATH_COMPRESSED_RX) || + (path == ADM_PATH_COMPRESSED_TX)) + 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) && + (path != ADM_PATH_COMPRESSED_TX)) + 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); + } + } + 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; + open.endpoint_id_2 = 0xFFFF; + + 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); + + if ((this_adm.num_ec_ref_rx_chans != 0) && (path != 1) && + (open.endpoint_id_2 != 0xFFFF)) { + memset(&open_v6, 0, + sizeof(struct adm_cmd_device_open_v6)); + memcpy(&open_v6, &open, + sizeof(struct adm_cmd_device_open_v5)); + open_v6.hdr.opcode = ADM_CMD_DEVICE_OPEN_V6; + open_v6.hdr.pkt_size = sizeof(open_v6); + open_v6.dev_num_channel_eid2 = + this_adm.num_ec_ref_rx_chans; + this_adm.num_ec_ref_rx_chans = 0; + + if (this_adm.ec_ref_rx_bit_width != 0) { + open_v6.bit_width_eid2 = + this_adm.ec_ref_rx_bit_width; + this_adm.ec_ref_rx_bit_width = 0; + } else { + open_v6.bit_width_eid2 = bit_width; + } + + if (this_adm.ec_ref_rx_sampling_rate != 0) { + open_v6.sample_rate_eid2 = + this_adm.ec_ref_rx_sampling_rate; + this_adm.ec_ref_rx_sampling_rate = 0; + } else { + open_v6.sample_rate_eid2 = rate; + } + + pr_debug("%s: eid2_channels=%d eid2_bit_width=%d eid2_rate=%d\n", + __func__, open_v6.dev_num_channel_eid2, + open_v6.bit_width_eid2, + open_v6.sample_rate_eid2); + + ret = adm_arrange_mch_ep2_map(&open_v6, + open_v6.dev_num_channel_eid2); + + if (ret) + return ret; + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open_v6); + } else { + 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; +} + +static void route_set_opcode_matrix_id( + struct adm_cmd_matrix_map_routings_v5 **route_addr, + int path, uint32_t passthr_mode) +{ + struct adm_cmd_matrix_map_routings_v5 *route = *route_addr; + + switch (path) { + case ADM_PATH_PLAYBACK: + route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5; + route->matrix_id = ADM_MATRIX_ID_AUDIO_RX; + break; + case ADM_PATH_LIVE_REC: + if (passthr_mode == LISTEN) { + route->hdr.opcode = + ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5; + route->matrix_id = ADM_MATRIX_ID_LISTEN_TX; + break; + } + /* fall through to set matrix id for non-listen case */ + case ADM_PATH_NONLIVE_REC: + route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5; + route->matrix_id = ADM_MATRIX_ID_AUDIO_TX; + break; + case ADM_PATH_COMPRESSED_RX: + route->hdr.opcode = ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5; + route->matrix_id = ADM_MATRIX_ID_COMPRESSED_AUDIO_RX; + break; + case ADM_PATH_COMPRESSED_TX: + route->hdr.opcode = ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5; + route->matrix_id = ADM_MATRIX_ID_COMPRESSED_AUDIO_TX; + break; + default: + pr_err("%s: Wrong path set[%d]\n", __func__, path); + break; + } + pr_debug("%s: opcode 0x%x, matrix id %d\n", + __func__, route->hdr.opcode, route->matrix_id); +} + +int adm_matrix_map(int path, struct route_payload payload_map, int perf_mode, + uint32_t passthr_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; + route->num_sessions = 1; + route_set_opcode_matrix_id(&route, path, passthr_mode); + + 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; + } + 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[i], + payload_map.acdb_dev_id[i]); + + 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[i], + payload_map.acdb_dev_id[i], + payload_map.sample_rate[i]); + /* 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\n", __func__, this_adm.ec_ref_rx); +} + +void adm_num_ec_ref_rx_chans(int num_chans) +{ + this_adm.num_ec_ref_rx_chans = num_chans; + pr_debug("%s: num_ec_ref_rx_chans:%d\n", + __func__, this_adm.num_ec_ref_rx_chans); +} + +void adm_ec_ref_rx_bit_width(int bit_width) +{ + this_adm.ec_ref_rx_bit_width = bit_width; + pr_debug("%s: ec_ref_rx_bit_width:%d\n", + __func__, this_adm.ec_ref_rx_bit_width); +} + +void adm_ec_ref_rx_sampling_rate(int sampling_rate) +{ + this_adm.ec_ref_rx_sampling_rate = sampling_rate; + pr_debug("%s: ec_ref_rx_sampling_rate:%d\n", + __func__, this_adm.ec_ref_rx_sampling_rate); +} + +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 ((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} }, + + {{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_set_mic_gain(int port_id, int copp_idx, int volume) +{ + struct adm_set_mic_gain_params mic_gain_params; + 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_set_mic_gain_params); + + mic_gain_params.params.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + mic_gain_params.params.hdr.pkt_size = sz; + mic_gain_params.params.hdr.src_svc = APR_SVC_ADM; + mic_gain_params.params.hdr.src_domain = APR_DOMAIN_APPS; + mic_gain_params.params.hdr.src_port = port_id; + mic_gain_params.params.hdr.dest_svc = APR_SVC_ADM; + mic_gain_params.params.hdr.dest_domain = APR_DOMAIN_ADSP; + mic_gain_params.params.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + mic_gain_params.params.hdr.token = port_idx << 16 | copp_idx; + mic_gain_params.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + mic_gain_params.params.payload_addr_lsw = 0; + mic_gain_params.params.payload_addr_msw = 0; + mic_gain_params.params.mem_map_handle = 0; + mic_gain_params.params.payload_size = + sizeof(struct adm_param_data_v5) + + sizeof(struct admx_mic_gain); + mic_gain_params.data.module_id = ADM_MODULE_IDX_MIC_GAIN_CTRL; + mic_gain_params.data.param_id = ADM_PARAM_IDX_MIC_GAIN; + mic_gain_params.data.param_size = + sizeof(struct admx_mic_gain); + mic_gain_params.data.reserved = 0; + mic_gain_params.mic_gain_data.tx_mic_gain = volume; + mic_gain_params.mic_gain_data.reserved = 0; + pr_debug("%s: Mic Gain set to %d at port_id 0x%x\n", + __func__, volume, port_id); + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)&mic_gain_params); + 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: Mic Gain 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_send_set_multichannel_ec_primary_mic_ch(int port_id, int copp_idx, + int primary_mic_ch) +{ + struct adm_set_sec_primary_ch_params sec_primary_ch_params; + int rc = 0; + int sz, port_idx; + + pr_debug("%s port_id 0x%x, copp_idx 0x%x, primary_mic_ch %d\n", + __func__, port_id, copp_idx, primary_mic_ch); + 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_idx 0x%x\n", __func__, copp_idx); + return -EINVAL; + } + + sz = sizeof(struct adm_set_sec_primary_ch_params); + + sec_primary_ch_params.params.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + sec_primary_ch_params.params.hdr.pkt_size = sz; + sec_primary_ch_params.params.hdr.src_svc = APR_SVC_ADM; + sec_primary_ch_params.params.hdr.src_domain = APR_DOMAIN_APPS; + sec_primary_ch_params.params.hdr.src_port = port_id; + sec_primary_ch_params.params.hdr.dest_svc = APR_SVC_ADM; + sec_primary_ch_params.params.hdr.dest_domain = APR_DOMAIN_ADSP; + sec_primary_ch_params.params.hdr.dest_port = + atomic_read(&this_adm.copp.id[port_idx][copp_idx]); + sec_primary_ch_params.params.hdr.token = port_idx << 16 | copp_idx; + sec_primary_ch_params.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5; + sec_primary_ch_params.params.payload_addr_lsw = 0; + sec_primary_ch_params.params.payload_addr_msw = 0; + sec_primary_ch_params.params.mem_map_handle = 0; + sec_primary_ch_params.params.payload_size = + sizeof(struct adm_param_data_v5) + + sizeof(struct admx_sec_primary_mic_ch); + sec_primary_ch_params.data.module_id = + AUDPROC_MODULE_ID_VOICE_TX_SECNS; + sec_primary_ch_params.data.param_id = + AUDPROC_PARAM_IDX_SEC_PRIMARY_MIC_CH; + sec_primary_ch_params.data.param_size = + sizeof(struct admx_sec_primary_mic_ch); + sec_primary_ch_params.data.reserved = 0; + sec_primary_ch_params.sec_primary_mic_ch_data.version = 0; + sec_primary_ch_params.sec_primary_mic_ch_data.reserved = 0; + sec_primary_ch_params.sec_primary_mic_ch_data.sec_primary_mic_ch = + primary_mic_ch; + sec_primary_ch_params.sec_primary_mic_ch_data.reserved1 = 0; + + atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1); + rc = apr_send_pkt(this_adm.apr, (uint32_t *)&sec_primary_ch_params); + 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: Mic 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; +} + +/** + * adm_swap_speaker_channels + * + * Receives port_id, copp_idx, sample rate, spk_swap and + * send MFC command to swap speaker channel. + * Return zero on success. On failure returns nonzero. + * + * port_id - Passed value, port_id for which channels swap is wanted + * copp_idx - Passed value, copp_idx for which channels swap is wanted + * sample_rate - Passed value, sample rate used by app type config + * spk_swap - Passed value, spk_swap for check if swap flag is set + */ +int adm_swap_speaker_channels(int port_id, int copp_idx, + int sample_rate, bool spk_swap) +{ + struct audproc_mfc_output_media_fmt mfc_cfg; + uint16_t num_channels; + int port_idx; + int ret = 0; + + pr_debug("%s: Enter, port_id %d, copp_idx %d\n", + __func__, port_id, 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 || port_idx >= AFE_MAX_PORTS) { + 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; + } + + num_channels = atomic_read( + &this_adm.copp.channels[port_idx][copp_idx]); + if (num_channels != 2) { + pr_debug("%s: Invalid number of channels: %d\n", + __func__, num_channels); + ret = -EINVAL; + goto done; + } + + memset(&mfc_cfg, 0, sizeof(mfc_cfg)); + 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 = + sizeof(mfc_cfg); + 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 = sample_rate; + mfc_cfg.bits_per_sample = + atomic_read(&this_adm.copp.bit_width[port_idx][copp_idx]); + mfc_cfg.num_channels = num_channels; + + /* Currently applying speaker swap for only 2 channel use case */ + if (spk_swap) { + mfc_cfg.channel_type[0] = + (uint16_t) PCM_CHANNEL_FR; + mfc_cfg.channel_type[1] = + (uint16_t) PCM_CHANNEL_FL; + } else { + mfc_cfg.channel_type[0] = + (uint16_t) PCM_CHANNEL_FL; + mfc_cfg.channel_type[1] = + (uint16_t) PCM_CHANNEL_FR; + } + + 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\n", + __func__, port_idx, copp_idx, mfc_cfg.sampling_rate, + mfc_cfg.bits_per_sample, mfc_cfg.num_channels); + + ret = apr_send_pkt(this_adm.apr, (uint32_t *)&mfc_cfg); + if (ret < 0) { + pr_err("%s: port_id: for[0x%x] failed %d\n", + __func__, port_id, ret); + goto done; + } + /* 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: mfc_cfg Set params timed out for port_id: for [0x%x]\n", + __func__, port_id); + ret = -ETIMEDOUT; + goto done; + } + + 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 done; + } + + pr_debug("%s: mfc_cfg Set params returned success", __func__); + ret = 0; + +done: + return ret; +} +EXPORT_SYMBOL(adm_swap_speaker_channels); + +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; + this_adm.num_ec_ref_rx_chans = 0; + this_adm.ec_ref_rx_bit_width = 0; + this_adm.ec_ref_rx_sampling_rate = 0; + 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..e1ce947cba48 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6afe.c @@ -0,0 +1,7150 @@ +/* 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_SIDETONE_IIR_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; + struct afe_av_dev_drift_get_param_resp av_dev_drift_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; + int dev_acdb_id[AFE_MAX_PORTS]; + routing_cb rt_cb; +}; + +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 void av_dev_drift_afe_cb_handler(uint32_t *payload, + uint32_t payload_size) +{ + u32 param_id; + struct afe_av_dev_drift_get_param_resp *resp = + (struct afe_av_dev_drift_get_param_resp *) payload; + + if (!(&(resp->pdata))) { + pr_err("%s: Error: resp pdata is NULL\n", __func__); + return; + } + + param_id = resp->pdata.param_id; + if (param_id == AFE_PARAM_ID_DEV_TIMING_STATS) { + if (payload_size < sizeof(this_afe.av_dev_drift_resp)) { + pr_err("%s: Error: received size %d, resp size %zu\n", + __func__, payload_size, + sizeof(this_afe.av_dev_drift_resp)); + return; + } + memcpy(&this_afe.av_dev_drift_resp, payload, + sizeof(this_afe.av_dev_drift_resp)); + if (!this_afe.av_dev_drift_resp.status) { + atomic_set(&this_afe.state, 0); + } else { + pr_debug("%s: av_dev_drift_resp status: %d", __func__, + this_afe.av_dev_drift_resp.status); + atomic_set(&this_afe.state, -1); + } + } +} + +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); + rtac_clear_mapping(AFE_RTAC_CAL); + + 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) { + uint32_t *payload = data->payload; + + 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 (payload[2] == AFE_PARAM_ID_DEV_TIMING_STATS) { + av_dev_drift_afe_cb_handler(data->payload, + data->payload_size); + } else { + if (rtac_make_afe_callback(data->payload, + data->payload_size)) + return 0; + + 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__); + + memset(&delay_entry, 0, sizeof(delay_entry)); + 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; + int afe_port_index = q6audio_get_port_index(port_id); + + if (afe_port_index < 0) + goto err_exit; + + 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) { + if (this_afe.dev_acdb_id[afe_port_index] > 0) { + if (afe_top->acdb_id == + this_afe.dev_acdb_id[afe_port_index]) { + pr_debug("%s: top_id:%x acdb_id:%d afe_port_id:%d\n", + __func__, afe_top->topology, + afe_top->acdb_id, + q6audio_get_port_id(port_id)); + return cal_block; + } + } else { + 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; + } + } + } + +err_exit: + 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) { + 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; + rtac_update_afe_topology(port_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 struct cal_block_data *afe_find_cal(int cal_index, int port_id) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_afe *afe_cal_info = NULL; + int afe_port_index = q6audio_get_port_index(port_id); + + pr_debug("%s: cal_index %d port_id %d port_index %d\n", __func__, + cal_index, port_id, afe_port_index); + if (afe_port_index < 0) { + pr_err("%s: Error getting AFE port index %d\n", + __func__, afe_port_index); + goto exit; + } + + list_for_each_safe(ptr, next, + &this_afe.cal_data[cal_index]->cal_blocks) { + cal_block = list_entry(ptr, struct cal_block_data, list); + afe_cal_info = cal_block->cal_info; + if ((afe_cal_info->acdb_id == + this_afe.dev_acdb_id[afe_port_index]) && + (afe_cal_info->sample_rate == + this_afe.afe_sample_rates[afe_port_index])) { + pr_debug("%s: cal block is a match, size is %zd\n", + __func__, cal_block->cal_data.size); + goto exit; + } + } + pr_err("%s: no matching cal_block found\n", __func__); + cal_block = NULL; + +exit: + return cal_block; +} + +static void send_afe_cal_type(int cal_index, int port_id) +{ + struct cal_block_data *cal_block = NULL; + int ret; + int afe_port_index = q6audio_get_port_index(port_id); + + 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; + } + + if (afe_port_index < 0) { + pr_err("%s: Error getting AFE port index %d\n", + __func__, afe_port_index); + goto done; + } + + mutex_lock(&this_afe.cal_data[cal_index]->lock); + + if (((cal_index == AFE_COMMON_RX_CAL) || + (cal_index == AFE_COMMON_TX_CAL)) && + (this_afe.dev_acdb_id[afe_port_index] > 0)) + cal_block = afe_find_cal(cal_index, port_id); + else + 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 || + port_id == AFE_PORT_ID_INT3_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 || + port_id == AFE_PORT_ID_INT3_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; +} +EXPORT_SYMBOL(afe_set_config); + +/* + * 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); +} +EXPORT_SYMBOL(afe_clear_config); + +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, u16 num_groups) +{ + 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; + } + + if ((index >= 0) && (index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[index] = rate; + + if (this_afe.rt_cb) + this_afe.dev_acdb_id[index] = this_afe.rt_cb(port_id); + } + + /* Also send the topology id here if multiple ports: */ + port_index = afe_get_port_index(port_id); + if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE) && + num_groups > 1) { + /* 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; + } + /* slot mapping is not need if there is only one group */ + if (num_groups > 1) { + 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; +} + +void afe_set_routing_callback(routing_cb cb) +{ + this_afe.rt_cb = cb; +} + +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; + } + + config.pdata.param_id = AFE_PARAM_ID_USB_AUDIO_DEV_LPCM_FMT; + config.pdata.param_size = sizeof(config.lpcm_fmt); + config.lpcm_fmt.cfg_minor_version = + AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG; + config.lpcm_fmt.endian = afe_config->usb_audio.endian; + + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE device param cmd LPCM_FMT 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); + if (index < 0) { + pr_err("%s: Invalid index number: %d\n", __func__, index); + 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 = 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; + } + + if ((index >= 0) && (index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[index] = rate; + + if (this_afe.rt_cb) + this_afe.dev_acdb_id[index] = this_afe.rt_cb(port_id); + } + + 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: + 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: + 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)) { + /* + * 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; + 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: + 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; + } + + if ((index >= 0) && (index < AFE_MAX_PORTS)) { + this_afe.afe_sample_rates[index] = rate; + + if (this_afe.rt_cb) + this_afe.dev_acdb_id[index] = this_afe.rt_cb(port_id); + } + + /* 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; +} + +static int afe_sidetone_iir(u16 tx_port_id) +{ + struct afe_loopback_iir_cfg_v2 iir_sidetone; + int ret; + int index = 0; + uint16_t size = 0; + int cal_index = AFE_SIDETONE_IIR_CAL; + int iir_pregain = 0; + int iir_num_biquad_stages = 0; + int iir_enable; + struct cal_block_data *cal_block; + int mid; + + memset(&iir_sidetone, 0, sizeof(iir_sidetone)); + index = q6audio_get_port_index(tx_port_id); + iir_sidetone.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + iir_sidetone.hdr.pkt_size = sizeof(iir_sidetone); + iir_sidetone.hdr.src_port = 0; + iir_sidetone.hdr.dest_port = 0; + iir_sidetone.hdr.token = index; + iir_sidetone.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + iir_sidetone.param.port_id = tx_port_id; + iir_sidetone.param.payload_address_lsw = 0x00; + iir_sidetone.param.payload_address_msw = 0x00; + iir_sidetone.param.mem_map_handle = 0x00; + + if (this_afe.cal_data[cal_index] == NULL) { + pr_err("%s: cal data is NULL\n", __func__); + ret = -EINVAL; + 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__); + mutex_unlock(&this_afe.cal_data[cal_index]->lock); + ret = -EINVAL; + goto done; + } + + iir_pregain = ((struct audio_cal_info_sidetone_iir *) + cal_block->cal_info)->pregain; + iir_enable = ((struct audio_cal_info_sidetone_iir *) + cal_block->cal_info)->iir_enable; + iir_num_biquad_stages = ((struct audio_cal_info_sidetone_iir *) + cal_block->cal_info)->num_biquad_stages; + mid = ((struct audio_cal_info_sidetone_iir *) + cal_block->cal_info)->mid; + + /* + * calculate the actual size of payload based on no of stages + * enabled in calibration + */ + size = (MAX_SIDETONE_IIR_DATA_SIZE / MAX_NO_IIR_FILTER_STAGE) * + iir_num_biquad_stages; + /* + * For an odd number of stages, 2 bytes of padding are + * required at the end of the payload. + */ + if (iir_num_biquad_stages % 2) { + pr_debug("%s: adding 2 to size:%d\n", __func__, size); + size = size + 2; + } + memcpy(&iir_sidetone.st_iir_filter_config_data.iir_config, + &((struct audio_cal_info_sidetone_iir *) + cal_block->cal_info)->iir_config, + sizeof(iir_sidetone.st_iir_filter_config_data.iir_config)); + mutex_unlock(&this_afe.cal_data[cal_index]->lock); + + /* + * Calculate the payload size for setparams command + */ + iir_sidetone.param.payload_size = (sizeof(iir_sidetone) - + sizeof(struct apr_hdr) - + sizeof(struct afe_port_cmd_set_param_v2) - + (MAX_SIDETONE_IIR_DATA_SIZE - size)); + + pr_debug("%s: payload size :%d\n", __func__, + iir_sidetone.param.payload_size); + + /* + * Set IIR enable params + */ + iir_sidetone.st_iir_enable_pdata.module_id = mid; + iir_sidetone.st_iir_enable_pdata.param_id = + AFE_PARAM_ID_ENABLE; + iir_sidetone.st_iir_enable_pdata.param_size = + sizeof(iir_sidetone.st_iir_mode_enable_data); + iir_sidetone.st_iir_mode_enable_data.enable = iir_enable; + + /* + * Set IIR filter config params + */ + iir_sidetone.st_iir_filter_config_pdata.module_id = mid; + iir_sidetone.st_iir_filter_config_pdata.param_id = + AFE_PARAM_ID_SIDETONE_IIR_FILTER_CONFIG; + iir_sidetone.st_iir_filter_config_pdata.param_size = + sizeof(iir_sidetone.st_iir_filter_config_data.num_biquad_stages) + + + sizeof(iir_sidetone.st_iir_filter_config_data.pregain) + size; + iir_sidetone.st_iir_filter_config_pdata.reserved = 0; + iir_sidetone.st_iir_filter_config_data.num_biquad_stages = + iir_num_biquad_stages; + iir_sidetone.st_iir_filter_config_data.pregain = iir_pregain; + pr_debug("%s: tx(0x%x)mid(0x%x)iir_en(%d)stg(%d)gain(0x%x)size(%d)\n", + __func__, tx_port_id, mid, + iir_sidetone.st_iir_mode_enable_data.enable, + iir_sidetone.st_iir_filter_config_data.num_biquad_stages, + iir_sidetone.st_iir_filter_config_data.pregain, + iir_sidetone.st_iir_filter_config_pdata.param_size); + ret = afe_apr_send_pkt(&iir_sidetone, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE sidetone failed for tx_port(0x%x)\n", + __func__, tx_port_id); + +done: + return ret; + +} + +static int afe_sidetone(u16 tx_port_id, u16 rx_port_id, bool enable) +{ + struct afe_st_loopback_cfg_v1 cmd_sidetone; + int ret; + int index; + int cal_index = AFE_SIDETONE_CAL; + int sidetone_gain; + int sidetone_enable; + struct cal_block_data *cal_block; + int mid = 0; + + memset(&cmd_sidetone, 0, sizeof(cmd_sidetone)); + if (this_afe.cal_data[cal_index] == NULL) { + pr_err("%s: cal data is NULL\n", __func__); + ret = -EINVAL; + 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__); + mutex_unlock(&this_afe.cal_data[cal_index]->lock); + ret = -EINVAL; + goto done; + } + sidetone_gain = ((struct audio_cal_info_sidetone *) + cal_block->cal_info)->gain; + sidetone_enable = ((struct audio_cal_info_sidetone *) + cal_block->cal_info)->enable; + mid = ((struct audio_cal_info_sidetone *) + cal_block->cal_info)->mid; + mutex_unlock(&this_afe.cal_data[cal_index]->lock); + + index = q6audio_get_port_index(tx_port_id); + 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 = index; + cmd_sidetone.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + cmd_sidetone.param.port_id = tx_port_id; + 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.gain_pdata.module_id = AFE_MODULE_LOOPBACK; + cmd_sidetone.gain_pdata.param_id = AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH; + /* + * size of actual payload only + */ + cmd_sidetone.gain_pdata.param_size = sizeof( + struct afe_loopback_sidetone_gain); + cmd_sidetone.gain_data.rx_port_id = rx_port_id; + cmd_sidetone.gain_data.gain = sidetone_gain; + + cmd_sidetone.cfg_pdata.module_id = AFE_MODULE_LOOPBACK; + cmd_sidetone.cfg_pdata.param_id = AFE_PARAM_ID_LOOPBACK_CONFIG; + /* + * size of actual payload only + */ + cmd_sidetone.cfg_pdata.param_size = sizeof(struct loopback_cfg_data); + cmd_sidetone.cfg_data.loopback_cfg_minor_version = + AFE_API_VERSION_LOOPBACK_CONFIG; + cmd_sidetone.cfg_data.dst_port_id = rx_port_id; + cmd_sidetone.cfg_data.routing_mode = LB_MODE_SIDETONE; + cmd_sidetone.cfg_data.enable = enable; + + pr_debug("%s rx(0x%x) tx(0x%x) enable(%d) mid(0x%x) gain(%d) sidetone_enable(%d)\n", + __func__, rx_port_id, tx_port_id, + enable, mid, sidetone_gain, sidetone_enable); + + ret = afe_apr_send_pkt(&cmd_sidetone, &this_afe.wait[index]); + if (ret) + pr_err("%s: AFE sidetone send failed for tx_port:%d rx_port:%d ret:%d\n", + __func__, tx_port_id, rx_port_id, ret); +done: + return ret; +} + +int afe_sidetone_enable(u16 tx_port_id, u16 rx_port_id, bool enable) +{ + int ret; + int index; + + 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); + ret = -EINVAL; + goto done; + } + if (q6audio_validate_port(rx_port_id) < 0) { + pr_err("%s: Invalid port 0x%x\n", + __func__, rx_port_id); + ret = -EINVAL; + goto done; + } + index = q6audio_get_port_index(tx_port_id); + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: AFE port index[%d] invalid!\n", + __func__, index); + ret = -EINVAL; + goto done; + } + if (q6audio_validate_port(tx_port_id) < 0) { + pr_err("%s: Invalid port 0x%x\n", + __func__, tx_port_id); + ret = -EINVAL; + goto done; + } + if (enable) { + ret = afe_sidetone_iir(tx_port_id); + if (ret) + goto done; + } + + ret = afe_sidetone(tx_port_id, rx_port_id, enable); + +done: + 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: + 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: + 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 = 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; + this_afe.dev_acdb_id[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; +} + +/** + * afe_set_lpass_clk_cfg - Set AFE clk config + * + * @index: port index + * @cfg: pointer to clk set struct + * + * Returns 0 on success, appropriate error code otherwise + */ +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; +} +EXPORT_SYMBOL(afe_set_lpass_clk_cfg); + +/** + * afe_set_lpass_clock_v2 - Enable AFE lpass clock + * + * @port_id: AFE port id + * @cfg: pointer to clk set struct + * + * Returns 0 on success, appropriate error code otherwise + */ +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; +} +EXPORT_SYMBOL(afe_set_lpass_clock_v2); + +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_get_av_dev_drift(struct afe_param_id_dev_timing_stats *timing_stats, + u16 port) +{ + int ret = -EINVAL; + int index = 0; + struct afe_av_dev_drift_get_param av_dev_drift; + + if (!timing_stats) { + pr_err("%s: Invalid params\n", __func__); + goto exit; + } + + ret = q6audio_validate_port(port); + if (ret < 0) { + pr_err("%s: invalid port 0x%x ret %d\n", __func__, port, ret); + ret = -EINVAL; + goto exit; + } + + index = q6audio_get_port_index(port); + if (index < 0 || index >= AFE_MAX_PORTS) { + pr_err("%s: Invalid AFE port index[%d]\n", + __func__, index); + ret = -EINVAL; + goto exit; + } + + memset(&av_dev_drift, 0, sizeof(struct afe_av_dev_drift_get_param)); + + av_dev_drift.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + av_dev_drift.hdr.pkt_size = sizeof(av_dev_drift); + av_dev_drift.hdr.src_port = 0; + av_dev_drift.hdr.dest_port = 0; + av_dev_drift.hdr.token = index; + av_dev_drift.hdr.opcode = AFE_PORT_CMD_GET_PARAM_V2; + av_dev_drift.get_param.mem_map_handle = 0; + av_dev_drift.get_param.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + av_dev_drift.get_param.param_id = AFE_PARAM_ID_DEV_TIMING_STATS; + av_dev_drift.get_param.payload_address_lsw = 0; + av_dev_drift.get_param.payload_address_msw = 0; + av_dev_drift.get_param.payload_size = sizeof(av_dev_drift) + - sizeof(av_dev_drift.get_param) - sizeof(av_dev_drift.hdr); + av_dev_drift.get_param.port_id = q6audio_get_port_id(port); + av_dev_drift.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE; + av_dev_drift.pdata.param_id = AFE_PARAM_ID_DEV_TIMING_STATS; + av_dev_drift.pdata.param_size = sizeof(av_dev_drift.timing_stats); + atomic_set(&this_afe.status, 0); + atomic_set(&this_afe.state, 1); + ret = apr_send_pkt(this_afe.apr, (uint32_t *)&av_dev_drift); + if (ret < 0) { + pr_err("%s: get param port 0x%x param id[0x%x] failed %d\n", + __func__, port, av_dev_drift.get_param.param_id, ret); + goto exit; + } + + 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 exit; + } + + 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 exit; + } + + memcpy(timing_stats, &this_afe.av_dev_drift_resp.timing_stats, + sizeof(this_afe.av_dev_drift_resp.timing_stats)); + ret = 0; +exit: + 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_SIDETONE_IIR_CAL_TYPE: + ret = AFE_SIDETONE_IIR_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)); + 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; + } + 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; + } + + + mutex_lock(&this_afe.afe_cmd_lock); + 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); + mutex_unlock(&this_afe.afe_cmd_lock); + goto done; + } + cal_block->map_data.q6map_handle = atomic_read(&this_afe. + mem_map_cal_handles[cal_index]); + mutex_unlock(&this_afe.afe_cmd_lock); +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_SIDETONE_IIR_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; + this_afe.afe_sample_rates[i] = 0; + this_afe.dev_acdb_id[i] = 0; + 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..e7e161849ff5 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6asm.c @@ -0,0 +1,9396 @@ +/* + * 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 uint32_t adsp_reg_event_opcode[] = { + ASM_STREAM_CMD_REGISTER_PP_EVENTS, + ASM_STREAM_CMD_REGISTER_ENCDEC_EVENTS, + ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE }; + +static uint32_t adsp_raise_event_opcode[] = { + ASM_STREAM_PP_EVENT, + ASM_STREAM_CMD_ENCDEC_EVENTS, + ASM_IEC_61937_MEDIA_FMT_EVENT }; + +static int is_adsp_reg_event(uint32_t cmd) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(adsp_reg_event_opcode); i++) { + if (cmd == adsp_reg_event_opcode[i]) + return i; + } + return -EINVAL; +} + +static int is_adsp_raise_event(uint32_t cmd) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(adsp_raise_event_opcode); i++) { + if (cmd == adsp_raise_event_opcode[i]) + return i; + } + return -EINVAL; +} + +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; +} + +int q6asm_send_stream_cmd(struct audio_client *ac, + struct msm_adsp_event_data *data) +{ + char *asm_params = NULL; + struct apr_hdr hdr; + int sz, rc; + + if (!data || !ac) { + pr_err("%s: %s is NULL\n", __func__, + (!data) ? "data" : "ac"); + rc = -EINVAL; + goto done; + } + + if (data->event_type >= ARRAY_SIZE(adsp_reg_event_opcode)) { + pr_err("%s: event %u out of boundary of array size of (%lu)\n", + __func__, data->event_type, + (long)ARRAY_SIZE(adsp_reg_event_opcode)); + rc = -EINVAL; + goto done; + } + + sz = sizeof(struct apr_hdr) + data->payload_len; + asm_params = kzalloc(sz, GFP_KERNEL); + if (!asm_params) { + rc = -ENOMEM; + goto done; + } + + q6asm_add_hdr_async(ac, &hdr, sz, TRUE); + atomic_set(&ac->cmd_state_pp, -1); + hdr.opcode = adsp_reg_event_opcode[data->event_type]; + memcpy(asm_params, &hdr, sizeof(struct apr_hdr)); + memcpy(asm_params + sizeof(struct apr_hdr), + data->payload, data->payload_len); + rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params); + if (rc < 0) { + pr_err("%s: stream event cmd apr pkt failed\n", __func__); + rc = -EINVAL; + goto fail_send_param; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state_pp) >= 0), 1 * HZ); + if (!rc) { + pr_err("%s: timeout for stream event cmd resp\n", __func__); + rc = -ETIMEDOUT; + goto fail_send_param; + } + + if (atomic_read(&ac->cmd_state_pp) > 0) { + pr_err("%s: DSP returned error[%s] for stream event cmd\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state_pp))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state_pp)); + goto fail_send_param; + } + + rc = 0; +fail_send_param: + kfree(asm_params); +done: + return rc; +} + +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->cmd_state_pp, 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__); + } + + 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; + struct msm_adsp_event_data *pp_event_package = NULL; + uint32_t payload_size = 0; + + 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; + ac->apr2 = NULL; + } + 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); + atomic_set(&ac->cmd_state_pp, 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_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK: + case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_DATA_CMD_IEC_60958_MEDIA_FMT: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM: + case ASM_STREAM_CMD_SET_ENCDEC_PARAM_V2: + case ASM_STREAM_CMD_REGISTER_ENCDEC_EVENTS: + case ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE: + 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) { + if ((is_adsp_reg_event(payload[0]) >= 0) + || (payload[0] == + ASM_STREAM_CMD_SET_PP_PARAMS_V2)) + atomic_set(&ac->cmd_state_pp, + payload[1]); + else + atomic_set(&ac->cmd_state, + payload[1]); + wake_up(&ac->cmd_wait); + } + return 0; + } + if ((is_adsp_reg_event(payload[0]) >= 0) || + (payload[0] == ASM_STREAM_CMD_SET_PP_PARAMS_V2)) { + if (atomic_read(&ac->cmd_state_pp) && + wakeup_flag) { + atomic_set(&ac->cmd_state_pp, 0); + wake_up(&ac->cmd_wait); + } + } else { + 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; + case ASM_STREAM_CMD_REGISTER_PP_EVENTS: + pr_debug("%s: ASM_STREAM_CMD_REGISTER_PP_EVENTS 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[1] != 0) + pr_err("%s: ASM get param error = %d, resuming\n", + __func__, payload[1]); + atomic_set(&ac->cmd_state_pp, payload[1]); + wake_up(&ac->cmd_wait); + 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_STREAM_PP_EVENT: + case ASM_STREAM_CMD_ENCDEC_EVENTS: + case ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE: + pr_debug("%s: ASM_STREAM_EVENT payload[0][0x%x] payload[1][0x%x]", + __func__, payload[0], payload[1]); + i = is_adsp_raise_event(data->opcode); + if (i < 0) + return 0; + + /* repack payload for asm_stream_pp_event + * package is composed of event type + size + actual payload + */ + payload_size = data->payload_size; + pp_event_package = kzalloc(payload_size + + sizeof(struct msm_adsp_event_data), + GFP_ATOMIC); + if (!pp_event_package) + return -ENOMEM; + + pp_event_package->event_type = i; + pp_event_package->payload_len = payload_size; + memcpy((void *)pp_event_package->payload, + data->payload, payload_size); + ac->cb(data->opcode, data->token, + (void *)pp_event_package, ac->priv); + kfree(pp_event_package); + return 0; + case ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2: + pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n", + __func__, ac->session, payload[0], payload[2], + payload[1]); + wake_up(&ac->cmd_wait); + 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(); + 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 + * @ts_mode: timestamp mode + */ +int q6asm_open_read_v4(struct audio_client *ac, uint32_t format, + uint16_t bits_per_sample, bool ts_mode) +{ + return __q6asm_open_read(ac, format, bits_per_sample, + PCM_MEDIA_FORMAT_V4 /*media fmt block ver*/, + 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; + case FORMAT_GEN_COMPR: + open.fmt_id = ASM_MEDIA_FMT_GENERIC_COMPRESSED; + break; + case FORMAT_TRUEHD: + open.fmt_id = ASM_MEDIA_FMT_TRUEHD; + break; + case FORMAT_IEC61937: + open.fmt_id = ASM_MEDIA_FMT_IEC; + 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 || + passthrough_flag == COMPRESSED_PASSTHROUGH_GEN) { + 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 if (passthrough_flag == COMPRESSED_PASSTHROUGH_IEC61937) { + open.flags = 0x1; + pr_debug("%s: Flag 1 - COMPRESSED_PASSTHROUGH_IEC61937\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_POSTPROCOPO_ID_NONE; + + 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; + case FORMAT_APTX: + open.dec_fmt_id = ASM_MEDIA_FMT_APTX; + break; + case FORMAT_GEN_COMPR: + open.dec_fmt_id = ASM_MEDIA_FMT_GENERIC_COMPRESSED; + 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(); + + + 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; + break; + 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; + + 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); + + if (ac->perf_mode == LOW_LATENCY_PCM_MODE) { + struct asm_stream_cmd_open_transcode_loopback_t open; + + q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE); + atomic_set(&ac->cmd_state, -1); + open.hdr.opcode = ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK; + + open.mode_flags = 0; + open.src_endpoint_type = 0; + open.sink_endpoint_type = 0; + open.src_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + open.sink_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; + /* source endpoint : matrix */ + open.audproc_topo_id = q6asm_get_asm_topology_cal(); + + ac->app_type = q6asm_get_asm_app_type_cal(); + if (ac->perf_mode == LOW_LATENCY_PCM_MODE) + open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION; + else + open.mode_flags |= ASM_LEGACY_STREAM_SESSION; + ac->topology = open.audproc_topo_id; + open.bits_per_sample = bits_per_sample; + open.reserved = 0; + pr_debug("%s: opening a transcode_loopback with mode_flags =[%d] session[%d]\n", + __func__, open.mode_flags, ac->session); + + 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; + } + } else {/*if(ac->perf_mode == LEGACY_PCM_MODE)*/ + struct asm_stream_cmd_open_loopback_v2 open; + + 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; + pr_debug("%s: opening a loopback_v2 with mode_flags =[%d] session[%d]\n", + __func__, open.mode_flags, ac->session); + + 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; +} + + +int q6asm_open_transcode_loopback(struct audio_client *ac, + uint16_t bits_per_sample, + uint32_t source_format, uint32_t sink_format) +{ + int rc = 0x00; + struct asm_stream_cmd_open_transcode_loopback_t 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_TRANSCODE_LOOPBACK; + + open.mode_flags = 0; + open.src_endpoint_type = 0; + open.sink_endpoint_type = 0; + switch (source_format) { + case FORMAT_LINEAR_PCM: + case FORMAT_MULTI_CHANNEL_LINEAR_PCM: + open.src_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; + break; + case FORMAT_AC3: + open.src_format_id = ASM_MEDIA_FMT_AC3; + break; + case FORMAT_EAC3: + open.src_format_id = ASM_MEDIA_FMT_EAC3; + break; + default: + pr_err("%s: Unsupported src fmt [%d]\n", + __func__, source_format); + return -EINVAL; + } + switch (sink_format) { + case FORMAT_LINEAR_PCM: + case FORMAT_MULTI_CHANNEL_LINEAR_PCM: + open.sink_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; + break; + default: + pr_err("%s: Unsupported sink fmt [%d]\n", + __func__, sink_format); + return -EINVAL; + } + + /* source endpoint : matrix */ + open.audproc_topo_id = q6asm_get_asm_topology_cal(); + + ac->app_type = q6asm_get_asm_app_type_cal(); + if (ac->perf_mode == LOW_LATENCY_PCM_MODE) + open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION; + else + open.mode_flags |= ASM_LEGACY_STREAM_SESSION; + ac->topology = open.audproc_topo_id; + open.bits_per_sample = bits_per_sample; + open.reserved = 0; + pr_debug("%s: opening a transcode_loopback with mode_flags =[%d] session[%d]\n", + __func__, open.mode_flags, ac->session); + + 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_transcode_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 = + msm_audio_populate_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 = + msm_audio_populate_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 = + msm_audio_populate_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 = + msm_audio_populate_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 == 7) { + /* + * Configured for 5.1 channel mapping + 1 channel for debug + * Can be customized based on DSP. + */ + 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; + lchannel_mapping[6] = PCM_CHANNEL_CS; + } 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); + +/* + * q6asm_media_format_block_gen_compr - set up generic compress format params + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + * @use_default_chmap: true if default channel map to be used + * @channel_map: input channel map + * @bits_per_sample: bit width of gen compress stream + */ +int q6asm_media_format_block_gen_compr(struct audio_client *ac, + uint32_t rate, uint32_t channels, + bool use_default_chmap, char *channel_map, + uint16_t bits_per_sample) +{ + struct asm_generic_compressed_fmt_blk_t fmt; + u8 *channel_mapping; + int rc = 0; + + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]\n", + __func__, ac->session, rate, + channels, bits_per_sample); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + + 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.sampling_rate = rate; + + 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); + } + + atomic_set(&ac->cmd_state, -1); + 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)); + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_gen_compr); + + +/* + * q6asm_media_format_block_iec - set up IEC61937 (compressed) or IEC60958 + * (pcm) format params. Both audio standards + * use the same format and are used for + * HDMI or SPDIF. + * + * @ac: Client session handle + * @rate: sample rate + * @channels: number of channels + */ +int q6asm_media_format_block_iec(struct audio_client *ac, + uint32_t rate, uint32_t channels) +{ + struct asm_iec_compressed_fmt_blk_t fmt; + int rc = 0; + + pr_debug("%s: session[%d]rate[%d]ch[%d]\n", + __func__, ac->session, rate, + channels); + + memset(&fmt, 0, sizeof(fmt)); + q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE); + + fmt.hdr.opcode = ASM_DATA_CMD_IEC_60958_MEDIA_FMT; + fmt.num_channels = channels; + fmt.sampling_rate = rate; + + atomic_set(&ac->cmd_state, -1); + 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)); + } + return 0; +fail_cmd: + return rc; +} +EXPORT_SYMBOL(q6asm_media_format_block_iec); + +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); + +int q6asm_stream_media_format_block_aptx_dec(struct audio_client *ac, + uint32_t srate, int stream_id) +{ + struct asm_aptx_dec_fmt_blk_v2 aptx_fmt; + int rc = 0; + + if (!ac->session) { + pr_err("%s: ac session invalid\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + pr_debug("%s :session[%d] rate[%d] stream_id[%d]\n", + __func__, ac->session, srate, stream_id); + + q6asm_stream_add_hdr(ac, &aptx_fmt.hdr, sizeof(aptx_fmt), TRUE, + stream_id); + atomic_set(&ac->cmd_state, -1); + + aptx_fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; + aptx_fmt.fmtblk.fmt_blk_size = sizeof(aptx_fmt) - sizeof(aptx_fmt.hdr) - + sizeof(aptx_fmt.fmtblk); + + aptx_fmt.sample_rate = srate; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &aptx_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; + } + rc = 0; +fail_cmd: + return rc; +} + +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_pp, -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_pp) >= 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_pp) > 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_pp)), + multi_ch_gain.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state_pp)); + 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_pp, -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_pp) >= 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_pp) > 0) { + pr_err("%s: DSP returned error[%d] , set-params paramid[0x%x]\n", + __func__, atomic_read(&ac->cmd_state_pp), + 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_pp, -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_pp) >= 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_pp) > 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_pp)), + mute.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state_pp)); + goto fail_cmd; + } + rc = 0; +fail_cmd: + 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_pp, -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_pp) >= 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_pp) > 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_pp)), + vol.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state_pp)); + 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_aptx_dec_bt_addr(struct audio_client *ac, + struct aptx_dec_bt_addr_cfg *cfg) +{ + struct aptx_dec_bt_dev_addr paylod; + int sz = 0; + int rc = 0; + + pr_debug("%s: BT addr nap %d, uap %d, lap %d\n", __func__, cfg->nap, + cfg->uap, cfg->lap); + + if (ac == NULL) { + pr_err("%s: AC 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 aptx_dec_bt_dev_addr); + q6asm_add_hdr_async(ac, &paylod.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + paylod.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; + paylod.encdec.param_id = APTX_DECODER_BT_ADDRESS; + paylod.encdec.param_size = sz - sizeof(paylod.hdr) + - sizeof(paylod.encdec); + paylod.bt_addr_cfg.lap = cfg->lap; + paylod.bt_addr_cfg.uap = cfg->uap; + paylod.bt_addr_cfg.nap = cfg->nap; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &paylod); + if (rc < 0) { + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, paylod.encdec.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__, + paylod.encdec.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)), + paylod.encdec.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + pr_debug("%s: set BT addr is success\n", __func__); + rc = 0; +fail_cmd: + return rc; +} + +int q6asm_send_ion_fd(struct audio_client *ac, int fd) +{ + struct ion_client *client; + struct ion_handle *handle; + ion_phys_addr_t paddr; + size_t pa_len = 0; + void *vaddr; + int ret; + int sz = 0; + struct avs_rtic_shared_mem_addr shm; + + if (ac == NULL) { + pr_err("%s: APR handle NULL\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + ret = -EINVAL; + goto fail_cmd; + } + + ret = msm_audio_ion_import("audio_mem_client", + &client, + &handle, + fd, + NULL, + 0, + &paddr, + &pa_len, + &vaddr); + if (ret) { + pr_err("%s: audio ION import failed, rc = %d\n", + __func__, ret); + ret = -ENOMEM; + goto fail_cmd; + } + /* get payload length */ + sz = sizeof(struct avs_rtic_shared_mem_addr); + q6asm_add_hdr_async(ac, &shm.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + shm.shm_buf_addr_lsw = lower_32_bits(paddr); + shm.shm_buf_addr_msw = msm_audio_populate_upper_32_bits(paddr); + shm.buf_size = pa_len; + shm.shm_buf_num_regions = 1; + shm.shm_buf_mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL; + shm.shm_buf_flag = 0x00; + shm.encdec.param_id = AVS_PARAM_ID_RTIC_SHARED_MEMORY_ADDR; + shm.encdec.param_size = sizeof(struct avs_rtic_shared_mem_addr) - + sizeof(struct apr_hdr) - + sizeof(struct asm_stream_cmd_set_encdec_param_v2); + shm.encdec.service_id = OUT; + shm.encdec.reserved = 0; + shm.map_region.shm_addr_lsw = shm.shm_buf_addr_lsw; + shm.map_region.shm_addr_msw = shm.shm_buf_addr_msw; + shm.map_region.mem_size_bytes = pa_len; + shm.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM_V2; + ret = apr_send_pkt(ac->apr, (uint32_t *) &shm); + if (ret < 0) { + pr_err("%s: set-params send failed paramid[0x%x] rc %d\n", + __func__, shm.encdec.param_id, ret); + ret = -EINVAL; + goto fail_cmd; + } + + ret = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 1*HZ); + if (!ret) { + pr_err("%s: timeout, shm.encdec paramid[0x%x]\n", __func__, + shm.encdec.param_id); + ret = -ETIMEDOUT; + goto fail_cmd; + } + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] shm.encdec paramid[0x%x]\n", + __func__, + adsp_err_get_err_str(atomic_read(&ac->cmd_state)), + shm.encdec.param_id); + ret = adsp_err_get_lnx_err_code(atomic_read(&ac->cmd_state)); + goto fail_cmd; + } + ret = 0; +fail_cmd: + return ret; +} + +int q6asm_send_rtic_event_ack(struct audio_client *ac, + void *param, uint32_t params_length) +{ + char *asm_params = NULL; + int sz, rc; + struct avs_param_rtic_event_ack ack; + + if (!param || !ac) { + pr_err("%s: %s is NULL\n", __func__, + (!param) ? "param" : "ac"); + rc = -EINVAL; + goto done; + } + + sz = sizeof(struct avs_param_rtic_event_ack) + params_length; + asm_params = kzalloc(sz, GFP_KERNEL); + if (!asm_params) { + rc = -ENOMEM; + goto done; + } + + q6asm_add_hdr_async(ac, &ack.hdr, + sizeof(struct avs_param_rtic_event_ack) + + params_length, TRUE); + atomic_set(&ac->cmd_state, -1); + ack.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM_V2; + ack.encdec.param_id = AVS_PARAM_ID_RTIC_EVENT_ACK; + ack.encdec.param_size = params_length; + ack.encdec.reserved = 0; + ack.encdec.service_id = OUT; + memcpy(asm_params, &ack, sizeof(struct avs_param_rtic_event_ack)); + memcpy(asm_params + sizeof(struct avs_param_rtic_event_ack), + param, params_length); + rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params); + if (rc < 0) { + pr_err("%s: apr pkt failed for rtic event ack\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 for rtic event ack cmd\n", __func__); + rc = -ETIMEDOUT; + goto fail_send_param; + } + + if (atomic_read(&ac->cmd_state) > 0) { + pr_err("%s: DSP returned error[%s] for rtic event ack cmd\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); +done: + return rc; +} + +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_pp, -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_pp) >= 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_pp) > 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_pp)), + softpause.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state_pp)); + 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_pp, -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_pp) >= 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_pp) > 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_pp)), + softvol.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state_pp)); + 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_pp, -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_pp) >= 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_pp) > 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_pp)), + eq.data.param_id); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state_pp)); + 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), TRUE, 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 { + if (param->flags & SET_TIMESTAMP) + lbuf_phys_addr = param->paddr - + sizeof(struct snd_codec_metadata); + 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 */ + NO_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 */ + NO_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_pp, -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_pp) >= 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_pp) > 0) { + pr_err("%s: DSP returned error[%s] set-params\n", + __func__, adsp_err_get_err_str( + atomic_read(&ac->cmd_state_pp))); + rc = adsp_err_get_lnx_err_code( + atomic_read(&ac->cmd_state_pp)); + 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(struct asm_stream_param_data_v2) + + sizeof(struct asm_session_mtmx_strtr_param_window_v2_t); + 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 = + sizeof(struct asm_session_mtmx_strtr_param_window_v2_t); + matrix.data.reserved = 0; + memcpy(&(matrix.config.window_param), + window_param, + sizeof(struct asm_session_mtmx_strtr_param_window_v2_t)); + + 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; +} + +int q6asm_send_mtmx_strtr_render_mode(struct audio_client *ac, + uint32_t render_mode) +{ + struct asm_mtmx_strtr_params matrix; + struct asm_session_mtmx_strtr_param_render_mode_t render_param; + int sz = 0; + int rc = 0; + + pr_debug("%s: render mode is %d\n", __func__, render_mode); + + if (!ac) { + pr_err("%s: audio client handle is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + if (ac->apr == NULL) { + pr_err("%s: ac->apr is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + if ((render_mode != ASM_SESSION_MTMX_STRTR_PARAM_RENDER_DEFAULT) && + (render_mode != ASM_SESSION_MTMX_STRTR_PARAM_RENDER_LOCAL_STC)) { + pr_err("%s: Invalid render mode %d\n", __func__, render_mode); + rc = -EINVAL; + goto exit; + } + + memset(&render_param, 0, + sizeof(struct asm_session_mtmx_strtr_param_render_mode_t)); + render_param.flags = render_mode; + + memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params)); + 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(struct asm_stream_param_data_v2) + + sizeof(struct asm_session_mtmx_strtr_param_render_mode_t); + matrix.param.direction = 0; /* RX */ + matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC; + matrix.data.param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_MODE_CMD; + matrix.data.param_size = + sizeof(struct asm_session_mtmx_strtr_param_render_mode_t); + matrix.data.reserved = 0; + memcpy(&(matrix.config.render_param), + &render_param, + sizeof(struct asm_session_mtmx_strtr_param_render_mode_t)); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix); + if (rc < 0) { + pr_err("%s: Render mode send failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -EINVAL; + goto exit; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout, Render mode send paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -ETIMEDOUT; + goto exit; + } + + 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 exit; + } + rc = 0; +exit: + return rc; +} + +int q6asm_send_mtmx_strtr_clk_rec_mode(struct audio_client *ac, + uint32_t clk_rec_mode) +{ + struct asm_mtmx_strtr_params matrix; + struct asm_session_mtmx_strtr_param_clk_rec_t clk_rec_param; + int sz = 0; + int rc = 0; + + pr_debug("%s: clk rec mode is %d\n", __func__, clk_rec_mode); + + if (!ac) { + pr_err("%s: audio client handle is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + if (ac->apr == NULL) { + pr_err("%s: ac->apr is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + if ((clk_rec_mode != ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_NONE) && + (clk_rec_mode != ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_AUTO)) { + pr_err("%s: Invalid clk rec mode %d\n", __func__, clk_rec_mode); + rc = -EINVAL; + goto exit; + } + + memset(&clk_rec_param, 0, + sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t)); + clk_rec_param.flags = clk_rec_mode; + + memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params)); + 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(struct asm_stream_param_data_v2) + + sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t); + matrix.param.direction = 0; /* RX */ + matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC; + matrix.data.param_id = ASM_SESSION_MTMX_STRTR_PARAM_CLK_REC_CMD; + matrix.data.param_size = + sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t); + matrix.data.reserved = 0; + memcpy(&(matrix.config.clk_rec_param), + &clk_rec_param, + sizeof(struct asm_session_mtmx_strtr_param_clk_rec_t)); + + rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix); + if (rc < 0) { + pr_err("%s: clk rec mode send failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -EINVAL; + goto exit; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: timeout, clk rec mode send paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -ETIMEDOUT; + goto exit; + } + + 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 exit; + } + rc = 0; +exit: + return rc; +} + +int q6asm_send_mtmx_strtr_enable_adjust_session_clock(struct audio_client *ac, + bool enable) +{ + struct asm_mtmx_strtr_params matrix; + struct asm_session_mtmx_param_adjust_session_time_ctl_t adjust_time; + int sz = 0; + int rc = 0; + + pr_debug("%s: adjust session enable %d\n", __func__, enable); + + if (!ac) { + pr_err("%s: audio client handle is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + if (ac->apr == NULL) { + pr_err("%s: ac->apr is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + adjust_time.enable = enable; + memset(&matrix, 0, sizeof(struct asm_mtmx_strtr_params)); + 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(struct asm_stream_param_data_v2) + + sizeof(struct asm_session_mtmx_param_adjust_session_time_ctl_t); + matrix.param.direction = 0; /* RX */ + matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC; + matrix.data.param_id = ASM_SESSION_MTMX_PARAM_ADJUST_SESSION_TIME_CTL; + matrix.data.param_size = + sizeof(struct asm_session_mtmx_param_adjust_session_time_ctl_t); + matrix.data.reserved = 0; + matrix.config.adj_time_param.enable = adjust_time.enable; + + rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix); + if (rc < 0) { + pr_err("%s: enable adjust session failed failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -EINVAL; + goto exit; + } + + rc = wait_event_timeout(ac->cmd_wait, + (atomic_read(&ac->cmd_state) >= 0), 5*HZ); + if (!rc) { + pr_err("%s: enable adjust session failed failed paramid [0x%x]\n", + __func__, matrix.data.param_id); + rc = -ETIMEDOUT; + goto exit; + } + + 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 exit; + } + rc = 0; +exit: + 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 */ + NO_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), TRUE, + 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 */ + NO_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; +} + +int q6asm_adjust_session_clock(struct audio_client *ac, + uint32_t adjust_time_lsw, + uint32_t adjust_time_msw) +{ + int rc = 0; + int sz = 0; + struct asm_session_cmd_adjust_session_clock_v2 adjust_clock; + + pr_debug("%s: adjust_time_lsw is %x, adjust_time_msw is %x\n", __func__, + adjust_time_lsw, adjust_time_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_session_cmd_adjust_session_clock_v2); + q6asm_add_hdr(ac, &adjust_clock.hdr, sz, TRUE); + atomic_set(&ac->cmd_state, -1); + adjust_clock.hdr.opcode = ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2; + + adjust_clock.adjustime_lsw = adjust_time_lsw; + adjust_clock.adjustime_msw = adjust_time_msw; + + + rc = apr_send_pkt(ac->apr, (uint32_t *) &adjust_clock); + if (rc < 0) { + pr_err("%s: adjust_clock send failed paramid [0x%x]\n", + __func__, adjust_clock.hdr.opcode); + 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, adjust_clock paramid[0x%x]\n", + __func__, adjust_clock.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; + } + rc = 0; +fail_cmd: + return rc; +} + +/* + * 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 = -EINVAL; + + if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) { + pr_err("%s: invalid session_id = %d\n", __func__, session_id); + goto done; + } + if (session[session_id] == NULL) { + pr_err("%s: session not created for session id = %d\n", + __func__, session_id); + goto done; + } + topology = session[session_id]->topology; +done: + return topology; +} + +int q6asm_get_asm_app_type(int session_id) +{ + int app_type = -EINVAL; + + if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) { + pr_err("%s: invalid session_id = %d\n", __func__, session_id); + goto done; + } + if (session[session_id] == NULL) { + pr_err("%s: session not created for session id = %d\n", + __func__, session_id); + 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_pp, -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_pp) >= 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_pp) > 0) { + pr_err("%s: DSP returned error[%d] audio audstrm cal send\n", + __func__, atomic_read(&ac->cmd_state_pp)); + 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..3aaaa35e535b --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6core.c @@ -0,0 +1,853 @@ +/* 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 + +#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("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__); +} + +struct cal_block_data *cal_utils_get_cal_block_by_key( + struct cal_type_data *cal_type, uint32_t key) +{ + struct list_head *ptr, *next; + struct cal_block_data *cal_block = NULL; + struct audio_cal_info_metainfo *metainfo; + + list_for_each_safe(ptr, next, + &cal_type->cal_blocks) { + + cal_block = list_entry(ptr, + struct cal_block_data, list); + metainfo = (struct audio_cal_info_metainfo *) + cal_block->cal_info; + if (metainfo->nKey != key) { + pr_debug("%s: metainfo key mismatch!!! found:%x, needed:%x\n", + __func__, metainfo->nKey, key); + } else { + pr_debug("%s: metainfo key match found", __func__); + return cal_block; + } + } + return NULL; +} + +int32_t core_set_license(uint32_t key, uint32_t module_id) +{ + struct avcs_cmd_set_license *cmd_setl = 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_cal_block_by_key( + q6core_lcl.cal_data[META_CAL], key); + 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; + } + + 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, id =0x%x, size = %d\n", + __func__, cmd_setl->hdr.opcode, + 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; +} + +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; +} + +/** + * q6core_is_adsp_ready - check adsp ready status + * + * Returns true if adsp is ready otherwise returns false + */ +bool q6core_is_adsp_ready(void) +{ + int rc = 0; + 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(); + if (q6core_lcl.core_handle_q) { + 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; +} +EXPORT_SYMBOL(q6core_is_adsp_ready); + +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..799d1be97ffb --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6lsm.c @@ -0,0 +1,2173 @@ +/* + * 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 +#define QLSM_PARAM_ID_MINOR_VERSION_2 2 + +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_param_opmode(struct lsm_client *client, + struct lsm_module_param_ids *opmode_ids, + u32 set_param_opcode) +{ + int rc; + struct lsm_cmd_set_params_opmode opmode_params; + struct apr_hdr *msg_hdr; + + struct lsm_param_op_mode *op_mode; + u32 data_payload_size, param_size; + + msg_hdr = &opmode_params.msg_hdr; + q6lsm_add_hdr(client, msg_hdr, + sizeof(opmode_params), true); + msg_hdr->opcode = set_param_opcode; + data_payload_size = sizeof(opmode_params) - + sizeof(*msg_hdr) - + sizeof(opmode_params.params_hdr); + q6lsm_set_param_hdr_info(&opmode_params.params_hdr, + data_payload_size, 0, 0, 0); + op_mode = &opmode_params.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); + + rc = q6lsm_apr_send_pkt(client, client->apr, + &opmode_params, 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_port_connected(struct lsm_client *client) +{ + int rc; + struct lsm_cmd_set_connectport connectport; + struct lsm_module_param_ids connectport_ids; + struct apr_hdr *msg_hdr; + struct lsm_param_connect_to_port *connect_to_port; + u32 data_payload_size, param_size, set_param_opcode; + + if (client->use_topology) { + set_param_opcode = LSM_SESSION_CMD_SET_PARAMS_V2; + connectport_ids.module_id = LSM_MODULE_ID_FRAMEWORK; + connectport_ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT; + } else { + set_param_opcode = LSM_SESSION_CMD_SET_PARAMS; + connectport_ids.module_id = LSM_MODULE_ID_VOICE_WAKEUP; + connectport_ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT; + } + client->connect_to_port = get_lsm_port(); + + msg_hdr = &connectport.msg_hdr; + q6lsm_add_hdr(client, msg_hdr, + sizeof(connectport), true); + msg_hdr->opcode = set_param_opcode; + data_payload_size = sizeof(connectport) - + sizeof(*msg_hdr) - + sizeof(connectport.params_hdr); + q6lsm_set_param_hdr_info(&connectport.params_hdr, + data_payload_size, 0, 0, 0); + connect_to_port = &connectport.connect_to_port; + + 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, + &connectport, true, NULL); + if (rc) + pr_err("%s: Failed set_params opcode 0x%x, rc %d\n", + __func__, msg_hdr->opcode, rc); + + return rc; +} +static int q6lsm_send_param_polling_enable(struct lsm_client *client, + bool poll_en, + struct lsm_module_param_ids *poll_enable_ids, + u32 set_param_opcode) +{ + int rc = 0; + struct lsm_cmd_poll_enable cmd; + struct apr_hdr *msg_hdr; + struct lsm_param_poll_enable *poll_enable; + u32 data_payload_size, param_size; + + msg_hdr = &cmd.msg_hdr; + q6lsm_add_hdr(client, msg_hdr, + sizeof(struct lsm_cmd_poll_enable), true); + msg_hdr->opcode = set_param_opcode; + data_payload_size = sizeof(struct lsm_cmd_poll_enable) - + sizeof(struct apr_hdr) - + sizeof(struct lsm_set_params_hdr); + q6lsm_set_param_hdr_info(&cmd.params_hdr, + data_payload_size, 0, 0, 0); + poll_enable = &cmd.poll_enable; + + param_size = (sizeof(struct lsm_param_poll_enable) - + sizeof(poll_enable->common)); + q6lsm_set_param_common(&poll_enable->common, + poll_enable_ids, param_size, + set_param_opcode); + poll_enable->minor_version = QLSM_PARAM_ID_MINOR_VERSION; + poll_enable->polling_enable = (poll_en) ? 1 : 0; + pr_debug("%s: poll enable= %d", __func__, poll_enable->polling_enable); + + rc = q6lsm_apr_send_pkt(client, client->apr, + &cmd, true, NULL); + if (rc) + pr_err("%s: Failed set_params opcode 0x%x, rc %d\n", + __func__, msg_hdr->opcode, rc); + + return rc; +} + +int q6lsm_set_fwk_mode_cfg(struct lsm_client *client, + uint32_t event_mode) +{ + int rc = 0; + struct lsm_cmd_set_fwk_mode_cfg cmd; + struct lsm_module_param_ids fwk_mode_cfg_ids; + struct apr_hdr *msg_hdr; + struct lsm_param_fwk_mode_cfg *fwk_mode_cfg; + u32 data_payload_size, param_size, set_param_opcode; + + if (client->use_topology) { + set_param_opcode = LSM_SESSION_CMD_SET_PARAMS_V2; + fwk_mode_cfg_ids.module_id = LSM_MODULE_ID_FRAMEWORK; + fwk_mode_cfg_ids.param_id = LSM_PARAM_ID_FWK_MODE_CONFIG; + } else { + pr_debug("%s: Ignore sending event mode\n", __func__); + return rc; + } + + msg_hdr = &cmd.msg_hdr; + q6lsm_add_hdr(client, msg_hdr, + sizeof(struct lsm_cmd_set_fwk_mode_cfg), true); + msg_hdr->opcode = set_param_opcode; + data_payload_size = sizeof(struct lsm_cmd_set_fwk_mode_cfg) - + sizeof(struct apr_hdr) - + sizeof(struct lsm_set_params_hdr); + q6lsm_set_param_hdr_info(&cmd.params_hdr, + data_payload_size, 0, 0, 0); + fwk_mode_cfg = &cmd.fwk_mode_cfg; + + param_size = (sizeof(struct lsm_param_fwk_mode_cfg) - + sizeof(fwk_mode_cfg->common)); + q6lsm_set_param_common(&fwk_mode_cfg->common, + &fwk_mode_cfg_ids, param_size, + set_param_opcode); + + fwk_mode_cfg->minor_version = QLSM_PARAM_ID_MINOR_VERSION; + fwk_mode_cfg->mode = event_mode; + pr_debug("%s: mode = %d\n", __func__, fwk_mode_cfg->mode); + + rc = q6lsm_apr_send_pkt(client, client->apr, + &cmd, true, NULL); + if (rc) + pr_err("%s: Failed set_params opcode 0x%x, rc %d\n", + __func__, msg_hdr->opcode, rc); + return rc; +} + +static int q6lsm_arrange_mch_map(struct lsm_param_media_fmt *media_fmt, + int channel_count) +{ + int rc = 0; + + memset(media_fmt->channel_mapping, 0, LSM_MAX_NUM_CHANNELS); + + switch (channel_count) { + case 1: + media_fmt->channel_mapping[0] = PCM_CHANNEL_FC; + break; + case 2: + media_fmt->channel_mapping[0] = PCM_CHANNEL_FL; + media_fmt->channel_mapping[1] = PCM_CHANNEL_FR; + break; + case 3: + media_fmt->channel_mapping[0] = PCM_CHANNEL_FL; + media_fmt->channel_mapping[1] = PCM_CHANNEL_FR; + media_fmt->channel_mapping[2] = PCM_CHANNEL_FC; + break; + case 4: + media_fmt->channel_mapping[0] = PCM_CHANNEL_FL; + media_fmt->channel_mapping[1] = PCM_CHANNEL_FR; + media_fmt->channel_mapping[2] = PCM_CHANNEL_LS; + media_fmt->channel_mapping[3] = PCM_CHANNEL_RS; + break; + default: + pr_err("%s: invalid num_chan %d\n", __func__, channel_count); + rc = -EINVAL; + break; + } + return rc; +} + +int q6lsm_set_media_fmt_params(struct lsm_client *client) +{ + int rc = 0; + struct lsm_cmd_set_media_fmt cmd; + struct lsm_module_param_ids media_fmt_ids; + struct apr_hdr *msg_hdr; + struct lsm_param_media_fmt *media_fmt; + u32 data_payload_size, param_size, set_param_opcode; + struct lsm_hw_params param = client->hw_params; + + if (client->use_topology) { + set_param_opcode = LSM_SESSION_CMD_SET_PARAMS_V2; + media_fmt_ids.module_id = LSM_MODULE_ID_FRAMEWORK; + media_fmt_ids.param_id = LSM_PARAM_ID_MEDIA_FMT; + } else { + pr_debug("%s: Ignore sending media format\n", __func__); + goto err_ret; + } + + msg_hdr = &cmd.msg_hdr; + q6lsm_add_hdr(client, msg_hdr, + sizeof(struct lsm_cmd_set_media_fmt), true); + msg_hdr->opcode = set_param_opcode; + data_payload_size = sizeof(struct lsm_cmd_set_media_fmt) - + sizeof(struct apr_hdr) - + sizeof(struct lsm_set_params_hdr); + q6lsm_set_param_hdr_info(&cmd.params_hdr, + data_payload_size, 0, 0, 0); + media_fmt = &cmd.media_fmt; + + param_size = (sizeof(struct lsm_param_media_fmt) - + sizeof(media_fmt->common)); + q6lsm_set_param_common(&media_fmt->common, + &media_fmt_ids, param_size, + set_param_opcode); + + media_fmt->minor_version = QLSM_PARAM_ID_MINOR_VERSION_2; + media_fmt->sample_rate = param.sample_rate; + media_fmt->num_channels = param.num_chs; + media_fmt->bit_width = param.sample_size; + + rc = q6lsm_arrange_mch_map(media_fmt, media_fmt->num_channels); + if (rc) + goto err_ret; + + pr_debug("%s: sample rate= %d, channels %d bit width %d\n", + __func__, media_fmt->sample_rate, media_fmt->num_channels, + media_fmt->bit_width); + + rc = q6lsm_apr_send_pkt(client, client->apr, + &cmd, true, NULL); + if (rc) + pr_err("%s: Failed set_params opcode 0x%x, rc %d\n", + __func__, msg_hdr->opcode, rc); +err_ret: + return rc; +} + +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; + 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; + + opmode_ids.module_id = LSM_MODULE_ID_VOICE_WAKEUP; + opmode_ids.param_id = LSM_PARAM_ID_OPERATION_MODE; + + rc = q6lsm_send_param_opmode(client, &opmode_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 || cal_block->cal_data.size <= 0) { + pr_debug("%s: No cal to send!\n", __func__); + 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, + uint32_t 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; + + 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; + + opmode_ids.module_id = p_info->module_id; + opmode_ids.param_id = p_info->param_id; + + rc = q6lsm_send_param_opmode(client, &opmode_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_POLLING_ENABLE: { + struct snd_lsm_poll_enable *lsm_poll_enable = + (struct snd_lsm_poll_enable *) data; + ids.module_id = p_info->module_id; + ids.param_id = p_info->param_id; + rc = q6lsm_send_param_polling_enable(client, + lsm_poll_enable->poll_en, &ids, + LSM_SESSION_CMD_SET_PARAMS_V2); + if (rc) + pr_err("%s: POLLING ENABLE 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..15c9e130f4f4 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6voice.c @@ -0,0 +1,8661 @@ +/* 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_media_fmt_info_cmd(struct voice_data *v); +static int voice_send_cvp_device_channels_cmd(struct voice_data *v); +static int voice_send_cvp_media_format_cmd(struct voice_data *v, + uint32_t param_type); +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 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; + v->rec_info.recording = 0; + } + } +} + +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; +} + +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; +} + +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; + } + 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_media_fmt_info.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_media_fmt_info.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_media_fmt_info_cmd(v); + if (ret < 0) { + pr_err("%s: Set media format info 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_media_fmt_info_cmd(struct voice_data *v) +{ + int ret; + + ret = voice_send_cvp_device_channels_cmd(v); + if (ret < 0) + goto done; + + if (voice_get_cvd_int_version(common.cvd_version) >= + CVD_INT_VERSION_2_3) { + ret = voice_send_cvp_media_format_cmd(v, RX_PATH); + if (ret < 0) + goto done; + + ret = voice_send_cvp_media_format_cmd(v, TX_PATH); + if (ret < 0) + goto done; + + if (common.ec_ref_ext) + ret = voice_send_cvp_media_format_cmd(v, EC_REF_PATH); + } + +done: + return ret; +} + +static int voice_send_cvp_media_format_cmd(struct voice_data *v, + uint32_t param_type) +{ + int ret = 0; + struct cvp_set_media_format_cmd cvp_set_media_format_cmd; + void *apr_cvp; + u16 cvp_handle; + struct vss_icommon_param_data_t *media_fmt_param_data = + &cvp_set_media_format_cmd.cvp_set_param_v2.param_data; + struct vss_param_endpoint_media_format_info_t *media_fmt_info = + &media_fmt_param_data->media_format_info; + + 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); + memset(&cvp_set_media_format_cmd, 0, sizeof(cvp_set_media_format_cmd)); + + /* Fill header data */ + cvp_set_media_format_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_set_media_format_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_set_media_format_cmd) - APR_HDR_SIZE); + cvp_set_media_format_cmd.hdr.src_svc = 0; + cvp_set_media_format_cmd.hdr.src_domain = APR_DOMAIN_APPS; + cvp_set_media_format_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_set_media_format_cmd.hdr.dest_svc = 0; + cvp_set_media_format_cmd.hdr.dest_domain = APR_DOMAIN_ADSP; + cvp_set_media_format_cmd.hdr.dest_port = cvp_handle; + cvp_set_media_format_cmd.hdr.token = VOC_SET_MEDIA_FORMAT_PARAM_TOKEN; + cvp_set_media_format_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_PARAM_V2; + + /* Fill param data */ + cvp_set_media_format_cmd.cvp_set_param_v2.mem_size = + sizeof(struct vss_icommon_param_data_t); + media_fmt_param_data->module_id = VSS_MODULE_CVD_GENERIC; + media_fmt_param_data->param_size = + sizeof(struct vss_param_endpoint_media_format_info_t); + + /* Fill device specific data */ + switch (param_type) { + case RX_PATH: + media_fmt_param_data->param_id = + VSS_PARAM_RX_PORT_ENDPOINT_MEDIA_INFO; + media_fmt_info->port_id = v->dev_rx.port_id; + media_fmt_info->num_channels = v->dev_rx.no_of_channels; + media_fmt_info->bits_per_sample = v->dev_rx.bits_per_sample; + media_fmt_info->sample_rate = v->dev_rx.sample_rate; + memcpy(&media_fmt_info->channel_mapping, + &v->dev_rx.channel_mapping, VSS_CHANNEL_MAPPING_SIZE); + break; + + case TX_PATH: + media_fmt_param_data->param_id = + VSS_PARAM_TX_PORT_ENDPOINT_MEDIA_INFO; + media_fmt_info->port_id = v->dev_tx.port_id; + media_fmt_info->num_channels = v->dev_tx.no_of_channels; + media_fmt_info->bits_per_sample = v->dev_tx.bits_per_sample; + media_fmt_info->sample_rate = v->dev_tx.sample_rate; + memcpy(&media_fmt_info->channel_mapping, + &v->dev_tx.channel_mapping, VSS_CHANNEL_MAPPING_SIZE); + break; + + case EC_REF_PATH: + media_fmt_param_data->param_id = + VSS_PARAM_EC_REF_PORT_ENDPOINT_MEDIA_INFO; + media_fmt_info->port_id = common.ec_media_fmt_info.port_id; + media_fmt_info->num_channels = + common.ec_media_fmt_info.num_channels; + media_fmt_info->bits_per_sample = + common.ec_media_fmt_info.bits_per_sample; + media_fmt_info->sample_rate = + common.ec_media_fmt_info.sample_rate; + memcpy(&media_fmt_info->channel_mapping, + &common.ec_media_fmt_info.channel_mapping, + VSS_CHANNEL_MAPPING_SIZE); + break; + + default: + pr_err("%s: Invalid param type %d\n", __func__, param_type); + ret = -EINVAL; + goto done; + } + + /* Send command */ + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_set_media_format_cmd); + if (ret < 0) { + pr_err("%s: Fail in sending VSS_ICOMMON_CMD_SET_PARAM_V2\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] handle = %d\n", __func__, + adsp_err_get_err_str(v->async_err), cvp_handle); + 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_set_afe_sidetone(uint32_t session_id, bool sidetone_enable) +{ + struct voice_data *v = NULL; + int ret = -EINVAL; + struct voice_session_itr itr; + u16 rx_port, tx_port; + + common.sidetone_enable = sidetone_enable; + voice_itr_init(&itr, session_id); + while (voice_itr_get_next_session(&itr, &v)) { + if (v == NULL) { + pr_err("%s: invalid session_id 0x%x\n", __func__, + session_id); + ret = -EINVAL; + break; + } + mutex_lock(&v->lock); + if (v->voc_state != VOC_RUN) { + mutex_unlock(&v->lock); + continue; + } + rx_port = v->dev_rx.port_id; + tx_port = v->dev_tx.port_id; + ret = afe_sidetone_enable(tx_port, rx_port, + sidetone_enable); + if (!ret) { + mutex_unlock(&v->lock); + break; + } + mutex_unlock(&v->lock); + } + return ret; +} + +bool voc_get_afe_sidetone(void) +{ + bool ret; + + ret = common.sidetone_enable; + 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, + struct media_format_info *finfo) +{ + 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, sample_rate=%d, bits_per_sample=%d\n", + __func__, path_dir, finfo->port_id, finfo->num_channels, + finfo->sample_rate, finfo->bits_per_sample); + + mutex_lock(&v->lock); + switch (path_dir) { + case RX_PATH: + v->dev_rx.port_id = q6audio_get_port_id(finfo->port_id); + v->dev_rx.no_of_channels = finfo->num_channels; + v->dev_rx.sample_rate = finfo->sample_rate; + v->dev_rx.bits_per_sample = finfo->bits_per_sample; + memcpy(&v->dev_rx.channel_mapping, &finfo->channel_mapping, + VSS_CHANNEL_MAPPING_SIZE); + break; + case TX_PATH: + v->dev_tx.port_id = q6audio_get_port_id(finfo->port_id); + v->dev_tx.no_of_channels = finfo->num_channels; + v->dev_tx.sample_rate = finfo->sample_rate; + v->dev_tx.bits_per_sample = finfo->bits_per_sample; + memcpy(&v->dev_tx.channel_mapping, &finfo->channel_mapping, + VSS_CHANNEL_MAPPING_SIZE); + break; + default: + pr_err("%s: Invalid path_dir %d\n", __func__, path_dir); + return -EINVAL; + } + + mutex_unlock(&v->lock); + + return 0; +} + +int voc_set_ext_ec_ref_media_fmt_info(struct media_format_info *finfo) +{ + mutex_lock(&common.common_lock); + if (common.ec_ref_ext) { + common.ec_media_fmt_info.num_channels = finfo->num_channels; + common.ec_media_fmt_info.bits_per_sample = + finfo->bits_per_sample; + common.ec_media_fmt_info.sample_rate = finfo->sample_rate; + memcpy(&common.ec_media_fmt_info.channel_mapping, + &finfo->channel_mapping, VSS_CHANNEL_MAPPING_SIZE); + } else { + pr_debug("%s: Ext Ec Ref not active, returning", __func__); + } + mutex_unlock(&common.common_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; + } 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_media_fmt_info_cmd(v); + if (ret < 0) { + pr_err("%s: Set format failed err:%d\n", __func__, ret); + 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_port_id(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_ref_ext = true; + } else { + common.ec_ref_ext = false; + } + /* Cache EC Fromat Info in common */ + common.ec_media_fmt_info.port_id = port_id; +exit: + mutex_unlock(&common.common_lock); + return ret; +} + +int voc_get_ext_ec_ref_port_id(void) +{ + if (common.ec_ref_ext) + return common.ec_media_fmt_info.port_id; + else + return AFE_PORT_INVALID; +} + +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 VSS_ICOMMON_CMD_SET_PARAM_V2: + pr_debug("%s: VSS_ICOMMON_CMD_SET_PARAM_V2\n", + __func__); + rtac_make_voice_callback(RTAC_CVS, ptr, + data->payload_size); + break; + case VSS_ICOMMON_CMD_GET_PARAM_V2: + pr_debug("%s: VSS_ICOMMON_CMD_GET_PARAM_V2\n", + __func__); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + /* VSS_ICOMMON_RSP_GET_PARAM */ + 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 == VSS_ICOMMON_RSP_GET_PARAM) { + pr_debug("%s: VSS_ICOMMON_RSP_GET_PARAM\n", __func__); + ptr = data->payload; + if (ptr[0] != 0) { + pr_err("%s: VSS_ICOMMON_RSP_GET_PARAM 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 VSS_ICOMMON_CMD_SET_PARAM_V2: + switch (data->token) { + case VOC_SET_MEDIA_FORMAT_PARAM_TOKEN: + pr_debug("%s: VSS_ICOMMON_CMD_SET_PARAM_V2 called by voice_send_cvp_media_format_cmd\n", + __func__); + v->cvp_state = CMD_STATUS_SUCCESS; + v->async_err = ptr[1]; + wake_up(&v->cvp_wait); + break; + case VOC_RTAC_SET_PARAM_TOKEN: + pr_debug("%s: VSS_ICOMMON_CMD_SET_PARAM_V2 called by rtac\n", + __func__); + rtac_make_voice_callback( + RTAC_CVP, ptr, + data->payload_size); + break; + default: + pr_debug("%s: invalid token for command VSS_ICOMMON_CMD_SET_PARAM_V2: %d\n", + __func__, data->token); + break; + } + break; + case VSS_ICOMMON_CMD_GET_PARAM_V2: + pr_debug("%s: VSS_ICOMMON_CMD_GET_PARAM_V2\n", + __func__); + /* Should only come here if there is an APR */ + /* error or malformed APR packet. Otherwise */ + /* response will be returned as */ + /* VSS_ICOMMON_RSP_GET_PARAM */ + 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 == VSS_ICOMMON_RSP_GET_PARAM) { + pr_debug("%s: VSS_ICOMMON_RSP_GET_PARAM\n", __func__); + ptr = data->payload; + if (ptr[0] != 0) { + pr_err("%s: VSS_ICOMMON_RSP_GET_PARAM 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; + + /* Initialize EC Ref media format info */ + common.ec_ref_ext = false; + common.ec_media_fmt_info.port_id = AFE_PORT_INVALID; + common.ec_media_fmt_info.num_channels = 0; + common.ec_media_fmt_info.bits_per_sample = 16; + common.ec_media_fmt_info.sample_rate = 8000; + memset(&common.ec_media_fmt_info.channel_mapping, 0, + VSS_CHANNEL_MAPPING_SIZE); + + /* Initialize AFE Sidetone Enable */ + common.sidetone_enable = 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_tx.no_of_channels = 0; + common.voice[i].dev_rx.no_of_channels = 0; + common.voice[i].dev_tx.sample_rate = 8000; + common.voice[i].dev_rx.sample_rate = 8000; + common.voice[i].dev_tx.bits_per_sample = 16; + common.voice[i].dev_rx.bits_per_sample = 16; + memset(&common.voice[i].dev_tx.channel_mapping, 0, + VSS_CHANNEL_MAPPING_SIZE); + memset(&common.voice[i].dev_rx.channel_mapping, 0, + VSS_CHANNEL_MAPPING_SIZE); + 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..74d80be57931 --- /dev/null +++ b/sound/soc/msm/qdsp6v2/q6voice.h @@ -0,0 +1,1900 @@ +/* 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 + +#define MAX_VOC_PKT_SIZE 642 +#define SESSION_NAME_LEN 20 +#define NUM_OF_MEMORY_BLOCKS 1 +#define NUM_OF_BUFFERS 2 +#define VSS_NUM_CHANNELS_MAX 8 +#define VSS_CHANNEL_MAPPING_SIZE (sizeof(uint8_t) * VSS_NUM_CHANNELS_MAX) +/* + * 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_rate; + uint16_t bits_per_sample; + uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; + 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; +}; + +/* + * Format information structure to match + * vss_param_endpoint_media_format_info_t + */ +struct media_format_info { + uint32_t port_id; + uint16_t num_channels; + uint16_t bits_per_sample; + uint32_t sample_rate; + uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; +}; + +enum { + VOC_NO_SET_PARAM_TOKEN = 0, + VOC_RTAC_SET_PARAM_TOKEN, + VOC_SET_MEDIA_FORMAT_PARAM_TOKEN, + VOC_SET_PARAM_TOKEN_MAX +}; + +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; + +struct vss_param_endpoint_media_format_info_t { + /* AFE port ID to which this media format corresponds to. */ + uint32_t port_id; + /* + * Number of channels of data. + * Supported values: 1 to 8 + */ + uint16_t num_channels; + /* + * Bits per sample of data. + * Supported values: 16 and 24 + */ + uint16_t bits_per_sample; + /* + * Sampling rate in Hz. + * Supported values: 8000, 11025, 16000, 22050, 24000, 32000, + * 44100, 48000, 88200, 96000, 176400, and 192000 + */ + uint32_t sample_rate; + /* + * The channel[i] mapping describes channel i. Each element i + * of the array describes channel i inside the data buffer. An + * unused or unknown channel is set to 0. + */ + uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; +} __packed; + +struct vss_icommon_param_data_t { + /* Valid ID of the module. */ + uint32_t module_id; + /* Valid ID of the parameter. */ + uint32_t param_id; + /* + * Data size of the structure relating to the param_id/module_id + * combination in uint8_t bytes. + */ + uint16_t param_size; + /* This field must be set to zero. */ + uint16_t reserved; + /* + * Parameter data payload when inband. Should have size param_size. + * Bit size of payload must be a multiple of 4. + */ + union { + struct vss_param_endpoint_media_format_info_t media_format_info; + }; +} __packed; + +/* Payload structure for the VSS_ICOMMON_CMD_SET_PARAM_V2 command. */ +struct vss_icommon_cmd_set_param_v2_t { + /* + * Pointer to the unique identifier for an address (physical/virtual). + * + * If the parameter data payload is within the message payload + * (in-band), set this field to 0. The parameter data begins at the + * specified data payload address. + * + * If the parameter data is out-of-band, this field is the handle to + * the physical address in the shared memory that holds the parameter + * data. + */ + uint32_t mem_handle; + /* + * Location of the parameter data payload. + * + * The payload is an array of vss_icommon_param_data_t. If the + * mem_handle is 0, this field is ignored. + */ + uint64_t mem_address; + /* Size of the parameter data payload in bytes. */ + uint32_t mem_size; + /* Parameter data payload when the data is inband. */ + struct vss_icommon_param_data_t param_data; +} __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_PARAM_TX_PORT_ENDPOINT_MEDIA_INFO 0x00013253 + +#define VSS_PARAM_RX_PORT_ENDPOINT_MEDIA_INFO 0x00013254 + +#define VSS_PARAM_EC_REF_PORT_ENDPOINT_MEDIA_INFO 0x00013255 + +#define VSS_MODULE_CVD_GENERIC 0x0001316E + +#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_media_format_cmd { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_param_v2_t cvp_set_param_v2; +} __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 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; + struct media_format_info ec_media_fmt_info; + + /* 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; + 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; + bool sidetone_enable; +}; + +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, + EC_REF_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_port_id(uint16_t port_id, bool state); +int voc_get_ext_ec_ref_port_id(void); +int voc_set_ext_ec_ref_media_fmt_info(struct media_format_info *finfo); +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); +int voc_disable_topology(uint32_t session_id, uint32_t disable); +int voc_set_device_config(uint32_t session_id, uint8_t path_dir, + struct media_format_info *finfo); +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); +int voc_set_afe_sidetone(uint32_t session_id, bool sidetone_enable); +bool voc_get_afe_sidetone(void); +#endif diff --git a/sound/soc/msm/qdsp6v2/rtac.c b/sound/soc/msm/qdsp6v2/rtac.c new file mode 100644 index 000000000000..cd02501bac7b --- /dev/null +++ b/sound/soc/msm/qdsp6v2/rtac.c @@ -0,0 +1,1924 @@ +/* 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_update_afe_topology(u32 port_id) +{ + u32 i = 0; + + mutex_lock(&rtac_adm_mutex); + 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].afe_topology = + afe_get_topology(port_id); + pr_debug("%s: port_id = 0x%x topology_id = 0x%x copp_id = %d\n", + __func__, port_id, + rtac_adm_data.device[i].afe_topology, + rtac_adm_data.device[i].copp); + } + } + mutex_unlock(&rtac_adm_mutex); +} + +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 == VSS_ICOMMON_CMD_SET_PARAM_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[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 = (opcode == VSS_ICOMMON_CMD_SET_PARAM_V2) ? + VOC_RTAC_SET_PARAM_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 == VSS_ICOMMON_CMD_GET_PARAM_V2) { + 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, + VSS_ICOMMON_CMD_GET_PARAM_V2); + break; + case AUDIO_SET_RTAC_CVS_CAL: + result = send_voice_apr(RTAC_CVS, (void *) arg, + VSS_ICOMMON_CMD_SET_PARAM_V2); + break; + case AUDIO_GET_RTAC_CVP_CAL: + result = send_voice_apr(RTAC_CVP, (void *) arg, + VSS_ICOMMON_CMD_GET_PARAM_V2); + break; + case AUDIO_SET_RTAC_CVP_CAL: + result = send_voice_apr(RTAC_CVP, (void *) arg, + VSS_ICOMMON_CMD_SET_PARAM_V2); + 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"); diff --git a/sound/soc/msm/sdm660-common.c b/sound/soc/msm/sdm660-common.c new file mode 100644 index 000000000000..eddcb45e9150 --- /dev/null +++ b/sound/soc/msm/sdm660-common.c @@ -0,0 +1,3210 @@ +/* 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 "sdm660-common.h" +#include "sdm660-internal.h" +#include "sdm660-external.h" +#include "../codecs/sdm660_cdc/msm-analog-cdc.h" +#include "../codecs/wsa881x.h" + +#define DRV_NAME "sdm660-asoc-snd" + +#define MSM_INT_DIGITAL_CODEC "msm-dig-codec" +#define PMIC_INT_ANALOG_CODEC "analog-codec" + +#define DEV_NAME_STR_LEN 32 +#define DEFAULT_MCLK_RATE 9600000 + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + +enum { + DP_RX_IDX, + EXT_DISP_RX_IDX_MAX, +}; + +/* 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 */ + } +}; + +/* Default configuration of external display BE */ +static struct dev_config ext_disp_rx_cfg[] = { + [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, +}; + +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_conf { + struct mutex lock; + u32 ref_cnt; + u32 msm_is_mi2s_master; + u32 msm_is_ext_mclk; +}; + +static u32 mi2s_ebit_clk[MI2S_MAX] = { + Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT +}; + +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 = 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 = 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 *mi2s_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 char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE"}; +static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", + "KHZ_192"}; + +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(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_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_format, mi2s_format_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(ext_disp_rx_format, ext_disp_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(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 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 afe_clk_set mi2s_mclk[MI2S_MAX] = { + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_MCLK_3, + Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_MCLK_4, + Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_MCLK_1, + Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_MCLK_2, + Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + } +}; + +static struct mi2s_conf mi2s_intf_conf[MI2S_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 mi2s_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_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_get_format_value(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_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + 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 mi2s_tx_format_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].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d] _tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_tx_format_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_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_format_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].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d] _rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int mi2s_rx_format_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_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + 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; +} + +static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol) +{ + int idx; + + 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; +} + +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 Format", prim_mi2s_rx_format, + mi2s_rx_format_get, + mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", sec_mi2s_rx_format, + mi2s_rx_format_get, + mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", tert_mi2s_rx_format, + mi2s_rx_format_get, + mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", quat_mi2s_rx_format, + mi2s_rx_format_get, + mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", prim_mi2s_tx_format, + mi2s_tx_format_get, + mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", sec_mi2s_tx_format, + mi2s_tx_format_get, + mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", tert_mi2s_tx_format, + mi2s_tx_format_get, + mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", quat_mi2s_tx_format, + mi2s_tx_format_get, + mi2s_tx_format_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("Display Port RX Channels", ext_disp_rx_chs, + ext_disp_rx_ch_get, ext_disp_rx_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("Display Port RX Bit Format", ext_disp_rx_format, + ext_disp_rx_format_get, ext_disp_rx_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("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), +}; + +/** + * msm_common_snd_controls_size - to return controls size + * + * Return: returns size of common controls array + */ +int msm_common_snd_controls_size(void) +{ + return ARRAY_SIZE(msm_common_snd_controls); +} +EXPORT_SYMBOL(msm_common_snd_controls_size); + +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_ext_disp_get_idx_from_beid(int32_t be_id) +{ + int idx; + + switch (be_id) { + 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; +} + +/** + * 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; + 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_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_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; + break; + } + + 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; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); + 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; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); + 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; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); + 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; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); + 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; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); + 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; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); + 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; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); + 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; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); + 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) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + 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); + + return 0; +} +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; + + 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); +} +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_S32_LE: + 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; + } +} + +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 (port_id < 0) { + 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 port_id = msm_get_port_id(rtd->dai_link->be_id); + 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) { + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) { + mi2s_clk[index].clk_id = mi2s_ebit_clk[index]; + fmt = SND_SOC_DAIFMT_CBM_CFM; + } + ret = msm_mi2s_set_sclk(substream, true); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: afe lpass clock failed to enable MI2S clock, err:%d\n", + __func__, ret); + goto clean_up; + } + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); + if (ret < 0) { + dev_err(rtd->card->dev, + "%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + if (mi2s_intf_conf[index].msm_is_ext_mclk) { + mi2s_mclk[index].enable = 1; + pr_debug("%s: Enabling mclk, clk_freq_in_hz = %u\n", + __func__, mi2s_mclk[index].clk_freq_in_hz); + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_mclk[index]); + if (ret < 0) { + pr_err("%s: afe lpass mclk failed, err:%d\n", + __func__, ret); + goto clk_off; + } + } + } + mutex_unlock(&mi2s_intf_conf[index].lock); + return 0; +clk_off: + if (ret < 0) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (ret < 0) + 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 port_id = msm_get_port_id(rtd->dai_link->be_id); + 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++; + } + if (mi2s_intf_conf[index].msm_is_ext_mclk) { + mi2s_mclk[index].enable = 0; + pr_debug("%s: Disabling mclk, clk_freq_in_hz = %u\n", + __func__, mi2s_mclk[index].clk_freq_in_hz); + ret = afe_set_lpass_clock_v2(port_id, + &mi2s_mclk[index]); + if (ret < 0) { + pr_err("%s: mclk disable failed for MCLK (%d); ret=%d\n", + __func__, index, ret); + } + } + } + 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 msm_asoc_mach_data *pdata, + 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; + } + if (pdata->snd_card_val == INT_SND_CARD) { + if ((dai_link[i].be_id == + MSM_BACKEND_DAI_INT0_MI2S_RX) || + (dai_link[i].be_id == + MSM_BACKEND_DAI_INT1_MI2S_RX) || + (dai_link[i].be_id == + MSM_BACKEND_DAI_INT2_MI2S_TX) || + (dai_link[i].be_id == + MSM_BACKEND_DAI_INT3_MI2S_TX)) { + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + MSM_INT_DIGITAL_CODEC); + phandle = of_parse_phandle(cdev->of_node, + "asoc-codec", + index); + dai_link[i].codecs[DIG_CDC].of_node = phandle; + index = of_property_match_string(cdev->of_node, + "asoc-codec-names", + PMIC_INT_ANALOG_CODEC); + phandle = of_parse_phandle(cdev->of_node, + "asoc-codec", + index); + dai_link[i].codecs[ANA_CDC].of_node = phandle; + } + } + } +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) +{ + int count; + u32 mi2s_master_slave[MI2S_MAX]; + u32 mi2s_ext_mclk[MI2S_MAX]; + int ret; + + for (count = 0; count < MI2S_MAX; count++) { + mutex_init(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + } + + 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]; + } + } + + ret = of_property_read_u32_array(pdev->dev.of_node, + "qcom,msm-mi2s-ext-mclk", + mi2s_ext_mclk, MI2S_MAX); + if (ret) { + dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-ext-mclk in DT node\n", + __func__); + } else { + for (count = 0; count < MI2S_MAX; count++) + mi2s_intf_conf[count].msm_is_ext_mclk = + mi2s_ext_mclk[count]; + } +} + +static const struct of_device_id sdm660_asoc_machine_of_match[] = { + { .compatible = "qcom,sdm660-asoc-snd", + .data = "internal_codec"}, + { .compatible = "qcom,sdm660-asoc-snd-tasha", + .data = "tasha_codec"}, + { .compatible = "qcom,sdm660-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(sdm660_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*/ + pdata->pdm_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-pdm-gpios", 0); + pdata->comp_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-comp-gpios", 0); + pdata->dmic_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-dmic-gpios", 0); + pdata->ext_spk_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-ext-spk-gpios", 0); + } + + /* + * 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(pdata, card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + if (!of_property_read_bool(pdev->dev.of_node, "qcom,wsa-disable")) { + 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) { + /* + * return failure as EINVAL since other codec + * registered sound card successfully. + * This avoids any further probe calls. + */ + ret = -EINVAL; + } + goto err; + } else 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(pdev); + + 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; + } + if (pdata->snd_card_val != INT_SND_CARD) + msm_ext_cdc_deinit(pdata); + 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); + else + msm_ext_cdc_deinit(pdata); + msm_free_auxdev_mem(pdev); + + gpio_free(pdata->us_euro_gpio); + gpio_free(pdata->hph_en1_gpio); + gpio_free(pdata->hph_en0_gpio); + snd_soc_unregister_card(card); + return 0; +} + +static struct platform_driver sdm660_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = sdm660_asoc_machine_of_match, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; +module_platform_driver(sdm660_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, sdm660_asoc_machine_of_match); diff --git a/sound/soc/msm/sdm660-common.h b/sound/soc/msm/sdm660-common.h new file mode 100644 index 000000000000..bca8cd788a39 --- /dev/null +++ b/sound/soc/msm/sdm660-common.h @@ -0,0 +1,125 @@ +/* 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_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; +}; + +enum { + DIG_CDC, + ANA_CDC, + CODECS_MAX, +}; + +extern const struct snd_kcontrol_new msm_common_snd_controls[]; +extern bool codec_reg_done; +struct sdm660_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_snd_interrupt { + void __iomem *mpm_wakeup; + void __iomem *intr1_cfg_apps; + void __iomem *lpi_gpio_intr_cfg; + void __iomem *lpi_gpio_cfg; + void __iomem *lpi_gpio_inout; +}; + +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 device_node *pdm_gpio_p; /* used by pinctrl API */ + struct device_node *comp_gpio_p; /* used by pinctrl API */ + struct device_node *dmic_gpio_p; /* used by pinctrl API */ + struct device_node *ext_spk_gpio_p; /* used by pinctrl API */ + struct snd_soc_codec *codec; + struct sdm660_codec sdm660_codec_fn; + struct snd_info_entry *codec_root; + int spk_ext_pa_gpio; + int mclk_freq; + bool native_clk_set; + 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; + struct msm_snd_interrupt msm_snd_intr_lpi; +}; + +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); +int msm_common_snd_controls_size(void); +#endif diff --git a/sound/soc/msm/sdm660-ext-dai-links.c b/sound/soc/msm/sdm660-ext-dai-links.c new file mode 100644 index 000000000000..1c03d8c9e797 --- /dev/null +++ b/sound/soc/msm/sdm660-ext-dai-links.c @@ -0,0 +1,2038 @@ +/* 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 "qdsp6v2/msm-pcm-routing-v2.h" +#include "../codecs/wcd9335.h" +#include "sdm660-common.h" +#include "sdm660-external.h" + +#define DEV_NAME_STR_LEN 32 +#define __CHIPSET__ "SDM660 " +#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, + .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", + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .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", + }, + {/* hw:x,35 */ + .name = "SLIMBUS7 Hostless", + .stream_name = "SLIMBUS7 Hostless", + .cpu_dai_name = "SLIMBUS7_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, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + {/* hw:x,36 */ + .name = "SDM660 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, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, +}; + +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 ext_disp_be_dai_link[] = { + /* 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_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .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) + +ARRAY_SIZE(ext_disp_be_dai_link)]; + +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) + +ARRAY_SIZE(ext_disp_be_dai_link)]; + +/** + * 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 = "sdm660-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); + } + 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_ext_tasha_dai_links + len4, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + len4 += ARRAY_SIZE(ext_disp_be_dai_link); + } + 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); + } + 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_ext_tavil_dai_links + len4, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + len4 += ARRAY_SIZE(ext_disp_be_dai_link); + } + 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/sdm660-external.c b/sound/soc/msm/sdm660-external.c new file mode 100644 index 000000000000..2c3d7fc3e63f --- /dev/null +++ b/sound/soc/msm/sdm660-external.c @@ -0,0 +1,1869 @@ +/* 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 "sdm660-common.h" +#include "sdm660-external.h" +#include "../codecs/wcd9335.h" +#include "../codecs/wcd934x/wcd934x.h" +#include "../codecs/wcd934x/wcd934x-mbhc.h" + +#define SDM660_SPK_ON 1 +#define SDM660_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 TLMM_CENTER_MPM_WAKEUP_INT_EN_0 0x03596000 +#define LPI_GPIO_22_WAKEUP_VAL 0x00000002 + +#define TLMM_LPI_DIR_CONN_INTR1_CFG_APPS 0x0359D004 +#define LPI_GPIO_22_INTR1_CFG_VAL 0x01 +#define LPI_GPIO_22_INTR1_CFG_MASK 0x03 + +#define TLMM_LPI_GPIO_INTR_CFG1 0x0359B004 +#define LPI_GPIO_INTR_CFG1_VAL 0x00000113 + +#define TLMM_LPI_GPIO22_CFG 0x15078040 +#define LPI_GPIO22_CFG_VAL 0x0000009 + +#define TLMM_LPI_GPIO22_INOUT 0x179D1318 +#define LPI_GPIO22_INOUT_VAL 0x0020000 + +#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; +bool codec_reg_done; + +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 == SDM660_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_soc_kcontrol_codec(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 void msm_snd_interrupt_config(struct msm_asoc_mach_data *pdata) +{ + int val; + + val = ioread32(pdata->msm_snd_intr_lpi.mpm_wakeup); + val |= LPI_GPIO_22_WAKEUP_VAL; + iowrite32(val, pdata->msm_snd_intr_lpi.mpm_wakeup); + + val = ioread32(pdata->msm_snd_intr_lpi.intr1_cfg_apps); + val &= ~(LPI_GPIO_22_INTR1_CFG_MASK); + val |= LPI_GPIO_22_INTR1_CFG_VAL; + iowrite32(val, pdata->msm_snd_intr_lpi.intr1_cfg_apps); + + iowrite32(LPI_GPIO_INTR_CFG1_VAL, + pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg); + iowrite32(LPI_GPIO22_CFG_VAL, + pdata->msm_snd_intr_lpi.lpi_gpio_cfg); + val = ioread32(pdata->msm_snd_intr_lpi.lpi_gpio_inout); + val |= LPI_GPIO22_INOUT_VAL; + iowrite32(val, pdata->msm_snd_intr_lpi.lpi_gpio_inout); +} + +static int msm_adsp_power_up_config(struct snd_soc_codec *codec) +{ + int ret = 0; + unsigned long timeout; + int adsp_ready = 0; + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata; + + pdata = snd_soc_card_get_drvdata(card); + 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; + } + msm_snd_interrupt_config(pdata); + + 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 sdm660_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 = sdm660_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}; + + /* Tavil Codec SLIMBUS configuration + * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch_tavil[WCD934X_RX_MAX] = {144, 145, 146, 147, 148, + 149, 150, 151}; + unsigned int tx_ch_tavil[WCD934X_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; + } + + ret = snd_soc_add_codec_controls(codec, msm_common_snd_controls, + msm_common_snd_controls_size()); + if (ret < 0) { + pr_err("%s: add_common_snd_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, "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, "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, "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"); + } else { + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1"); + snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2"); + } + + snd_soc_dapm_sync(dapm); + + if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) { + snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch_tavil), + tx_ch_tavil, ARRAY_SIZE(rx_ch_tavil), + rx_ch_tavil); + } else { + 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; + } + + } + codec_reg_done = true; +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(struct platform_device *pdev) +{ + int ret; + + is_initial_boot = true; + spdev = pdev; + ret = audio_notifier_register("sdm660", 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; + } + platform_set_drvdata(pdev, *card); + snd_soc_card_set_drvdata(*card, pdata); + 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; + } + pdata->msm_snd_intr_lpi.mpm_wakeup = + ioremap(TLMM_CENTER_MPM_WAKEUP_INT_EN_0, 4); + pdata->msm_snd_intr_lpi.intr1_cfg_apps = + ioremap(TLMM_LPI_DIR_CONN_INTR1_CFG_APPS, 4); + pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg = + ioremap(TLMM_LPI_GPIO_INTR_CFG1, 4); + pdata->msm_snd_intr_lpi.lpi_gpio_cfg = + ioremap(TLMM_LPI_GPIO22_CFG, 4); + pdata->msm_snd_intr_lpi.lpi_gpio_inout = + ioremap(TLMM_LPI_GPIO22_INOUT, 4); +err: + return ret; +} +EXPORT_SYMBOL(msm_ext_cdc_init); + +/** + * msm_ext_cdc_deinit - external codec machine specific deinit. + */ +void msm_ext_cdc_deinit(struct msm_asoc_mach_data *pdata) +{ + if (pdata->msm_snd_intr_lpi.mpm_wakeup) + iounmap(pdata->msm_snd_intr_lpi.mpm_wakeup); + if (pdata->msm_snd_intr_lpi.intr1_cfg_apps) + iounmap(pdata->msm_snd_intr_lpi.intr1_cfg_apps); + if (pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg) + iounmap(pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg); + if (pdata->msm_snd_intr_lpi.lpi_gpio_cfg) + iounmap(pdata->msm_snd_intr_lpi.lpi_gpio_cfg); + if (pdata->msm_snd_intr_lpi.lpi_gpio_inout) + iounmap(pdata->msm_snd_intr_lpi.lpi_gpio_inout); + +} +EXPORT_SYMBOL(msm_ext_cdc_deinit); diff --git a/sound/soc/msm/sdm660-external.h b/sound/soc/msm/sdm660-external.h new file mode 100644 index 000000000000..acf5735c2502 --- /dev/null +++ b/sound/soc/msm/sdm660-external.h @@ -0,0 +1,54 @@ +/* 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 __SDM660_EXTERNAL +#define __SDM660_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(struct platform_device *pdev); +void msm_ext_cdc_deinit(struct msm_asoc_mach_data *pdata); +#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(struct platform_device *pdev) +{ +} +inline void msm_ext_cdc_deinit(void) +{ +} +#endif +#endif diff --git a/sound/soc/msm/sdm660-internal.c b/sound/soc/msm/sdm660-internal.c new file mode 100644 index 000000000000..802137ba4b50 --- /dev/null +++ b/sound/soc/msm/sdm660-internal.c @@ -0,0 +1,3149 @@ +/* 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 "sdm660-common.h" +#include "../codecs/sdm660_cdc/msm-digital-cdc.h" +#include "../codecs/sdm660_cdc/msm-analog-cdc.h" +#include "../codecs/msm_sdw/msm_sdw.h" + +#define __CHIPSET__ "SDM660 " +#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 + +#define WSA8810_NAME_1 "wsa881x.20170211" +#define WSA8810_NAME_2 "wsa881x.20170212" + +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_48KHZ, 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 struct snd_info_entry *codec_root; + +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, + 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 ret = 0; + + pr_debug("%s: %s HPH Compander\n", __func__, + enable ? "Enable" : "Disable"); + + if (enable) { + ret = msm_cdc_pinctrl_select_active_state(pdata->comp_gpio_p); + if (ret) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "comp_gpio"); + goto done; + } + } else { + ret = msm_cdc_pinctrl_select_sleep_state(pdata->comp_gpio_p); + 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_cdc_pinctrl_select_active_state( + pdata->ext_spk_gpio_p); + 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_cdc_pinctrl_select_sleep_state( + pdata->ext_spk_gpio_p); + 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; + pdata->native_clk_set = true; + } else { + clk_freq_in_hz = pdata->mclk_freq; + pdata->native_clk_set = false; + } + + 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) { + if (atomic_read(&pdata->int_mclk0_enabled)) { + pdata->digital_cdc_core_clk.enable = 0; + afe_set_lpass_clock_v2( + AFE_PORT_ID_INT0_MI2S_RX, + &pdata->digital_cdc_core_clk); + } + 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_cdc_pinctrl_select_active_state(pdata->pdm_gpio_p); + 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_cdc_pinctrl_select_sleep_state( + pdata->pdm_gpio_p); + 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); + msm_anlg_cdc_mclk_enable(codec, 1, true); + break; + case 0: + if (atomic_read(&pdata->int_mclk0_rsc_ref) <= 0) + break; + msm_anlg_cdc_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_cdc_pinctrl_select_sleep_state(pdata->pdm_gpio_p); + 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 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_sdw_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 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_cdc_pinctrl_select_active_state(pdata->dmic_gpio_p); + 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_cdc_pinctrl_select_sleep_state(pdata->dmic_gpio_p); + 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_cdc_pinctrl_select_sleep_state(pdata->pdm_gpio_p); + 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*/ + msm_anlg_cdc_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 * 2 * 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 (port_id < 0) { + 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_sdw_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; + } + 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_sdw_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_dais[ANA_CDC]->codec; + int ret = 0; + struct msm_asoc_mach_data *pdata = NULL; + + pdata = snd_soc_card_get_drvdata(codec->component.card); + 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_cdc_pinctrl_select_active_state(pdata->pdm_gpio_p); + if (ret < 0) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "int_pdm"); + return ret; + } + msm_anlg_cdc_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 *dig_cdc = rtd->codec_dais[DIG_CDC]->codec; + struct snd_soc_codec *ana_cdc = rtd->codec_dais[ANA_CDC]->codec; + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(ana_cdc); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(rtd->card); + struct snd_card *card; + int ret = -ENOMEM; + + pr_debug("%s(),dev_name%s\n", __func__, dev_name(cpu_dai->dev)); + + ret = snd_soc_add_codec_controls(ana_cdc, msm_snd_controls, + ARRAY_SIZE(msm_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed: %d\n", + __func__, ret); + return ret; + } + ret = snd_soc_add_codec_controls(ana_cdc, msm_common_snd_controls, + msm_common_snd_controls_size()); + if (ret < 0) { + pr_err("%s: add common snd controls failed: %d\n", + __func__, ret); + return ret; + } + + 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); + + msm_anlg_cdc_spk_ext_pa_cb(enable_spk_ext_pa, ana_cdc); + msm_dig_cdc_hph_comp_cb(msm_config_hph_compander_gpio, dig_cdc); + + card = rtd->card->snd_card; + if (!codec_root) + codec_root = snd_register_module_info(card->module, "codecs", + card->proc_root); + if (!codec_root) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + goto done; + } + pdata->codec_root = codec_root; + msm_dig_codec_info_create_codec_entry(codec_root, dig_cdc); + msm_anlg_codec_info_create_codec_entry(codec_root, ana_cdc); +done: + return 0; +} + +static int msm_sdw_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 msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux; + struct snd_card *card; + + snd_soc_add_codec_controls(codec, msm_sdw_controls, + ARRAY_SIZE(msm_sdw_controls)); + + snd_soc_dapm_ignore_suspend(dapm, "AIF1_SDW Playback"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed_SDW"); + snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT"); + snd_soc_dapm_ignore_suspend(dapm, "AIF1_SDW VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT_SDW"); + + snd_soc_dapm_sync(dapm); + + /* + * Send speaker configuration only for WSA8810. + * Default 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)) { + msm_sdw_set_spkr_mode(rtd->codec, SPKR_MODE_1); + msm_sdw_set_spkr_gain_offset(rtd->codec, + RX_GAIN_OFFSET_M1P5_DB); + } + card = rtd->card->snd_card; + if (!codec_root) + codec_root = snd_register_module_info(card->module, "codecs", + card->proc_root); + if (!codec_root) { + pr_debug("%s: Cannot create codecs module entry\n", + __func__); + goto done; + } + pdata->codec_root = codec_root; + msm_sdw_codec_info_create_codec_entry(codec_root, codec); +done: + 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 int msm_snd_card_late_probe(struct snd_soc_card *card) +{ + const char *be_dl_name = LPASS_BE_INT0_MI2S_RX; + struct snd_soc_codec *ana_cdc; + struct snd_soc_pcm_runtime *rtd; + int ret = 0; + + 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); + return -EINVAL; + } + + ana_cdc = rtd->codec_dais[ANA_CDC]->codec; + mbhc_cfg_ptr->calibration = def_msm_int_wcd_mbhc_cal(); + if (!mbhc_cfg_ptr->calibration) + return -ENOMEM; + + ret = msm_anlg_cdc_hs_detect(ana_cdc, mbhc_cfg_ptr); + if (ret) { + dev_err(card->dev, + "%s: msm_anlg_cdc_hs_detect failed\n", __func__); + kfree(mbhc_cfg_ptr->calibration); + } + + 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_sdw_mi2s_be_ops = { + .startup = msm_sdw_mi2s_snd_startup, + .shutdown = msm_sdw_mi2s_snd_shutdown, +}; + +struct snd_soc_dai_link_component dlc_rx1[] = { + { + .of_node = NULL, + .dai_name = "msm_dig_cdc_dai_rx1", + }, + { + .of_node = NULL, + .dai_name = "msm_anlg_cdc_i2s_rx1", + }, +}; + +struct snd_soc_dai_link_component dlc_tx1[] = { + { + .of_node = NULL, + .dai_name = "msm_dig_cdc_dai_tx1", + }, + { + .of_node = NULL, + .dai_name = "msm_anlg_cdc_i2s_tx1", + }, +}; + +struct snd_soc_dai_link_component dlc_tx2[] = { + { + .of_node = NULL, + .dai_name = "msm_dig_cdc_dai_tx2", + }, + { + .of_node = NULL, + .dai_name = "msm_anlg_cdc_i2s_tx2", + }, +}; + +/* 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", + .async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS, + .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 = "INT3 MI2S_TX Hostless", + .stream_name = "INT3 MI2S_TX Hostless", + .cpu_dai_name = "INT3_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", + }, + {/* hw:x,12 */ + .name = "SLIMBUS_7 Hostless", + .stream_name = "SLIMBUS_7 Hostless", + .cpu_dai_name = "SLIMBUS7_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 = "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,36 */ + .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,37 */ + .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,38 */ + .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", + }, + {/* hw:x,39 */ + .name = "SDM660 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, + .ignore_pmdown_time = 1, + .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, +}; + + +static struct snd_soc_dai_link msm_int_wsa_dai[] = { + {/* hw:x,40 */ + .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_sdw_codec", + .codec_dai_name = "msm_sdw_vifeedback", + .be_id = MSM_BACKEND_DAI_INT5_MI2S_TX, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_sdw_mi2s_be_ops, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + .ignore_suspend = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + }, +}; + +static struct snd_soc_dai_link msm_int_be_dai[] = { + /* 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", + .codecs = dlc_rx1, + .num_codecs = CODECS_MAX, + .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_INT3_MI2S_TX, + .stream_name = "INT3 MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.10", + .platform_name = "msm-pcm-routing", + .codecs = dlc_tx1, + .num_codecs = CODECS_MAX, + .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_INT2_MI2S_TX, + .stream_name = "INT2 MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.9", + .platform_name = "msm-pcm-routing", + .codecs = dlc_tx2, + .num_codecs = CODECS_MAX, + .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_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_wsa_be_dai_links[] = { + { + .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_sdw_codec", + .codec_dai_name = "msm_sdw_i2s_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .be_id = MSM_BACKEND_DAI_INT4_MI2S_RX, + .init = &msm_sdw_audrx_init, + .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, + .ops = &msm_sdw_mi2s_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link ext_disp_be_dai_link[] = { + /* 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_common_be_hw_params_fixup, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link msm_int_dai_links[ +ARRAY_SIZE(msm_int_dai) + +ARRAY_SIZE(msm_int_wsa_dai) + +ARRAY_SIZE(msm_int_be_dai) + +ARRAY_SIZE(msm_mi2s_be_dai_links) + +ARRAY_SIZE(msm_auxpcm_be_dai_links)+ +ARRAY_SIZE(msm_wcn_be_dai_links) + +ARRAY_SIZE(msm_wsa_be_dai_links) + +ARRAY_SIZE(ext_disp_be_dai_link)]; + +static struct snd_soc_card sdm660_card = { + /* snd_soc_card_sdm660 */ + .name = "sdm660-snd-card", + .dai_link = msm_int_dai, + .num_links = ARRAY_SIZE(msm_int_dai), + .late_probe = msm_snd_card_late_probe, +}; + +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 = &sdm660_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,wsa-disable")) { + memcpy(dailink + len1, + msm_int_wsa_dai, + sizeof(msm_int_wsa_dai)); + len1 += ARRAY_SIZE(msm_int_wsa_dai); + } + memcpy(dailink + len1, msm_int_be_dai, sizeof(msm_int_be_dai)); + len1 += ARRAY_SIZE(msm_int_be_dai); + + 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); + } + if (!of_property_read_bool(dev->of_node, "qcom,wsa-disable")) { + memcpy(dailink + len1, + msm_wsa_be_dai_links, + sizeof(msm_wsa_be_dai_links)); + len1 += ARRAY_SIZE(msm_wsa_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(dailink + len1, + ext_disp_be_dai_link, + sizeof(ext_disp_be_dai_link)); + len1 += ARRAY_SIZE(ext_disp_be_dai_link); + } + 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/sdm660-internal.h b/sound/soc/msm/sdm660-internal.h new file mode 100644 index 000000000000..ccc62b8f33dc --- /dev/null +++ b/sound/soc/msm/sdm660-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 __SDM660_INTERNAL +#define __SDM660_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/sdm845.c b/sound/soc/msm/sdm845.c new file mode 100644 index 000000000000..d3c4e05c65c8 --- /dev/null +++ b/sound/soc/msm/sdm845.c @@ -0,0 +1,6951 @@ +/* + * 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 "qdsp6v2/msm-pcm-routing-v2.h" +#include "../codecs/wcd934x/wcd934x.h" +#include "../codecs/wcd934x/wcd934x-mbhc.h" +#include "../codecs/wsa881x.h" + +#define DRV_NAME "sdm845-asoc-snd" + +#define __CHIPSET__ "SDM845 " +#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 { + DP_RX_IDX = 0, + EXT_DISP_RX_IDX_MAX, +}; + +struct msm_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; + +enum pinctrl_pin_state { + STATE_DISABLE = 0, /* All pins are in sleep state */ + STATE_MI2S_ACTIVE, /* IS2 = active, TDM = sleep */ + STATE_TDM_ACTIVE, /* IS2 = sleep, TDM = active */ +}; + +struct msm_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *mi2s_disable; + struct pinctrl_state *tdm_disable; + struct pinctrl_state *mi2s_active; + struct pinctrl_state *tdm_active; + enum pinctrl_pin_state curr_state; +}; + +struct msm_asoc_mach_data { + u32 mclk_freq; + int us_euro_gpio; /* used by gpio driver API */ + int usbc_en2_gpio; /* used by gpio driver API */ + struct device_node *us_euro_gpio_p; /* used by pinctrl API */ + struct pinctrl *usbc_en2_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_pinctrl_info pinctrl_info; +}; + +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 const char *const pin_states[] = {"sleep", "i2s-active", + "tdm-active"}; + +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[] = { + [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", "KHZ_32", "KHZ_44P1", + "KHZ_88P2", "KHZ_176P4" }; +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(mi2s_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_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_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, + .mbhc_micbias = MIC_BIAS_2, + .anc_micbias = MIC_BIAS_2, + .enable_anc_mic_detect = false, +}; + +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, "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_176P4KHZ: + sample_rate_val = 6; + break; + + case SAMPLING_RATE_88P2KHZ: + sample_rate_val = 5; + break; + + case SAMPLING_RATE_44P1KHZ: + sample_rate_val = 4; + break; + + case SAMPLING_RATE_32KHZ: + 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: 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 6: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 4: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_32KHZ; + break; + 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_get_format(int value) +{ + int format; + + 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_S24_3LE; + break; + case 3: + format = SNDRV_PCM_FORMAT_S32_LE; + break; + default: + format = SNDRV_PCM_FORMAT_S16_LE; + break; + } + return format; +} + +static int mi2s_get_format_value(int format) +{ + int value; + + switch (format) { + case SNDRV_PCM_FORMAT_S16_LE: + value = 0; + break; + case SNDRV_PCM_FORMAT_S24_LE: + value = 1; + break; + case SNDRV_PCM_FORMAT_S24_3LE: + value = 2; + break; + case SNDRV_PCM_FORMAT_S32_LE: + value = 3; + break; + default: + value = 0; + break; + } + return value; +} + +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_mi2s_rx_format_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_format_value(mi2s_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_rx_format_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].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, mi2s_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_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_format_value(mi2s_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_mi2s_tx_format_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].bit_format = + mi2s_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, mi2s_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +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("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("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("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("PRIM_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("PRIM_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("SEC_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("SEC_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("TERT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("TERT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_RX Format", mi2s_rx_format, + msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), + SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format, + msm_mi2s_tx_format_get, msm_mi2s_tx_format_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), "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_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, + NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SPK("Lineout_1 amp", NULL), + SND_SOC_DAPM_SPK("Lineout_2 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("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_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->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->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->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_DISPLAY_PORT_RX: + idx = msm_ext_disp_get_idx_from_beid(dai_link->id); + if (idx < 0) { + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[PRIM_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[PRIM_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[SEC_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[SEC_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[TERT_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[TERT_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUAT_MI2S].bit_format); + 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: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUAT_MI2S].bit_format); + 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_usbc_swap_gnd_mic(struct snd_soc_codec *codec, bool active) +{ + int value = 0; + bool ret = 0; + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct pinctrl_state *en2_pinctrl_active; + struct pinctrl_state *en2_pinctrl_sleep; + + if (!pdata->usbc_en2_gpio_p) { + if (active) { + /* if active and usbc_en2_gpio undefined, get pin */ + pdata->usbc_en2_gpio_p = devm_pinctrl_get(card->dev); + if (IS_ERR_OR_NULL(pdata->usbc_en2_gpio_p)) { + dev_err(card->dev, + "%s: Can't get EN2 gpio pinctrl:%ld\n", + __func__, + PTR_ERR(pdata->usbc_en2_gpio_p)); + pdata->usbc_en2_gpio_p = NULL; + return false; + } + } else + /* if not active and usbc_en2_gpio undefined, return */ + return false; + } + + pdata->usbc_en2_gpio = of_get_named_gpio(card->dev->of_node, + "qcom,usbc-analog-en2-gpio", 0); + if (!gpio_is_valid(pdata->usbc_en2_gpio)) { + dev_err(card->dev, "%s, property %s not in node %s", + __func__, "qcom,usbc-analog-en2-gpio", + card->dev->of_node->full_name); + return false; + } + + en2_pinctrl_active = pinctrl_lookup_state( + pdata->usbc_en2_gpio_p, "aud_active"); + if (IS_ERR_OR_NULL(en2_pinctrl_active)) { + dev_err(card->dev, + "%s: Cannot get aud_active pinctrl state:%ld\n", + __func__, PTR_ERR(en2_pinctrl_active)); + ret = false; + goto err_lookup_state; + } + + en2_pinctrl_sleep = pinctrl_lookup_state( + pdata->usbc_en2_gpio_p, "aud_sleep"); + if (IS_ERR_OR_NULL(en2_pinctrl_sleep)) { + dev_err(card->dev, + "%s: Cannot get aud_sleep pinctrl state:%ld\n", + __func__, PTR_ERR(en2_pinctrl_sleep)); + ret = false; + goto err_lookup_state; + } + + /* if active and usbc_en2_gpio_p defined, swap using usbc_en2_gpio_p */ + if (active) { + dev_dbg(codec->dev, "%s: enter\n", __func__); + if (pdata->usbc_en2_gpio_p) { + value = gpio_get_value_cansleep(pdata->usbc_en2_gpio); + if (value) + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_sleep); + else + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_active); + } else if (pdata->usbc_en2_gpio >= 0) { + value = gpio_get_value_cansleep(pdata->usbc_en2_gpio); + gpio_set_value_cansleep(pdata->usbc_en2_gpio, !value); + } + pr_debug("%s: swap select switch %d to %d\n", __func__, + value, !value); + ret = true; + } else { + /* if not active, release usbc_en2_gpio_p pin */ + pinctrl_select_state(pdata->usbc_en2_gpio_p, + en2_pinctrl_sleep); + } + +err_lookup_state: + devm_pinctrl_put(pdata->usbc_en2_gpio_p); + pdata->usbc_en2_gpio_p = NULL; + return ret; +} + +static bool msm_swap_gnd_mic(struct snd_soc_codec *codec, bool active) +{ + int value = 0; + int ret = 0; + struct snd_soc_card *card = codec->component.card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + + if (!pdata) + return false; + + if (!wcd_mbhc_cfg.enable_usbc_analog) { + /* if usbc is not defined, swap using us_euro_gpio_p */ + 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); + ret = true; + } else { + /* if usbc is defined, swap using usbc_en2 */ + ret = msm_usbc_swap_gnd_mic(codec, active); + } + return ret; +} + +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; + } + + ret = msm_afe_set_config(codec); + if (ret) + pr_err("%s: Failed to set AFE config. err %d\n", + __func__, ret); + + return 0; + +err: + return ret; +} + +static int sdm845_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 err; + } + 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 err; + } + break; + default: + break; + } +err: + return NOTIFY_OK; +} + +static struct notifier_block service_nb = { + .notifier_call = sdm845_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_component *aux_comp; + 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 + * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13 + * TX14, TX15, TX16 + */ + unsigned int rx_ch[WCD934X_RX_MAX] = {144, 145, 146, 147, 148, 149, + 150, 151}; + unsigned int tx_ch[WCD934X_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)); + + 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, "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"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR"); + + 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); + + msm_codec_fn.get_afe_config_fn = tavil_get_afe_config; + + ret = msm_adsp_power_up_config(codec); + if (ret) { + pr_err("%s: Failed to set AFE config %d\n", __func__, ret); + goto err; + } + + 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; + } + } + + /* + * Send speaker configuration only for WSA8810. + * Default configuration is for WSA8815. + */ + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { + tavil_set_spkr_mode(rtd->codec, WCD934X_SPKR_MODE_1); + tavil_set_spkr_gain_offset(rtd->codec, + WCD934X_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); + +done: + codec_reg_done = true; + return 0; + +err: + 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_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; + } + if (dai_link->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->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->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; + } + } 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; + } + /* For _tx1 case */ + if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + user_set_tx_ch = slim_tx_cfg[0].channels; + /* For _tx3 case */ + else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX) + user_set_tx_ch = slim_tx_cfg[1].channels; + else if (dai_link->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->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: + 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; + } + 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; + } + } 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; + } + 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; + } + } + +err: + 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 err; + } + + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) BE id %d\n", + __func__, tx_ch_cnt, dai_link->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); + +err: + 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; + + 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 err; + } + + 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 (ret < 0) + auxpcm_intf_conf[index].ref_cnt--; + + mutex_unlock(&auxpcm_intf_conf[index].lock); + +err: + 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_S32_LE: + 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->id); + if (port_id < 0) { + dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); + ret = port_id; + goto err; + } + + 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 err; + } + +err: + return ret; +} + +static int msm_set_pinctrl(struct msm_pinctrl_info *pinctrl_info, + enum pinctrl_pin_state new_state) +{ + int ret = 0; + int curr_state = 0; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + if (pinctrl_info->pinctrl == NULL) { + pr_err("%s: pinctrl_info->pinctrl is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + curr_state = pinctrl_info->curr_state; + pinctrl_info->curr_state = new_state; + pr_debug("%s: curr_state = %s new_state = %s\n", __func__, + pin_states[curr_state], pin_states[pinctrl_info->curr_state]); + + if (curr_state == pinctrl_info->curr_state) { + pr_debug("%s: Already in same state\n", __func__); + goto err; + } + + if (curr_state != STATE_DISABLE && + pinctrl_info->curr_state != STATE_DISABLE) { + pr_debug("%s: state already active cannot switch\n", __func__); + ret = -EIO; + goto err; + } + + switch (pinctrl_info->curr_state) { + case STATE_MI2S_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_active); + if (ret) { + pr_err("%s: MI2S state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_TDM_ACTIVE: + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_active); + if (ret) { + pr_err("%s: TDM state select failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + case STATE_DISABLE: + if (curr_state == STATE_MI2S_ACTIVE) { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + } else { + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->tdm_disable); + } + if (ret) { + pr_err("%s: state disable failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + break; + default: + pr_err("%s: TLMM pin state is invalid\n", __func__); + return -EINVAL; + } + +err: + return ret; +} + +static void msm_release_pinctrl(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); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info->pinctrl) { + devm_pinctrl_put(pinctrl_info->pinctrl); + pinctrl_info->pinctrl = NULL; + } +} + +static int msm_get_pinctrl(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); + struct msm_pinctrl_info *pinctrl_info = NULL; + struct pinctrl *pinctrl; + int ret; + + pinctrl_info = &pdata->pinctrl_info; + + if (pinctrl_info == NULL) { + pr_err("%s: pinctrl_info is NULL\n", __func__); + return -EINVAL; + } + + pinctrl = devm_pinctrl_get(&pdev->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + pr_err("%s: Unable to get pinctrl handle\n", __func__); + return -EINVAL; + } + pinctrl_info->pinctrl = pinctrl; + + /* get all the states handles from Device Tree */ + pinctrl_info->mi2s_disable = pinctrl_lookup_state(pinctrl, + "quat-mi2s-sleep"); + if (IS_ERR(pinctrl_info->mi2s_disable)) { + pr_err("%s: could not get mi2s_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->mi2s_active = pinctrl_lookup_state(pinctrl, + "quat-mi2s-active"); + if (IS_ERR(pinctrl_info->mi2s_active)) { + pr_err("%s: could not get mi2s_active pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_disable = pinctrl_lookup_state(pinctrl, + "quat-tdm-sleep"); + if (IS_ERR(pinctrl_info->tdm_disable)) { + pr_err("%s: could not get tdm_disable pinstate\n", __func__); + goto err; + } + pinctrl_info->tdm_active = pinctrl_lookup_state(pinctrl, + "quat-tdm-active"); + if (IS_ERR(pinctrl_info->tdm_active)) { + pr_err("%s: could not get tdm_active pinstate\n", + __func__); + goto err; + } + /* Reset the TLMM pins to a default state */ + ret = pinctrl_select_state(pinctrl_info->pinctrl, + pinctrl_info->mi2s_disable); + if (ret != 0) { + pr_err("%s: Disable TLMM pins failed with %d\n", + __func__, ret); + ret = -EIO; + goto err; + } + pinctrl_info->curr_state = STATE_DISABLE; + + return 0; + +err: + devm_pinctrl_put(pinctrl); + pinctrl_info->pinctrl = NULL; + return -EINVAL; +} + +static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + 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); + + if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) { + 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; + } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { + 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; + } else { + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static int sdm845_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[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + slots = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + slot_width = 32; + channels = slots; + + pr_debug("%s: slot_width %d slots %d\n", __func__, slot_width, slots); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_debug("%s: slot_width %d\n", __func__, slot_width); + 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 { + pr_err("%s: invalid use case, err:%d\n", + __func__, ret); + } + +end: + return ret; +} + +static int sdm845_tdm_snd_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + 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); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); + if (ret) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret); + + return ret; +} + +static void sdm845_tdm_snd_shutdown(struct snd_pcm_substream *substream) +{ + int ret = 0; + 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); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + + ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret); + +} + +static struct snd_soc_ops sdm845_tdm_be_ops = { + .hw_params = sdm845_tdm_snd_hw_params, + .startup = sdm845_tdm_snd_startup, + .shutdown = sdm845_tdm_snd_shutdown +}; + +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; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 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_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 err; + } + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } + /* + * 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 (ret < 0) { + 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 (ret < 0) { + pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", + __func__, index, ret); + goto clk_off; + } + } +clk_off: + if (ret < 0) + msm_mi2s_set_sclk(substream, false); +clean_up: + if (ret < 0) + mi2s_intf_conf[index].ref_cnt--; + mutex_unlock(&mi2s_intf_conf[index].lock); +err: + 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; + struct snd_soc_card *card = rtd->card; + struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; + int ret_pinctrl = 0; + + 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); + + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } +} + +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 err; + } + + 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 err; + } + } 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 err; + } + + 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 err; + } + } +err: + return ret; +} + +static struct snd_soc_ops msm_be_ops = { + .hw_params = msm_snd_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, + .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, + .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", + .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, + .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, + .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 */ + .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, + .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", + .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 */ + .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 */ + .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 */ + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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, + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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 */ + .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_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", + .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_misc_fe_dai_links[] = { + { + .name = MSM_DAILINK_NAME(ASM Loopback), + .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, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, + { + .name = "USB Audio Hostless", + .stream_name = "USB Audio Hostless", + .cpu_dai_name = "USBAUDIO_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, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, +}; + +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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sdm845_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, + .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_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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + }, + /* Slimbus VI Recording */ + { + .name = LPASS_BE_SLIMBUS_TX_VI, + .stream_name = "Slimbus4 Capture", + .cpu_dai_name = "msm-dai-q6-dev.16393", + .platform_name = "msm-pcm-routing", + .codec_name = "tavil_codec", + .codec_dai_name = "tavil_vifeedback", + .id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .be_hw_params_fixup = msm_be_hw_params_fixup, + .ops = &msm_be_ops, + .ignore_suspend = 1, + .no_pcm = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 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, + .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, + .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, + .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[] = { + /* 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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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, + .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_tavil_snd_card_dai_links[ + ARRAY_SIZE(msm_common_dai_links) + + ARRAY_SIZE(msm_tavil_fe_dai_links) + + ARRAY_SIZE(msm_common_misc_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_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; + } + + mbhc_calibration = def_tavil_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err; + } + 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_free_mbhc_cal; + } + return 0; + +err_free_mbhc_cal: + kfree(mbhc_calibration); +err: + return ret; +} + +struct snd_soc_card snd_soc_card_tavil_msm = { + .name = "sdm845-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, "TAVIL_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}; + 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, + .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, + .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, + .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 = "sdm845-stub-snd-card", +}; + +static const struct of_device_id sdm845_asoc_machine_of_match[] = { + { .compatible = "qcom,sdm845-asoc-snd-tavil", + .data = "tavil_codec"}, + { .compatible = "qcom,sdm845-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, len_4; + int total_links; + const struct of_device_id *match; + + match = of_match_node(sdm845_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, "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_misc_fe_dai_links); + len_4 = len_3 + ARRAY_SIZE(msm_common_be_dai_links); + total_links = len_4 + ARRAY_SIZE(msm_tavil_be_dai_links); + memcpy(msm_tavil_snd_card_dai_links, + msm_common_dai_links, + sizeof(msm_common_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_1, + msm_tavil_fe_dai_links, + sizeof(msm_tavil_fe_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_2, + msm_common_misc_fe_dai_links, + sizeof(msm_common_misc_fe_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_3, + msm_common_be_dai_links, + sizeof(msm_common_be_dai_links)); + memcpy(msm_tavil_snd_card_dai_links + len_4, + 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_snd_card_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_snd_card_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_snd_card_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_snd_card_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_snd_card_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; + } + pdata = snd_soc_card_get_drvdata(component->card); + if (pdata && pdata->codec_root) + wsa881x_codec_info_create_codec_entry(pdata->codec_root, + codec); + +err: + 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; + } + if (wsa_max_devs == 0) { + dev_warn(&pdev->dev, + "%s: Max WSA devices is 0 for this target?\n", + __func__); + goto err; + } + + /* 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; + } 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; + } + + /* + * 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; + } + + /* + * 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; + } + + /* + * 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_free_dev_info; + } + 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_free_dev_info; + } + + /* 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_free_aux_dev; + } + + 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_free_cdc_conf; + } + + 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_free_dev_name_str; + } + + 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_free_dev_name_str: + devm_kfree(&pdev->dev, dev_name_str); +err_free_cdc_conf: + devm_kfree(&pdev->dev, msm_codec_conf); +err_free_aux_dev: + devm_kfree(&pdev->dev, msm_aux_dev); +err_free_dev_info: + devm_kfree(&pdev->dev, wsa881x_dev_info); +err: + return ret; +} + +static void msm_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 msm_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); + mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL; + } + mutex_destroy(&mi2s_auxpcm_conf[count].lock); + } + + for (count = 0; count < AUX_PCM_MAX; count++) { + mutex_destroy(&auxpcm_intf_conf[count].lock); + auxpcm_intf_conf[count].ref_cnt = 0; + } + + for (count = 0; count < MI2S_MAX; count++) { + mutex_destroy(&mi2s_intf_conf[count].lock); + mi2s_intf_conf[count].ref_cnt = 0; + mi2s_intf_conf[count].msm_is_mi2s_master = 0; + } +} + +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; + const char *usb_c_dt = "qcom,msm-mbhc-usbc-audio-supported"; + + 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(sdm845_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,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")) { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + dev_dbg(&pdev->dev, "This hardware has 4 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 5 pole jack"); + } else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack")) { + wcd_mbhc_cfg.enable_anc_mic_detect = true; + dev_dbg(&pdev->dev, "This hardware has 6 pole jack"); + } else { + wcd_mbhc_cfg.enable_anc_mic_detect = false; + 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; + } + + if (of_find_property(pdev->dev.of_node, usb_c_dt, NULL)) + 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); + + /* Parse pinctrl info from devicetree */ + ret = msm_get_pinctrl(pdev); + if (!ret) { + pr_debug("%s: pinctrl parsing successful\n", __func__); + } else { + dev_dbg(&pdev->dev, + "%s: Parsing pinctrl failed with %d. Cannot use Ports\n", + __func__, ret); + ret = 0; + } + + msm_i2s_auxpcm_init(pdev); + + is_initial_boot = true; + ret = audio_notifier_register("sdm845", AUDIO_NOTIFIER_ADSP_DOMAIN, + &service_nb); + if (ret < 0) + pr_err("%s: Audio notifier register failed ret = %d\n", + __func__, ret); + + return 0; +err: + msm_release_pinctrl(pdev); + 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->us_euro_gpio > 0) { + gpio_free(pdata->us_euro_gpio); + pdata->us_euro_gpio = 0; + } + msm_i2s_auxpcm_deinit(); + + msm_release_pinctrl(pdev); + snd_soc_unregister_card(card); + return 0; +} + +static struct platform_driver sdm845_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = sdm845_asoc_machine_of_match, + }, + .probe = msm_asoc_machine_probe, + .remove = msm_asoc_machine_remove, +}; +module_platform_driver(sdm845_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC msm"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, sdm845_asoc_machine_of_match); From 3d0e01d5bef22fa3e2a2171a55343f644ede1480 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Fri, 28 Jul 2017 12:46:23 +0530 Subject: [PATCH 003/276] autoconf: config file support for audio techpack for sdm845 New conf files are required to enable config for audio techpack. Add respective files for SDM845. This provides required config to compile required files for SDM845 audio. Change-Id: Ic93d0df0da03d7f891267994380729602ce64c65 Signed-off-by: Laxminath Kasam --- config/sdm845auto.conf | 41 ++++++++++++++++++++++++++++++ config/sdm845autoconf.h | 55 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 config/sdm845auto.conf create mode 100644 config/sdm845autoconf.h diff --git a/config/sdm845auto.conf b/config/sdm845auto.conf new file mode 100644 index 000000000000..f9a7748a3fde --- /dev/null +++ b/config/sdm845auto.conf @@ -0,0 +1,41 @@ +CONFIG_WCD934X_CODEC=y +CONFIG_WCD9335_CODEC=y +CONFIG_PINCTRL_WCD=y +CONFIG_SND_SOC_WCD934X=y +CONFIG_AUDIO_EXT_CLK=y +CONFIG_SND_SOC_WCD9XXX_V2=y +CONFIG_SND_SOC_WCD_MBHC=y +CONFIG_SND_SOC_WSA881X=y +CONFIG_SND_SOC_WCD_DSP_MGR=y +CONFIG_SND_SOC_WCD_SPI=y +CONFIG_SND_SOC_WCD934X=y +CONFIG_SND_SOC_WCD934X_MBHC=y +CONFIG_SND_SOC_WCD934X_DSD=y +CONFIG_MSM_QDSP6V2_CODECS=y +CONFIG_MSM_ULTRASOUND=y +CONFIG_MSM_QDSP6_APRV2_GLINK=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_REGMAP_SWR=y +CONFIG_MSM_QDSP6_SSR=y +CONFIG_MSM_QDSP6_PDR=y +CONFIG_MSM_QDSP6_NOTIFIER=y +CONFIG_MSM_CDSP_LOADER=y +CONFIG_SND_SOC_MSM_HOSTLESS_PCM=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_SND_SOC_SDM845=y +CONFIG_MSM_GLINK_SPI_XPRT=y +CONFIG_SOUNDWIRE=y +CONFIG_SOUNDWIRE_WCD_CTRL=y +CONFIG_SND_SOC_QDSP6V2=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_MSM_CDC_PINCTRL=y +CONFIG_WCD9XXX_CODEC_CORE=y +CONFIG_QTI_PP=y +CONFIG_SND_HWDEP=y +CONFIG_DTS_EAGLE=y +CONFIG_DOLBY_DS2=y +CONFIG_DOLBY_LICENSE=y +CONFIG_DTS_SRS_TM=y +CONFIG_SND_SOC_MACHINE_SDM845=y +CONFIG_SND_SOC_MSM_STUB=y diff --git a/config/sdm845autoconf.h b/config/sdm845autoconf.h new file mode 100644 index 000000000000..6c3bb330656d --- /dev/null +++ b/config/sdm845autoconf.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 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. + */ + +#define CONFIG_WCD934X_CODEC 1 +#define CONFIG_WCD9335_CODEC 1 +#define CONFIG_PINCTRL_WCD 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_AUDIO_EXT_CLK 1 +#define CONFIG_SND_SOC_WCD9XXX_V2 1 +#define CONFIG_SND_SOC_WCD_CPE 1 +#define CONFIG_SND_SOC_WCD_MBHC 1 +#define CONFIG_SND_SOC_WSA881X 1 +#define CONFIG_SND_SOC_WCD_DSP_MGR 1 +#define CONFIG_SND_SOC_WCD_SPI 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_SND_SOC_WCD934X_MBHC 1 +#define CONFIG_SND_SOC_WCD934X_DSD 1 +#define CONFIG_MSM_QDSP6V2_CODECS 1 +#define CONFIG_MSM_ULTRASOUND 1 +#define CONFIG_MSM_QDSP6_APRV2_GLINK 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_ADSP_LOADER 1 +#define CONFIG_REGMAP_SWR 1 +#define CONFIG_MSM_QDSP6_SSR 1 +#define CONFIG_MSM_QDSP6_PDR 1 +#define CONFIG_MSM_QDSP6_NOTIFIER 1 +#define CONFIG_MSM_CDSP_LOADER 1 +#define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_SND_SOC_SDM845 1 +#define CONFIG_MSM_GLINK_SPI_XPRT 1 +#define CONFIG_SOUNDWIRE 1 +#define CONFIG_SOUNDWIRE_WCD_CTRL 1 +#define CONFIG_SND_SOC_WCD_MBHC_ADC 1 +#define CONFIG_SND_SOC_QDSP6V2 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_CDC_PINCTRL 1 +#define CONFIG_QTI_PP 1 +#define CONFIG_SND_HWDEP 1 +#define CONFIG_DTS_EAGLE 1 +#define CONFIG_DOLBY_DS2 1 +#define CONFIG_DOLBY_LICENSE 1 +#define CONFIG_DTS_SRS_TM 1 +#define CONFIG_WCD9XXX_CODEC_CORE 1 +#define CONFIG_SND_SOC_MSM_STUB 1 From 366f750416cac34ccf369198ba38c4e9ff3799cc Mon Sep 17 00:00:00 2001 From: Asish Bhattacharya Date: Tue, 25 Jul 2017 15:15:56 +0530 Subject: [PATCH 004/276] audio-lnx: Propagate changes from kernel for msm_sdw/hdmi Add snapshot for msm_sdw codec/hdmi codec drivers for SDM targets. The code is migrated from msm-4.9 kernel at the below cutoff - (8efe4a8d64f: "ARM: dts: msm: add rotator vbif memtype setting to sdm845") This changes are done to migrate msm_sdw/hdmi codec drivers to this new audio kernel techpack. Change-Id: Ia807b87a7c67957512a6b38cc62ae9cb5223c8b2 Signed-off-by: Asish Bhattacharya --- sound/soc/codecs/msm_hdmi_codec_rx.c | 562 +++++ sound/soc/codecs/msm_sdw/Kconfig | 6 + sound/soc/codecs/msm_sdw/Makefile | 3 + sound/soc/codecs/msm_sdw/msm-sdw-tables.c | 319 +++ sound/soc/codecs/msm_sdw/msm_sdw.h | 170 ++ sound/soc/codecs/msm_sdw/msm_sdw_cdc.c | 2007 ++++++++++++++++++ sound/soc/codecs/msm_sdw/msm_sdw_cdc_utils.c | 211 ++ sound/soc/codecs/msm_sdw/msm_sdw_registers.h | 126 ++ sound/soc/codecs/msm_sdw/msm_sdw_regmap.c | 161 ++ 9 files changed, 3565 insertions(+) create mode 100644 sound/soc/codecs/msm_hdmi_codec_rx.c create mode 100644 sound/soc/codecs/msm_sdw/Kconfig create mode 100644 sound/soc/codecs/msm_sdw/Makefile create mode 100644 sound/soc/codecs/msm_sdw/msm-sdw-tables.c create mode 100644 sound/soc/codecs/msm_sdw/msm_sdw.h create mode 100644 sound/soc/codecs/msm_sdw/msm_sdw_cdc.c create mode 100644 sound/soc/codecs/msm_sdw/msm_sdw_cdc_utils.c create mode 100644 sound/soc/codecs/msm_sdw/msm_sdw_registers.h create mode 100644 sound/soc/codecs/msm_sdw/msm_sdw_regmap.c diff --git a/sound/soc/codecs/msm_hdmi_codec_rx.c b/sound/soc/codecs/msm_hdmi_codec_rx.c new file mode 100644 index 000000000000..46cfe7dede26 --- /dev/null +++ b/sound/soc/codecs/msm_hdmi_codec_rx.c @@ -0,0 +1,562 @@ +/* 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 + +#define MSM_EXT_DISP_PCM_RATES SNDRV_PCM_RATE_48000 +#define AUD_EXT_DISP_ACK_DISCONNECT (AUDIO_ACK_CONNECT ^ AUDIO_ACK_CONNECT) +#define AUD_EXT_DISP_ACK_CONNECT (AUDIO_ACK_CONNECT) +#define AUD_EXT_DISP_ACK_ENABLE (AUDIO_ACK_SET_ENABLE | AUDIO_ACK_ENABLE) + +static const char *const ext_disp_audio_type_text[] = {"None", "HDMI", "DP"}; +static const char *const ext_disp_audio_ack_text[] = {"Disconnect", "Connect", + "Ack_Enable"}; + +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_type, ext_disp_audio_type_text); +static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_ack_state, + ext_disp_audio_ack_text); + +struct msm_ext_disp_audio_codec_rx_data { + struct platform_device *ext_disp_core_pdev; + struct msm_ext_disp_audio_codec_ops ext_disp_ops; + int cable_status; +}; + +static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + struct msm_ext_disp_audio_edid_blk edid_blk; + int rc; + + codec_data = snd_soc_codec_get_drvdata(codec); + + if (!codec_data) { + dev_err(codec->dev, "%s: codec_data is NULL\n", __func__); + return -EINVAL; + } + + if (!codec_data->ext_disp_ops.get_audio_edid_blk) { + dev_dbg(codec->dev, "%s: get_audio_edid_blk() is NULL\n", + __func__); + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 0; + return 0; + } + + rc = codec_data->ext_disp_ops.get_audio_edid_blk( + codec_data->ext_disp_core_pdev, &edid_blk); + if (rc >= 0) { + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = edid_blk.audio_data_blk_size + + edid_blk.spk_alloc_data_blk_size; + } + + dev_dbg(codec->dev, "%s: count: %d\n", __func__, uinfo->count); + + return rc; +} + +static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) { + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + struct msm_ext_disp_audio_edid_blk edid_blk; + int rc; + + codec_data = snd_soc_codec_get_drvdata(codec); + if (!codec_data || !codec_data->ext_disp_ops.get_audio_edid_blk) { + dev_err(codec->dev, "%s: codec_data or get_audio_edid_blk() is NULL\n", + __func__); + return -EINVAL; + } + + rc = codec_data->ext_disp_ops.get_audio_edid_blk( + codec_data->ext_disp_core_pdev, &edid_blk); + if (rc >= 0) { + if (sizeof(ucontrol->value.bytes.data) < + (edid_blk.audio_data_blk_size + + edid_blk.spk_alloc_data_blk_size)) { + dev_err(codec->dev, + "%s: Not enough memory to copy EDID data\n", + __func__); + return -ENOMEM; + } + + memcpy(ucontrol->value.bytes.data, + edid_blk.audio_data_blk, + edid_blk.audio_data_blk_size); + memcpy((ucontrol->value.bytes.data + + edid_blk.audio_data_blk_size), + edid_blk.spk_alloc_data_blk, + edid_blk.spk_alloc_data_blk_size); + + dev_dbg(codec->dev, "%s: data_blk_size:%d, spk_alloc_data_blk_size:%d\n", + __func__, edid_blk.audio_data_blk_size, + edid_blk.spk_alloc_data_blk_size); + } + + return rc; +} + +static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + enum msm_ext_disp_cable_state cable_state; + enum msm_ext_disp_type disp_type; + int rc; + + codec_data = snd_soc_codec_get_drvdata(codec); + if (!codec_data || + !codec_data->ext_disp_ops.get_audio_edid_blk || + !codec_data->ext_disp_ops.get_intf_id) { + dev_err(codec->dev, "%s: codec_data, get_audio_edid_blk() or get_intf_id is NULL\n", + __func__); + return -EINVAL; + } + + cable_state = codec_data->ext_disp_ops.cable_status( + codec_data->ext_disp_core_pdev, 1); + if (cable_state < 0) { + dev_err(codec->dev, "%s: Error retrieving cable state from ext_disp, err:%d\n", + __func__, cable_state); + rc = cable_state; + goto done; + } + + codec_data->cable_status = cable_state; + if (cable_state == EXT_DISPLAY_CABLE_DISCONNECT) { + dev_err(codec->dev, "%s: Display cable disconnected\n", + __func__); + ucontrol->value.integer.value[0] = 0; + rc = 0; + goto done; + } + + disp_type = codec_data->ext_disp_ops.get_intf_id( + codec_data->ext_disp_core_pdev); + if (disp_type >= 0) { + switch (disp_type) { + case EXT_DISPLAY_TYPE_DP: + ucontrol->value.integer.value[0] = 2; + rc = 0; + break; + case EXT_DISPLAY_TYPE_HDMI: + ucontrol->value.integer.value[0] = 1; + rc = 0; + break; + default: + rc = -EINVAL; + dev_err(codec->dev, "%s: Invalid disp_type:%d\n", + __func__, disp_type); + goto done; + } + dev_dbg(codec->dev, "%s: Display type: %d\n", + __func__, disp_type); + } else { + dev_err(codec->dev, "%s: Error retrieving disp_type from ext_disp, err:%d\n", + __func__, disp_type); + rc = disp_type; + } + +done: + return rc; +} + +static int msm_ext_disp_audio_ack_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_ext_disp_audio_codec_rx_data *codec_data; + u32 ack_state = 0; + int rc; + + codec_data = snd_soc_codec_get_drvdata(codec); + if (!codec_data || + !codec_data->ext_disp_ops.acknowledge) { + dev_err(codec->dev, + "%s: codec_data or ops acknowledge() is NULL\n", + __func__); + rc = -EINVAL; + goto done; + } + + switch (ucontrol->value.enumerated.item[0]) { + case 0: + ack_state = AUD_EXT_DISP_ACK_DISCONNECT; + break; + case 1: + ack_state = AUD_EXT_DISP_ACK_CONNECT; + break; + case 2: + ack_state = AUD_EXT_DISP_ACK_ENABLE; + break; + default: + rc = -EINVAL; + dev_err(codec->dev, + "%s: invalid value %d for mixer ctl\n", + __func__, ucontrol->value.enumerated.item[0]); + goto done; + } + dev_dbg(codec->dev, "%s: control %d, ack set value 0x%x\n", + __func__, ucontrol->value.enumerated.item[0], ack_state); + + rc = codec_data->ext_disp_ops.acknowledge( + codec_data->ext_disp_core_pdev, ack_state); + if (rc < 0) { + dev_err(codec->dev, "%s: error from acknowledge(), err:%d\n", + __func__, rc); + } + +done: + return rc; +} + +static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = { + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "HDMI EDID", + .info = msm_ext_disp_edid_ctl_info, + .get = msm_ext_disp_edid_get, + }, + { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "Display Port EDID", + .info = msm_ext_disp_edid_ctl_info, + .get = msm_ext_disp_edid_get, + }, + SOC_ENUM_EXT("External Display Type", ext_disp_audio_type, + msm_ext_disp_audio_type_get, NULL), + SOC_ENUM_EXT("External Display Audio Ack", ext_disp_audio_ack_state, + NULL, msm_ext_disp_audio_ack_set), +}; + +static int msm_ext_disp_audio_codec_rx_dai_startup( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret = 0; + struct msm_ext_disp_audio_codec_rx_data *codec_data = + dev_get_drvdata(dai->codec->dev); + + if (!codec_data || !codec_data->ext_disp_ops.cable_status) { + dev_err(dai->dev, "%s() codec_data or cable_status is null\n", + __func__); + return -EINVAL; + } + + codec_data->cable_status = + codec_data->ext_disp_ops.cable_status( + codec_data->ext_disp_core_pdev, 1); + if (codec_data->cable_status < 0) { + dev_err(dai->dev, + "%s() ext disp core is not ready (ret val = %d)\n", + __func__, codec_data->cable_status); + ret = codec_data->cable_status; + } else if (!codec_data->cable_status) { + dev_err(dai->dev, + "%s() ext disp cable is not connected (ret val = %d)\n", + __func__, codec_data->cable_status); + ret = -ENODEV; + } + + return ret; +} + +static int msm_ext_disp_audio_codec_rx_dai_hw_params( + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u32 channel_allocation = 0; + u32 level_shift = 0; /* 0dB */ + bool down_mix = 0; + u32 num_channels = params_channels(params); + int rc = 0; + struct msm_ext_disp_audio_setup_params audio_setup_params = {0}; + + struct msm_ext_disp_audio_codec_rx_data *codec_data = + dev_get_drvdata(dai->codec->dev); + + if (!codec_data || !codec_data->ext_disp_ops.audio_info_setup) { + dev_err(dai->dev, "%s: codec_data or audio_info_setup is null\n", + __func__); + return -EINVAL; + } + + if (codec_data->cable_status < 0) { + dev_err_ratelimited(dai->dev, + "%s() ext disp core is not ready (ret val = %d)\n", + __func__, codec_data->cable_status); + return codec_data->cable_status; + } else if (!codec_data->cable_status) { + dev_err_ratelimited(dai->dev, + "%s() ext disp cable is not connected (ret val = %d)\n", + __func__, codec_data->cable_status); + return -ENODEV; + } + + /*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/ + switch (num_channels) { + case 2: + channel_allocation = 0; + break; + case 3: + channel_allocation = 0x02;/*default to FL/FR/FC*/ + audio_setup_params.sample_present = 0x3; + break; + case 4: + channel_allocation = 0x06;/*default to FL/FR/FC/RC*/ + audio_setup_params.sample_present = 0x7; + break; + case 5: + channel_allocation = 0x0A;/*default to FL/FR/FC/RR/RL*/ + audio_setup_params.sample_present = 0x7; + break; + case 6: + channel_allocation = 0x0B; + audio_setup_params.sample_present = 0x7; + break; + case 7: + channel_allocation = 0x12;/*default to FL/FR/FC/RL/RR/RRC/RLC*/ + audio_setup_params.sample_present = 0xf; + break; + case 8: + channel_allocation = 0x13; + audio_setup_params.sample_present = 0xf; + break; + default: + dev_err(dai->dev, "invalid Channels = %u\n", num_channels); + return -EINVAL; + } + + dev_dbg(dai->dev, + "%s() num_ch %u samplerate %u channel_allocation = %u\n", + __func__, num_channels, params_rate(params), + channel_allocation); + + audio_setup_params.sample_rate_hz = params_rate(params); + audio_setup_params.num_of_channels = num_channels; + audio_setup_params.channel_allocation = channel_allocation; + audio_setup_params.level_shift = level_shift; + audio_setup_params.down_mix = down_mix; + + rc = codec_data->ext_disp_ops.audio_info_setup( + codec_data->ext_disp_core_pdev, &audio_setup_params); + if (rc < 0) { + dev_err_ratelimited(dai->dev, + "%s() ext disp core is not ready, rc: %d\n", + __func__, rc); + } + + return rc; +} + +static void msm_ext_disp_audio_codec_rx_dai_shutdown( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int rc; + + struct msm_ext_disp_audio_codec_rx_data *codec_data = + dev_get_drvdata(dai->codec->dev); + + if (!codec_data || !codec_data->ext_disp_ops.teardown_done || + !codec_data->ext_disp_ops.cable_status) { + dev_err(dai->dev, "%s: codec data or teardown_done or cable_status is null\n", + __func__); + return; + } + + rc = codec_data->ext_disp_ops.cable_status( + codec_data->ext_disp_core_pdev, 0); + if (rc < 0) { + dev_err(dai->dev, + "%s: ext disp core had problems releasing audio flag\n", + __func__); + } + + codec_data->ext_disp_ops.teardown_done( + codec_data->ext_disp_core_pdev); +} + +static int msm_ext_disp_audio_codec_rx_probe(struct snd_soc_codec *codec) +{ + struct msm_ext_disp_audio_codec_rx_data *codec_data; + struct device_node *of_node_parent = NULL; + + codec_data = kzalloc(sizeof(struct msm_ext_disp_audio_codec_rx_data), + GFP_KERNEL); + + if (!codec_data) { + dev_err(codec->dev, "%s(): fail to allocate dai data\n", + __func__); + return -ENOMEM; + } + + of_node_parent = of_get_parent(codec->dev->of_node); + if (!of_node_parent) { + dev_err(codec->dev, "%s(): Parent device tree node not found\n", + __func__); + kfree(codec_data); + return -ENODEV; + } + + codec_data->ext_disp_core_pdev = of_find_device_by_node(of_node_parent); + if (!codec_data->ext_disp_core_pdev) { + dev_err(codec->dev, "%s(): can't get parent pdev\n", __func__); + kfree(codec_data); + return -ENODEV; + } + + if (msm_ext_disp_register_audio_codec(codec_data->ext_disp_core_pdev, + &codec_data->ext_disp_ops)) { + dev_err(codec->dev, "%s(): can't register with ext disp core", + __func__); + kfree(codec_data); + return -ENODEV; + } + + dev_set_drvdata(codec->dev, codec_data); + + dev_dbg(codec->dev, "%s(): registered %s with ext disp core\n", + __func__, codec->component.name); + + return 0; +} + +static int msm_ext_disp_audio_codec_rx_remove(struct snd_soc_codec *codec) +{ + struct msm_ext_disp_audio_codec_rx_data *codec_data; + + codec_data = dev_get_drvdata(codec->dev); + kfree(codec_data); + + return 0; +} + +static struct snd_soc_dai_ops msm_ext_disp_audio_codec_rx_dai_ops = { + .startup = msm_ext_disp_audio_codec_rx_dai_startup, + .hw_params = msm_ext_disp_audio_codec_rx_dai_hw_params, + .shutdown = msm_ext_disp_audio_codec_rx_dai_shutdown +}; + +static struct snd_soc_dai_driver msm_ext_disp_audio_codec_rx_dais[] = { + { + .name = "msm_hdmi_audio_codec_rx_dai", + .playback = { + .stream_name = "HDMI Playback", + .channels_min = 1, + .channels_max = 8, + .rate_min = 48000, + .rate_max = 48000, + .rates = MSM_EXT_DISP_PCM_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &msm_ext_disp_audio_codec_rx_dai_ops, + }, + { + .name = "msm_dp_audio_codec_rx_dai", + .playback = { + .stream_name = "Display Port Playback", + .channels_min = 1, + .channels_max = 8, + .rate_min = 48000, + .rate_max = 192000, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &msm_ext_disp_audio_codec_rx_dai_ops, + }, +}; + +static struct snd_soc_codec_driver msm_ext_disp_audio_codec_rx_soc_driver = { + .probe = msm_ext_disp_audio_codec_rx_probe, + .remove = msm_ext_disp_audio_codec_rx_remove, + .component_driver = { + .controls = msm_ext_disp_codec_rx_controls, + .num_controls = ARRAY_SIZE(msm_ext_disp_codec_rx_controls), + }, +}; + +static int msm_ext_disp_audio_codec_rx_plat_probe( + struct platform_device *pdev) +{ + dev_dbg(&pdev->dev, "%s(): dev name %s\n", __func__, + dev_name(&pdev->dev)); + + return snd_soc_register_codec(&pdev->dev, + &msm_ext_disp_audio_codec_rx_soc_driver, + msm_ext_disp_audio_codec_rx_dais, + ARRAY_SIZE(msm_ext_disp_audio_codec_rx_dais)); +} + +static int msm_ext_disp_audio_codec_rx_plat_remove( + struct platform_device *pdev) +{ + snd_soc_unregister_codec(&pdev->dev); + return 0; +} +static const struct of_device_id msm_ext_disp_audio_codec_rx_dt_match[] = { + { .compatible = "qcom,msm-ext-disp-audio-codec-rx", }, + {} +}; +MODULE_DEVICE_TABLE(of, msm_ext_disp_audio_codec_rx_dt_match); + +static struct platform_driver msm_ext_disp_audio_codec_rx_driver = { + .driver = { + .name = "msm-ext-disp-audio-codec-rx", + .owner = THIS_MODULE, + .of_match_table = msm_ext_disp_audio_codec_rx_dt_match, + }, + .probe = msm_ext_disp_audio_codec_rx_plat_probe, + .remove = msm_ext_disp_audio_codec_rx_plat_remove, +}; + +static int __init msm_ext_disp_audio_codec_rx_init(void) +{ + int rc; + + rc = platform_driver_register(&msm_ext_disp_audio_codec_rx_driver); + if (rc) { + pr_err("%s: failed to register ext disp codec driver err:%d\n", + __func__, rc); + } + + return rc; +} +module_init(msm_ext_disp_audio_codec_rx_init); + +static void __exit msm_ext_disp_audio_codec_rx_exit(void) +{ + platform_driver_unregister(&msm_ext_disp_audio_codec_rx_driver); +} +module_exit(msm_ext_disp_audio_codec_rx_exit); + +MODULE_DESCRIPTION("MSM External Display Audio CODEC Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm_sdw/Kconfig b/sound/soc/codecs/msm_sdw/Kconfig new file mode 100644 index 000000000000..abd7c8c7dfb0 --- /dev/null +++ b/sound/soc/codecs/msm_sdw/Kconfig @@ -0,0 +1,6 @@ +config SND_SOC_MSM_SDW + tristate "MSM Internal soundwire codec" + help + MSM-based soundwire codec core driver + supported along with internal digital + codec core. diff --git a/sound/soc/codecs/msm_sdw/Makefile b/sound/soc/codecs/msm_sdw/Makefile new file mode 100644 index 000000000000..64e932b9d262 --- /dev/null +++ b/sound/soc/codecs/msm_sdw/Makefile @@ -0,0 +1,3 @@ +snd-soc-msm-sdw-objs := msm_sdw_cdc.o msm_sdw_regmap.o msm-sdw-tables.o msm_sdw_cdc_utils.o +obj-$(CONFIG_SND_SOC_MSM_SDW) += snd-soc-msm-sdw.o +ccflags-y += -I$(srctree)/sound/soc/msm diff --git a/sound/soc/codecs/msm_sdw/msm-sdw-tables.c b/sound/soc/codecs/msm_sdw/msm-sdw-tables.c new file mode 100644 index 000000000000..1b51805bb92e --- /dev/null +++ b/sound/soc/codecs/msm_sdw/msm-sdw-tables.c @@ -0,0 +1,319 @@ +/* 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 "msm_sdw.h" + +const u8 msm_sdw_page_map[MSM_SDW_MAX_REGISTER] = { + [MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 0xa, + [MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 0xa, + [MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 0xa, + [MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 0xa, + [MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 0xa, + [MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 0xa, + [MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 0xa, + [MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 0xa, + [MSM_SDW_COMPANDER7_CTL0] = 0xb, + [MSM_SDW_COMPANDER7_CTL1] = 0xb, + [MSM_SDW_COMPANDER7_CTL2] = 0xb, + [MSM_SDW_COMPANDER7_CTL3] = 0xb, + [MSM_SDW_COMPANDER7_CTL4] = 0xb, + [MSM_SDW_COMPANDER7_CTL5] = 0xb, + [MSM_SDW_COMPANDER7_CTL6] = 0xb, + [MSM_SDW_COMPANDER7_CTL7] = 0xb, + [MSM_SDW_COMPANDER8_CTL0] = 0xb, + [MSM_SDW_COMPANDER8_CTL1] = 0xb, + [MSM_SDW_COMPANDER8_CTL2] = 0xb, + [MSM_SDW_COMPANDER8_CTL3] = 0xb, + [MSM_SDW_COMPANDER8_CTL4] = 0xb, + [MSM_SDW_COMPANDER8_CTL5] = 0xb, + [MSM_SDW_COMPANDER8_CTL6] = 0xb, + [MSM_SDW_COMPANDER8_CTL7] = 0xb, + [MSM_SDW_RX7_RX_PATH_CTL] = 0xb, + [MSM_SDW_RX7_RX_PATH_CFG0] = 0xb, + [MSM_SDW_RX7_RX_PATH_CFG1] = 0xb, + [MSM_SDW_RX7_RX_PATH_CFG2] = 0xb, + [MSM_SDW_RX7_RX_VOL_CTL] = 0xb, + [MSM_SDW_RX7_RX_PATH_MIX_CTL] = 0xb, + [MSM_SDW_RX7_RX_PATH_MIX_CFG] = 0xb, + [MSM_SDW_RX7_RX_VOL_MIX_CTL] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC0] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC1] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC2] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC3] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC5] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC6] = 0xb, + [MSM_SDW_RX7_RX_PATH_SEC7] = 0xb, + [MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 0xb, + [MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 0xb, + [MSM_SDW_RX8_RX_PATH_CTL] = 0xb, + [MSM_SDW_RX8_RX_PATH_CFG0] = 0xb, + [MSM_SDW_RX8_RX_PATH_CFG1] = 0xb, + [MSM_SDW_RX8_RX_PATH_CFG2] = 0xb, + [MSM_SDW_RX8_RX_VOL_CTL] = 0xb, + [MSM_SDW_RX8_RX_PATH_MIX_CTL] = 0xb, + [MSM_SDW_RX8_RX_PATH_MIX_CFG] = 0xb, + [MSM_SDW_RX8_RX_VOL_MIX_CTL] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC0] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC1] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC2] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC3] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC5] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC6] = 0xb, + [MSM_SDW_RX8_RX_PATH_SEC7] = 0xb, + [MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 0xb, + [MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 0xb, + [MSM_SDW_BOOST0_BOOST_PATH_CTL] = 0xc, + [MSM_SDW_BOOST0_BOOST_CTL] = 0xc, + [MSM_SDW_BOOST0_BOOST_CFG1] = 0xc, + [MSM_SDW_BOOST0_BOOST_CFG2] = 0xc, + [MSM_SDW_BOOST1_BOOST_PATH_CTL] = 0xc, + [MSM_SDW_BOOST1_BOOST_CTL] = 0xc, + [MSM_SDW_BOOST1_BOOST_CFG1] = 0xc, + [MSM_SDW_BOOST1_BOOST_CFG2] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 0xc, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_DATA_0] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_DATA_1] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_DATA_2] = 0xc, + [MSM_SDW_AHB_BRIDGE_RD_DATA_3] = 0xc, + [MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 0xc, + [MSM_SDW_AHB_BRIDGE_ACCESS_STATUS] = 0xc, + [MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 0xd, + [MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 0xd, + [MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 0xd, + [MSM_SDW_TOP_TOP_CFG0] = 0xd, + [MSM_SDW_TOP_TOP_CFG1] = 0xd, + [MSM_SDW_TOP_RX_I2S_CTL] = 0xd, + [MSM_SDW_TOP_TX_I2S_CTL] = 0xd, + [MSM_SDW_TOP_I2S_CLK] = 0xd, + [MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 0xd, + [MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 0xd, + [MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 0xd, + [MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 0xd, + [MSM_SDW_TOP_FREQ_MCLK] = 0xd, + [MSM_SDW_TOP_DEBUG_BUS_SEL] = 0xd, + [MSM_SDW_TOP_DEBUG_EN] = 0xd, + [MSM_SDW_TOP_I2S_RESET] = 0xd, + [MSM_SDW_TOP_BLOCKS_RESET] = 0xd, +}; + +const u8 msm_sdw_reg_readable[MSM_SDW_MAX_REGISTER] = { + [MSM_SDW_PAGE_REGISTER] = 1, + [MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_COMPANDER7_CTL0] = 1, + [MSM_SDW_COMPANDER7_CTL1] = 1, + [MSM_SDW_COMPANDER7_CTL2] = 1, + [MSM_SDW_COMPANDER7_CTL3] = 1, + [MSM_SDW_COMPANDER7_CTL4] = 1, + [MSM_SDW_COMPANDER7_CTL5] = 1, + [MSM_SDW_COMPANDER7_CTL6] = 1, + [MSM_SDW_COMPANDER7_CTL7] = 1, + [MSM_SDW_COMPANDER8_CTL0] = 1, + [MSM_SDW_COMPANDER8_CTL1] = 1, + [MSM_SDW_COMPANDER8_CTL2] = 1, + [MSM_SDW_COMPANDER8_CTL3] = 1, + [MSM_SDW_COMPANDER8_CTL4] = 1, + [MSM_SDW_COMPANDER8_CTL5] = 1, + [MSM_SDW_COMPANDER8_CTL6] = 1, + [MSM_SDW_COMPANDER8_CTL7] = 1, + [MSM_SDW_RX7_RX_PATH_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_CFG0] = 1, + [MSM_SDW_RX7_RX_PATH_CFG1] = 1, + [MSM_SDW_RX7_RX_PATH_CFG2] = 1, + [MSM_SDW_RX7_RX_VOL_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_CFG] = 1, + [MSM_SDW_RX7_RX_VOL_MIX_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_SEC0] = 1, + [MSM_SDW_RX7_RX_PATH_SEC1] = 1, + [MSM_SDW_RX7_RX_PATH_SEC2] = 1, + [MSM_SDW_RX7_RX_PATH_SEC3] = 1, + [MSM_SDW_RX7_RX_PATH_SEC5] = 1, + [MSM_SDW_RX7_RX_PATH_SEC6] = 1, + [MSM_SDW_RX7_RX_PATH_SEC7] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 1, + [MSM_SDW_RX8_RX_PATH_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_CFG0] = 1, + [MSM_SDW_RX8_RX_PATH_CFG1] = 1, + [MSM_SDW_RX8_RX_PATH_CFG2] = 1, + [MSM_SDW_RX8_RX_VOL_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_CFG] = 1, + [MSM_SDW_RX8_RX_VOL_MIX_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_SEC0] = 1, + [MSM_SDW_RX8_RX_PATH_SEC1] = 1, + [MSM_SDW_RX8_RX_PATH_SEC2] = 1, + [MSM_SDW_RX8_RX_PATH_SEC3] = 1, + [MSM_SDW_RX8_RX_PATH_SEC5] = 1, + [MSM_SDW_RX8_RX_PATH_SEC6] = 1, + [MSM_SDW_RX8_RX_PATH_SEC7] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 1, + [MSM_SDW_BOOST0_BOOST_PATH_CTL] = 1, + [MSM_SDW_BOOST0_BOOST_CTL] = 1, + [MSM_SDW_BOOST0_BOOST_CFG1] = 1, + [MSM_SDW_BOOST0_BOOST_CFG2] = 1, + [MSM_SDW_BOOST1_BOOST_PATH_CTL] = 1, + [MSM_SDW_BOOST1_BOOST_CTL] = 1, + [MSM_SDW_BOOST1_BOOST_CFG1] = 1, + [MSM_SDW_BOOST1_BOOST_CFG2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 1, + [MSM_SDW_AHB_BRIDGE_RD_DATA_0] = 1, + [MSM_SDW_AHB_BRIDGE_RD_DATA_1] = 1, + [MSM_SDW_AHB_BRIDGE_RD_DATA_2] = 1, + [MSM_SDW_AHB_BRIDGE_RD_DATA_3] = 1, + [MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 1, + [MSM_SDW_AHB_BRIDGE_ACCESS_STATUS] = 1, + [MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 1, + [MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 1, + [MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 1, + [MSM_SDW_TOP_TOP_CFG0] = 1, + [MSM_SDW_TOP_TOP_CFG1] = 1, + [MSM_SDW_TOP_RX_I2S_CTL] = 1, + [MSM_SDW_TOP_TX_I2S_CTL] = 1, + [MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 1, + [MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 1, + [MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 1, + [MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 1, + [MSM_SDW_TOP_FREQ_MCLK] = 1, + [MSM_SDW_TOP_DEBUG_BUS_SEL] = 1, + [MSM_SDW_TOP_DEBUG_EN] = 1, + [MSM_SDW_TOP_I2S_RESET] = 1, + [MSM_SDW_TOP_BLOCKS_RESET] = 1, +}; + +const u8 msm_sdw_reg_writeable[MSM_SDW_MAX_REGISTER] = { + [MSM_SDW_PAGE_REGISTER] = 1, + [MSM_SDW_TX9_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX9_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX10_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX10_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX11_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX11_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_TX12_SPKR_PROT_PATH_CTL] = 1, + [MSM_SDW_TX12_SPKR_PROT_PATH_CFG0] = 1, + [MSM_SDW_COMPANDER7_CTL0] = 1, + [MSM_SDW_COMPANDER7_CTL1] = 1, + [MSM_SDW_COMPANDER7_CTL2] = 1, + [MSM_SDW_COMPANDER7_CTL3] = 1, + [MSM_SDW_COMPANDER7_CTL4] = 1, + [MSM_SDW_COMPANDER7_CTL5] = 1, + [MSM_SDW_COMPANDER7_CTL7] = 1, + [MSM_SDW_COMPANDER8_CTL0] = 1, + [MSM_SDW_COMPANDER8_CTL1] = 1, + [MSM_SDW_COMPANDER8_CTL2] = 1, + [MSM_SDW_COMPANDER8_CTL3] = 1, + [MSM_SDW_COMPANDER8_CTL4] = 1, + [MSM_SDW_COMPANDER8_CTL5] = 1, + [MSM_SDW_COMPANDER8_CTL7] = 1, + [MSM_SDW_RX7_RX_PATH_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_CFG0] = 1, + [MSM_SDW_RX7_RX_PATH_CFG1] = 1, + [MSM_SDW_RX7_RX_PATH_CFG2] = 1, + [MSM_SDW_RX7_RX_VOL_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_CFG] = 1, + [MSM_SDW_RX7_RX_VOL_MIX_CTL] = 1, + [MSM_SDW_RX7_RX_PATH_SEC0] = 1, + [MSM_SDW_RX7_RX_PATH_SEC1] = 1, + [MSM_SDW_RX7_RX_PATH_SEC2] = 1, + [MSM_SDW_RX7_RX_PATH_SEC3] = 1, + [MSM_SDW_RX7_RX_PATH_SEC5] = 1, + [MSM_SDW_RX7_RX_PATH_SEC6] = 1, + [MSM_SDW_RX7_RX_PATH_SEC7] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_SEC0] = 1, + [MSM_SDW_RX7_RX_PATH_MIX_SEC1] = 1, + [MSM_SDW_RX8_RX_PATH_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_CFG0] = 1, + [MSM_SDW_RX8_RX_PATH_CFG1] = 1, + [MSM_SDW_RX8_RX_PATH_CFG2] = 1, + [MSM_SDW_RX8_RX_VOL_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_CFG] = 1, + [MSM_SDW_RX8_RX_VOL_MIX_CTL] = 1, + [MSM_SDW_RX8_RX_PATH_SEC0] = 1, + [MSM_SDW_RX8_RX_PATH_SEC1] = 1, + [MSM_SDW_RX8_RX_PATH_SEC2] = 1, + [MSM_SDW_RX8_RX_PATH_SEC3] = 1, + [MSM_SDW_RX8_RX_PATH_SEC5] = 1, + [MSM_SDW_RX8_RX_PATH_SEC6] = 1, + [MSM_SDW_RX8_RX_PATH_SEC7] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_SEC0] = 1, + [MSM_SDW_RX8_RX_PATH_MIX_SEC1] = 1, + [MSM_SDW_BOOST0_BOOST_PATH_CTL] = 1, + [MSM_SDW_BOOST0_BOOST_CTL] = 1, + [MSM_SDW_BOOST0_BOOST_CFG1] = 1, + [MSM_SDW_BOOST0_BOOST_CFG2] = 1, + [MSM_SDW_BOOST1_BOOST_PATH_CTL] = 1, + [MSM_SDW_BOOST1_BOOST_CTL] = 1, + [MSM_SDW_BOOST1_BOOST_CFG1] = 1, + [MSM_SDW_BOOST1_BOOST_CFG2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_0] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_1] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_DATA_3] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_0] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_1] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_2] = 1, + [MSM_SDW_AHB_BRIDGE_WR_ADDR_3] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_0] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_1] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_2] = 1, + [MSM_SDW_AHB_BRIDGE_RD_ADDR_3] = 1, + [MSM_SDW_AHB_BRIDGE_ACCESS_CFG] = 1, + [MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL] = 1, + [MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL] = 1, + [MSM_SDW_CLK_RST_CTRL_SWR_CONTROL] = 1, + [MSM_SDW_TOP_TOP_CFG0] = 1, + [MSM_SDW_TOP_TOP_CFG1] = 1, + [MSM_SDW_TOP_RX_I2S_CTL] = 1, + [MSM_SDW_TOP_TX_I2S_CTL] = 1, + [MSM_SDW_TOP_RX7_PATH_INPUT0_MUX] = 1, + [MSM_SDW_TOP_RX7_PATH_INPUT1_MUX] = 1, + [MSM_SDW_TOP_RX8_PATH_INPUT0_MUX] = 1, + [MSM_SDW_TOP_RX8_PATH_INPUT1_MUX] = 1, + [MSM_SDW_TOP_FREQ_MCLK] = 1, + [MSM_SDW_TOP_DEBUG_BUS_SEL] = 1, + [MSM_SDW_TOP_DEBUG_EN] = 1, + [MSM_SDW_TOP_I2S_RESET] = 1, + [MSM_SDW_TOP_BLOCKS_RESET] = 1, +}; diff --git a/sound/soc/codecs/msm_sdw/msm_sdw.h b/sound/soc/codecs/msm_sdw/msm_sdw.h new file mode 100644 index 000000000000..376ebc6c38db --- /dev/null +++ b/sound/soc/codecs/msm_sdw/msm_sdw.h @@ -0,0 +1,170 @@ +/* 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 MSM_SDW_H +#define MSM_SDW_H + +#include +#include +#include "msm_sdw_registers.h" + +#define MSM_SDW_MAX_REGISTER 0x400 + +extern const struct regmap_config msm_sdw_regmap_config; +extern const u8 msm_sdw_page_map[MSM_SDW_MAX_REGISTER]; +extern const u8 msm_sdw_reg_readable[MSM_SDW_MAX_REGISTER]; +extern const u8 msm_sdw_reg_writeable[MSM_SDW_MAX_REGISTER]; + +enum { + MSM_SDW_RX4 = 0, + MSM_SDW_RX5, + MSM_SDW_RX_MAX, +}; + +enum { + MSM_SDW_TX0 = 0, + MSM_SDW_TX1, + MSM_SDW_TX_MAX, +}; + +enum { + COMP1, /* SPK_L */ + COMP2, /* SPK_R */ + COMP_MAX +}; + +/* + * Structure used to update codec + * register defaults after reset + */ +struct msm_sdw_reg_mask_val { + u16 reg; + u8 mask; + u8 val; +}; + +/* + * Selects compander and smart boost settings + * for a given speaker mode + */ +enum { + SPKR_MODE_DEFAULT, + SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */ +}; + +/* Rx path gain offsets */ +enum { + RX_GAIN_OFFSET_M1P5_DB, + RX_GAIN_OFFSET_0_DB, +}; + +struct msm_sdw_reg_val { + unsigned short reg; /* register address */ + u8 *buf; /* buffer to be written to reg. addr */ + int bytes; /* number of bytes to be written */ +}; + +/* Hold instance to soundwire platform device */ +struct msm_sdw_ctrl_data { + struct platform_device *sdw_pdev; +}; + +struct wcd_sdw_ctrl_platform_data { + void *handle; /* holds codec private data */ + int (*read)(void *handle, int reg); + int (*write)(void *handle, int reg, int val); + int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len); + int (*clk)(void *handle, bool enable); + int (*handle_irq)(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action); +}; + +struct msm_sdw_priv { + struct device *dev; + struct mutex io_lock; + + int (*read_dev)(struct msm_sdw_priv *msm_sdw, unsigned short reg, + int bytes, void *dest); + int (*write_dev)(struct msm_sdw_priv *msm_sdw, unsigned short reg, + int bytes, void *src); + int (*multi_reg_write)(struct msm_sdw_priv *msm_sdw, const void *data, + size_t count); + struct snd_soc_codec *codec; + struct device_node *sdw_gpio_p; /* used by pinctrl API */ + /* SoundWire data structure */ + struct msm_sdw_ctrl_data *sdw_ctrl_data; + int nr; + + /* compander */ + int comp_enabled[COMP_MAX]; + int ear_spkr_gain; + + /* to track the status */ + unsigned long status_mask; + + struct work_struct msm_sdw_add_child_devices_work; + struct wcd_sdw_ctrl_platform_data sdw_plat_data; + + unsigned int vi_feed_value; + + struct mutex sdw_read_lock; + struct mutex sdw_write_lock; + struct mutex sdw_clk_lock; + int sdw_clk_users; + int sdw_mclk_users; + + int sdw_irq; + int int_mclk1_rsc_ref; + bool int_mclk1_enabled; + bool sdw_npl_clk_enabled; + struct mutex cdc_int_mclk1_mutex; + struct mutex sdw_npl_clk_mutex; + struct delayed_work disable_int_mclk1_work; + struct afe_clk_set sdw_cdc_core_clk; + struct afe_clk_set sdw_npl_clk; + struct notifier_block service_nb; + int (*sdw_cdc_gpio_fn)(bool enable, struct snd_soc_codec *codec); + bool dev_up; + + int spkr_gain_offset; + int spkr_mode; + struct mutex codec_mutex; + int rx_4_count; + int rx_5_count; + u32 mclk_rate; + struct regmap *regmap; + + bool prev_pg_valid; + u8 prev_pg; + u32 sdw_base_addr; + char __iomem *sdw_base; + u32 version; + + /* Entry for version info */ + struct snd_info_entry *entry; + struct snd_info_entry *version_entry; +}; + +extern int msm_sdw_set_spkr_mode(struct snd_soc_codec *codec, int mode); +extern int msm_sdw_set_spkr_gain_offset(struct snd_soc_codec *codec, + int offset); +extern void msm_sdw_gpio_cb( + int (*sdw_cdc_gpio_fn)(bool enable, struct snd_soc_codec *codec), + struct snd_soc_codec *codec); +extern struct regmap *msm_sdw_regmap_init(struct device *dev, + const struct regmap_config *config); +extern int msm_sdw_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec); +#endif diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c b/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c new file mode 100644 index 000000000000..cfe42e00e358 --- /dev/null +++ b/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c @@ -0,0 +1,2007 @@ +/* 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 "msm_sdw.h" +#include "msm_sdw_registers.h" + +#define MSM_SDW_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) +#define MSM_SDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE) + +#define MSM_SDW_STRING_LEN 80 + +#define INT_MCLK1_FREQ 9600000 +#define SDW_NPL_FREQ 153600000 + +#define MSM_SDW_VERSION_1_0 0x0001 +#define MSM_SDW_VERSION_ENTRY_SIZE 32 + +/* + * 200 Milliseconds sufficient for DSP bring up in the modem + * after Sub System Restart + */ +#define ADSP_STATE_READY_TIMEOUT_MS 200 + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); +static struct snd_soc_dai_driver msm_sdw_dai[]; +static bool skip_irq = true; + +static int msm_sdw_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg); +static int msm_sdw_config_compander(struct snd_soc_codec *, int, int); +static int msm_sdw_mclk_enable(struct msm_sdw_priv *msm_sdw, + int mclk_enable, bool dapm); +static int msm_int_enable_sdw_cdc_clk(struct msm_sdw_priv *msm_sdw, + int enable, bool dapm); + +enum { + VI_SENSE_1, + VI_SENSE_2, +}; + +enum { + AIF1_SDW_PB = 0, + AIF1_SDW_VIFEED, + NUM_CODEC_DAIS, +}; + +static const struct msm_sdw_reg_mask_val msm_sdw_spkr_default[] = { + {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x80}, + {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x80}, + {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x01}, + {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x01}, + {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x50}, + {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x50}, +}; + +static const struct msm_sdw_reg_mask_val msm_sdw_spkr_mode1[] = { + {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x00}, + {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x00}, + {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x00}, + {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x00}, + {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x44}, + {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x44}, +}; + +/** + * msm_sdw_set_spkr_gain_offset - offset the speaker path + * gain with the given offset value. + * + * @codec: codec instance + * @offset: Indicates speaker path gain offset value. + * + * Returns 0 on success or -EINVAL on error. + */ +int msm_sdw_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset) +{ + struct msm_sdw_priv *priv; + + if (!codec) { + pr_err("%s: NULL codec pointer!\n", __func__); + return -EINVAL; + } + + priv = snd_soc_codec_get_drvdata(codec); + if (!priv) + return -EINVAL; + + priv->spkr_gain_offset = offset; + return 0; +} +EXPORT_SYMBOL(msm_sdw_set_spkr_gain_offset); + +/** + * msm_sdw_set_spkr_mode - Configures speaker compander and smartboost + * settings based on speaker mode. + * + * @codec: codec instance + * @mode: Indicates speaker configuration mode. + * + * Returns 0 on success or -EINVAL on error. + */ +int msm_sdw_set_spkr_mode(struct snd_soc_codec *codec, int mode) +{ + struct msm_sdw_priv *priv; + int i; + const struct msm_sdw_reg_mask_val *regs; + int size; + + if (!codec) { + pr_err("%s: NULL codec pointer!\n", __func__); + return -EINVAL; + } + + priv = snd_soc_codec_get_drvdata(codec); + if (!priv) + return -EINVAL; + + switch (mode) { + case SPKR_MODE_1: + regs = msm_sdw_spkr_mode1; + size = ARRAY_SIZE(msm_sdw_spkr_mode1); + break; + default: + regs = msm_sdw_spkr_default; + size = ARRAY_SIZE(msm_sdw_spkr_default); + break; + } + + priv->spkr_mode = mode; + for (i = 0; i < size; i++) + snd_soc_update_bits(codec, regs[i].reg, + regs[i].mask, regs[i].val); + return 0; +} +EXPORT_SYMBOL(msm_sdw_set_spkr_mode); + +static int msm_enable_sdw_npl_clk(struct msm_sdw_priv *msm_sdw, int enable) +{ + int ret = 0; + + dev_dbg(msm_sdw->dev, "%s: enable %d\n", __func__, enable); + + mutex_lock(&msm_sdw->sdw_npl_clk_mutex); + if (enable) { + if (msm_sdw->sdw_npl_clk_enabled == false) { + msm_sdw->sdw_npl_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_npl_clk); + if (ret < 0) { + dev_err(msm_sdw->dev, + "%s: failed to enable SDW NPL CLK\n", + __func__); + mutex_unlock(&msm_sdw->sdw_npl_clk_mutex); + return ret; + } + dev_dbg(msm_sdw->dev, "enabled sdw npl clk\n"); + msm_sdw->sdw_npl_clk_enabled = true; + } + } else { + if (msm_sdw->sdw_npl_clk_enabled == true) { + msm_sdw->sdw_npl_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_npl_clk); + if (ret < 0) + dev_err(msm_sdw->dev, + "%s: failed to disable SDW NPL CLK\n", + __func__); + msm_sdw->sdw_npl_clk_enabled = false; + } + } + mutex_unlock(&msm_sdw->sdw_npl_clk_mutex); + return ret; +} + +static int msm_int_enable_sdw_cdc_clk(struct msm_sdw_priv *msm_sdw, + int enable, bool dapm) +{ + int ret = 0; + + mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); + dev_dbg(msm_sdw->dev, "%s: enable %d mclk1 ref counter %d\n", + __func__, enable, msm_sdw->int_mclk1_rsc_ref); + if (enable) { + if (msm_sdw->int_mclk1_rsc_ref == 0) { + cancel_delayed_work_sync( + &msm_sdw->disable_int_mclk1_work); + if (msm_sdw->int_mclk1_enabled == false) { + msm_sdw->sdw_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_cdc_core_clk); + if (ret < 0) { + dev_err(msm_sdw->dev, + "%s: failed to enable SDW MCLK\n", + __func__); + goto rtn; + } + dev_dbg(msm_sdw->dev, + "enabled sdw codec core mclk\n"); + msm_sdw->int_mclk1_enabled = true; + } + } + msm_sdw->int_mclk1_rsc_ref++; + } else { + cancel_delayed_work_sync(&msm_sdw->disable_int_mclk1_work); + if (msm_sdw->int_mclk1_rsc_ref > 0) { + msm_sdw->int_mclk1_rsc_ref--; + dev_dbg(msm_sdw->dev, + "%s: decrementing mclk_res_ref %d\n", + __func__, msm_sdw->int_mclk1_rsc_ref); + } + if (msm_sdw->int_mclk1_enabled == true && + msm_sdw->int_mclk1_rsc_ref == 0) { + msm_sdw->sdw_cdc_core_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_cdc_core_clk); + if (ret < 0) + dev_err(msm_sdw->dev, + "%s: failed to disable SDW MCLK\n", + __func__); + msm_sdw->int_mclk1_enabled = false; + } + } + mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); +rtn: + return ret; +} +EXPORT_SYMBOL(msm_int_enable_sdw_cdc_clk); + +static void msm_disable_int_mclk1(struct work_struct *work) +{ + struct msm_sdw_priv *msm_sdw = NULL; + struct delayed_work *dwork; + int ret = 0; + + dwork = to_delayed_work(work); + msm_sdw = container_of(dwork, struct msm_sdw_priv, + disable_int_mclk1_work); + mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); + dev_dbg(msm_sdw->dev, "%s: mclk1_enabled %d mclk1_rsc_ref %d\n", + __func__, msm_sdw->int_mclk1_enabled, + msm_sdw->int_mclk1_rsc_ref); + if (msm_sdw->int_mclk1_enabled == true + && msm_sdw->int_mclk1_rsc_ref == 0) { + dev_dbg(msm_sdw->dev, "Disable the mclk1\n"); + msm_sdw->sdw_cdc_core_clk.enable = 0; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_cdc_core_clk); + if (ret < 0) + dev_err(msm_sdw->dev, + "%s failed to disable the MCLK1\n", + __func__); + msm_sdw->int_mclk1_enabled = false; + } + mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); +} + +static int msm_int_mclk1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(msm_sdw->dev, "%s: event = %d\n", __func__, event); + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* enable the codec mclk config */ + msm_int_enable_sdw_cdc_clk(msm_sdw, 1, true); + msm_sdw_mclk_enable(msm_sdw, 1, true); + break; + case SND_SOC_DAPM_POST_PMD: + /* disable the codec mclk config */ + msm_sdw_mclk_enable(msm_sdw, 0, true); + msm_int_enable_sdw_cdc_clk(msm_sdw, 0, true); + break; + default: + dev_err(msm_sdw->dev, + "%s: invalid DAPM event %d\n", __func__, event); + ret = -EINVAL; + } + return ret; +} + +static int msm_sdw_ahb_write_device(struct msm_sdw_priv *msm_sdw, + u16 reg, u8 *value) +{ + u32 temp = (u32)(*value) & 0x000000FF; + + if (!msm_sdw->dev_up) { + dev_err_ratelimited(msm_sdw->dev, "%s: q6 not ready\n", + __func__); + return 0; + } + + iowrite32(temp, msm_sdw->sdw_base + reg); + return 0; +} + +static int msm_sdw_ahb_read_device(struct msm_sdw_priv *msm_sdw, + u16 reg, u8 *value) +{ + u32 temp; + + if (!msm_sdw->dev_up) { + dev_err_ratelimited(msm_sdw->dev, "%s: q6 not ready\n", + __func__); + return 0; + } + + temp = ioread32(msm_sdw->sdw_base + reg); + *value = (u8)temp; + return 0; +} + +static int __msm_sdw_reg_read(struct msm_sdw_priv *msm_sdw, unsigned short reg, + int bytes, void *dest) +{ + int ret = -EINVAL, i; + u8 temp = 0; + + dev_dbg(msm_sdw->dev, "%s reg = %x\n", __func__, reg); + mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); + if (msm_sdw->int_mclk1_enabled == false) { + msm_sdw->sdw_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2( + AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_cdc_core_clk); + if (ret < 0) { + dev_err(msm_sdw->dev, + "%s:failed to enable the INT_MCLK1\n", + __func__); + goto unlock_exit; + } + dev_dbg(msm_sdw->dev, "%s:enabled sdw codec core clk\n", + __func__); + for (i = 0; i < bytes; i++) { + ret = msm_sdw_ahb_read_device( + msm_sdw, reg + (4 * i), &temp); + ((u8 *)dest)[i] = temp; + } + msm_sdw->int_mclk1_enabled = true; + schedule_delayed_work(&msm_sdw->disable_int_mclk1_work, 50); + goto unlock_exit; + } + for (i = 0; i < bytes; i++) { + ret = msm_sdw_ahb_read_device( + msm_sdw, reg + (4 * i), &temp); + ((u8 *)dest)[i] = temp; + } +unlock_exit: + mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); + if (ret < 0) { + dev_err_ratelimited(msm_sdw->dev, + "%s: codec read failed for reg 0x%x\n", + __func__, reg); + return ret; + } + dev_dbg(msm_sdw->dev, "Read 0x%02x from 0x%x\n", temp, reg); + + return 0; +} + +static int __msm_sdw_reg_write(struct msm_sdw_priv *msm_sdw, unsigned short reg, + int bytes, void *src) +{ + int ret = -EINVAL, i; + + mutex_lock(&msm_sdw->cdc_int_mclk1_mutex); + if (msm_sdw->int_mclk1_enabled == false) { + msm_sdw->sdw_cdc_core_clk.enable = 1; + ret = afe_set_lpass_clock_v2(AFE_PORT_ID_INT4_MI2S_RX, + &msm_sdw->sdw_cdc_core_clk); + if (ret < 0) { + dev_err(msm_sdw->dev, + "%s: failed to enable the INT_MCLK1\n", + __func__); + ret = 0; + goto unlock_exit; + } + dev_dbg(msm_sdw->dev, "%s: enabled INT_MCLK1\n", __func__); + for (i = 0; i < bytes; i++) + ret = msm_sdw_ahb_write_device(msm_sdw, reg + (4 * i), + &((u8 *)src)[i]); + msm_sdw->int_mclk1_enabled = true; + schedule_delayed_work(&msm_sdw->disable_int_mclk1_work, 50); + goto unlock_exit; + } + for (i = 0; i < bytes; i++) + ret = msm_sdw_ahb_write_device(msm_sdw, reg + (4 * i), + &((u8 *)src)[i]); +unlock_exit: + mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); + dev_dbg(msm_sdw->dev, "Write 0x%x val 0x%02x\n", + reg, (u32)(*(u32 *)src)); + + return ret; +} + +static int msm_sdw_codec_enable_vi_feedback(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = NULL; + struct msm_sdw_priv *msm_sdw_p = NULL; + int ret = 0; + + if (!w) { + pr_err("%s invalid params\n", __func__); + return -EINVAL; + } + codec = snd_soc_dapm_to_codec(w->dapm); + msm_sdw_p = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: num_dai %d stream name %s\n", + __func__, codec->component.num_dai, w->sname); + + dev_dbg(codec->dev, "%s(): w->name %s event %d w->shift %d\n", + __func__, w->name, event, w->shift); + if (w->shift != AIF1_SDW_VIFEED) { + dev_err(codec->dev, + "%s:Error in enabling the vi feedback path\n", + __func__); + ret = -EINVAL; + goto out_vi; + } + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (test_bit(VI_SENSE_1, &msm_sdw_p->status_mask)) { + dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x04); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x0F, 0x04); + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + if (test_bit(VI_SENSE_2, &msm_sdw_p->status_mask)) { + dev_dbg(codec->dev, "%s: spkr2 enabled\n", __func__); + /* Enable V&I sensing */ + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x0F, + 0x04); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x0F, + 0x04); + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x10); + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x00); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (test_bit(VI_SENSE_1, &msm_sdw_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__); + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00); + snd_soc_update_bits(codec, + MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + if (test_bit(VI_SENSE_2, &msm_sdw_p->status_mask)) { + /* Disable V&I sensing */ + dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__); + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x20, + 0x20); + snd_soc_update_bits(codec, + MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + snd_soc_update_bits(codec, + MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x10, + 0x00); + } + break; + } +out_vi: + return ret; +} + +static int msm_sdwm_handle_irq(void *handle, + irqreturn_t (*swrm_irq_handler)(int irq, + void *data), + void *swrm_handle, + int action) +{ + struct msm_sdw_priv *msm_sdw; + int ret = 0; + + if (!handle) { + pr_err("%s: null handle received\n", __func__); + return -EINVAL; + } + msm_sdw = (struct msm_sdw_priv *) handle; + + if (skip_irq) + return ret; + + if (action) { + ret = request_threaded_irq(msm_sdw->sdw_irq, NULL, + swrm_irq_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "swr_master_irq", swrm_handle); + if (ret) + dev_err(msm_sdw->dev, "%s: Failed to request irq %d\n", + __func__, ret); + } else + free_irq(msm_sdw->sdw_irq, swrm_handle); + + return ret; +} + +static void msm_sdw_codec_hd2_control(struct snd_soc_codec *codec, + u16 reg, int event) +{ + u16 hd2_scale_reg; + u16 hd2_enable_reg = 0; + + if (reg == MSM_SDW_RX7_RX_PATH_CTL) { + hd2_scale_reg = MSM_SDW_RX7_RX_PATH_SEC3; + hd2_enable_reg = MSM_SDW_RX7_RX_PATH_CFG0; + } + if (reg == MSM_SDW_RX8_RX_PATH_CTL) { + hd2_scale_reg = MSM_SDW_RX8_RX_PATH_SEC3; + hd2_enable_reg = MSM_SDW_RX8_RX_PATH_CFG0; + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) { + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01); + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04); + } + + if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00); + snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00); + } +} + +static int msm_sdw_enable_swr(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_sdw_priv *msm_sdw; + int i, ch_cnt; + + msm_sdw = snd_soc_codec_get_drvdata(codec); + + if (!msm_sdw->nr) + return 0; + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (!(strnstr(w->name, "RX4", sizeof("RX4 MIX"))) && + !msm_sdw->rx_4_count) + msm_sdw->rx_4_count++; + if (!(strnstr(w->name, "RX5", sizeof("RX5 MIX"))) && + !msm_sdw->rx_5_count) + msm_sdw->rx_5_count++; + ch_cnt = msm_sdw->rx_4_count + msm_sdw->rx_5_count; + + for (i = 0; i < msm_sdw->nr; i++) { + swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, + SWR_DEVICE_UP, NULL); + swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + } + break; + case SND_SOC_DAPM_POST_PMD: + if (!(strnstr(w->name, "RX4", sizeof("RX4 MIX"))) && + msm_sdw->rx_4_count) + msm_sdw->rx_4_count--; + if (!(strnstr(w->name, "RX5", sizeof("RX5 MIX"))) && + msm_sdw->rx_5_count) + msm_sdw->rx_5_count--; + ch_cnt = msm_sdw->rx_4_count + msm_sdw->rx_5_count; + + for (i = 0; i < msm_sdw->nr; i++) + swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, + SWR_SET_NUM_RX_CH, &ch_cnt); + break; + } + dev_dbg(msm_sdw->dev, "%s: current swr ch cnt: %d\n", + __func__, msm_sdw->rx_4_count + msm_sdw->rx_5_count); + + return 0; +} + +static int msm_sdw_codec_enable_interpolator(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + u16 gain_reg; + u16 reg; + int val; + int offset_val = 0; + + dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name); + + if (!(strcmp(w->name, "RX INT4 INTERP"))) { + reg = MSM_SDW_RX7_RX_PATH_CTL; + gain_reg = MSM_SDW_RX7_RX_VOL_CTL; + } else if (!(strcmp(w->name, "RX INT5 INTERP"))) { + reg = MSM_SDW_RX8_RX_PATH_CTL; + gain_reg = MSM_SDW_RX8_RX_VOL_CTL; + } else { + dev_err(codec->dev, "%s: Interpolator reg not found\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, reg, 0x10, 0x10); + msm_sdw_codec_hd2_control(codec, reg, event); + snd_soc_update_bits(codec, reg, 1 << 0x5, 1 << 0x5); + break; + case SND_SOC_DAPM_POST_PMU: + msm_sdw_config_compander(codec, w->shift, event); + /* apply gain after int clk is enabled */ + if ((msm_sdw->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (msm_sdw->comp_enabled[COMP1] || + msm_sdw->comp_enabled[COMP2]) && + (gain_reg == MSM_SDW_RX7_RX_VOL_CTL || + gain_reg == MSM_SDW_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, MSM_SDW_RX7_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + MSM_SDW_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x01); + snd_soc_update_bits(codec, MSM_SDW_RX8_RX_PATH_SEC1, + 0x01, 0x01); + snd_soc_update_bits(codec, + MSM_SDW_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x01); + offset_val = -2; + } + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + msm_sdw_config_ear_spkr_gain(codec, event, gain_reg); + snd_soc_update_bits(codec, reg, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, reg, 1 << 0x5, 0 << 0x5); + snd_soc_update_bits(codec, reg, 0x40, 0x40); + snd_soc_update_bits(codec, reg, 0x40, 0x00); + msm_sdw_codec_hd2_control(codec, reg, event); + msm_sdw_config_compander(codec, w->shift, event); + if ((msm_sdw->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) && + (msm_sdw->comp_enabled[COMP1] || + msm_sdw->comp_enabled[COMP2]) && + (gain_reg == MSM_SDW_RX7_RX_VOL_CTL || + gain_reg == MSM_SDW_RX8_RX_VOL_CTL)) { + snd_soc_update_bits(codec, MSM_SDW_RX7_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + MSM_SDW_RX7_RX_PATH_MIX_SEC0, + 0x01, 0x00); + snd_soc_update_bits(codec, MSM_SDW_RX8_RX_PATH_SEC1, + 0x01, 0x00); + snd_soc_update_bits(codec, + MSM_SDW_RX8_RX_PATH_MIX_SEC0, + 0x01, 0x00); + offset_val = 2; + val = snd_soc_read(codec, gain_reg); + val += offset_val; + snd_soc_write(codec, gain_reg, val); + } + msm_sdw_config_ear_spkr_gain(codec, event, gain_reg); + break; + }; + + return 0; +} + +static int msm_sdw_config_ear_spkr_gain(struct snd_soc_codec *codec, + int event, int gain_reg) +{ + int comp_gain_offset, val; + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + + switch (msm_sdw->spkr_mode) { + /* Compander gain in SPKR_MODE1 case is 12 dB */ + case SPKR_MODE_1: + comp_gain_offset = -12; + break; + /* Default case compander gain is 15 dB */ + default: + comp_gain_offset = -15; + break; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* Apply ear spkr gain only if compander is enabled */ + if (msm_sdw->comp_enabled[COMP1] && + (gain_reg == MSM_SDW_RX7_RX_VOL_CTL) && + (msm_sdw->ear_spkr_gain != 0)) { + /* For example, val is -8(-12+5-1) for 4dB of gain */ + val = comp_gain_offset + msm_sdw->ear_spkr_gain - 1; + snd_soc_write(codec, gain_reg, val); + + dev_dbg(codec->dev, "%s: RX4 Volume %d dB\n", + __func__, val); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* + * Reset RX4 volume to 0 dB if compander is enabled and + * ear_spkr_gain is non-zero. + */ + if (msm_sdw->comp_enabled[COMP1] && + (gain_reg == MSM_SDW_RX7_RX_VOL_CTL) && + (msm_sdw->ear_spkr_gain != 0)) { + snd_soc_write(codec, gain_reg, 0x0); + + dev_dbg(codec->dev, "%s: Reset RX4 Volume to 0 dB\n", + __func__); + } + break; + } + + return 0; +} + +static int msm_sdw_codec_spk_boost_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); + u16 boost_path_ctl, boost_path_cfg1; + u16 reg; + + dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); + + if (!strcmp(w->name, "RX INT4 CHAIN")) { + boost_path_ctl = MSM_SDW_BOOST0_BOOST_PATH_CTL; + boost_path_cfg1 = MSM_SDW_RX7_RX_PATH_CFG1; + reg = MSM_SDW_RX7_RX_PATH_CTL; + } else if (!strcmp(w->name, "RX INT5 CHAIN")) { + boost_path_ctl = MSM_SDW_BOOST1_BOOST_PATH_CTL; + boost_path_cfg1 = MSM_SDW_RX8_RX_PATH_CFG1; + reg = MSM_SDW_RX8_RX_PATH_CTL; + } else { + dev_err(codec->dev, "%s: boost reg not found\n", + __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10); + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01); + snd_soc_update_bits(codec, reg, 0x10, 0x00); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00); + snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00); + break; + }; + + return 0; +} + +static int msm_sdw_config_compander(struct snd_soc_codec *codec, int comp, + int event) +{ + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + u16 comp_ctl0_reg, rx_path_cfg0_reg; + + if (comp < COMP1 || comp >= COMP_MAX) + return 0; + + dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n", + __func__, event, comp + 1, msm_sdw->comp_enabled[comp]); + + if (!msm_sdw->comp_enabled[comp]) + return 0; + + comp_ctl0_reg = MSM_SDW_COMPANDER7_CTL0 + (comp * 0x20); + rx_path_cfg0_reg = MSM_SDW_RX7_RX_PATH_CFG0 + (comp * 0x1E0); + + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02); + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04); + snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00); + snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00); + } + + return 0; +} + +static int msm_sdw_get_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = msm_sdw->comp_enabled[comp]; + return 0; +} + +static int msm_sdw_set_compander(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + int comp = ((struct soc_multi_mixer_control *) + kcontrol->private_value)->shift; + int value = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n", + __func__, comp + 1, msm_sdw->comp_enabled[comp], value); + msm_sdw->comp_enabled[comp] = value; + + return 0; +} + +static int msm_sdw_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = msm_sdw->ear_spkr_gain; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_sdw_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct msm_sdw_priv *msm_sdw = snd_soc_codec_get_drvdata(codec); + + msm_sdw->ear_spkr_gain = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: gain = %d\n", __func__, + msm_sdw->ear_spkr_gain); + + return 0; +} + +static int msm_sdw_vi_feed_mixer_get(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_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = msm_sdw_p->vi_feed_value; + + return 0; +} + +static int msm_sdw_vi_feed_mixer_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 snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec); + struct soc_multi_mixer_control *mixer = + ((struct soc_multi_mixer_control *)kcontrol->private_value); + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + + dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n", + __func__, enable, port_id, dai_id); + + msm_sdw_p->vi_feed_value = ucontrol->value.integer.value[0]; + + mutex_lock(&msm_sdw_p->codec_mutex); + if (enable) { + if (port_id == MSM_SDW_TX0 && !test_bit(VI_SENSE_1, + &msm_sdw_p->status_mask)) + set_bit(VI_SENSE_1, &msm_sdw_p->status_mask); + if (port_id == MSM_SDW_TX1 && !test_bit(VI_SENSE_2, + &msm_sdw_p->status_mask)) + set_bit(VI_SENSE_2, &msm_sdw_p->status_mask); + } else { + if (port_id == MSM_SDW_TX0 && test_bit(VI_SENSE_1, + &msm_sdw_p->status_mask)) + clear_bit(VI_SENSE_1, &msm_sdw_p->status_mask); + if (port_id == MSM_SDW_TX1 && test_bit(VI_SENSE_2, + &msm_sdw_p->status_mask)) + clear_bit(VI_SENSE_2, &msm_sdw_p->status_mask); + } + mutex_unlock(&msm_sdw_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL); + + return 0; +} + +static int msm_sdw_mclk_enable(struct msm_sdw_priv *msm_sdw, + int mclk_enable, bool dapm) +{ + dev_dbg(msm_sdw->dev, "%s: mclk_enable = %u, dapm = %d clk_users= %d\n", + __func__, mclk_enable, dapm, msm_sdw->sdw_mclk_users); + if (mclk_enable) { + msm_sdw->sdw_mclk_users++; + if (msm_sdw->sdw_mclk_users == 1) { + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x01); + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x01); + /* 9.6MHz MCLK, set value 0x00 if other frequency */ + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_TOP_FREQ_MCLK, 0x01, 0x01); + } + } else { + msm_sdw->sdw_mclk_users--; + if (msm_sdw->sdw_mclk_users == 0) { + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL, + 0x01, 0x00); + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL, + 0x01, 0x00); + } + } + return 0; +} +EXPORT_SYMBOL(msm_sdw_mclk_enable); + +static int msm_sdw_swrm_read(void *handle, int reg) +{ + struct msm_sdw_priv *msm_sdw; + unsigned short sdw_rd_addr_base; + unsigned short sdw_rd_data_base; + int val, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + msm_sdw = (struct msm_sdw_priv *)handle; + + dev_dbg(msm_sdw->dev, "%s: Reading soundwire register, 0x%x\n", + __func__, reg); + sdw_rd_addr_base = MSM_SDW_AHB_BRIDGE_RD_ADDR_0; + sdw_rd_data_base = MSM_SDW_AHB_BRIDGE_RD_DATA_0; + /* + * Add sleep as SWR slave access read takes time. + * Allow for RD_DONE to complete for previous register if any. + */ + usleep_range(100, 105); + + /* read_lock */ + mutex_lock(&msm_sdw->sdw_read_lock); + ret = regmap_bulk_write(msm_sdw->regmap, sdw_rd_addr_base, + (u8 *)®, 4); + if (ret < 0) { + dev_err(msm_sdw->dev, "%s: RD Addr Failure\n", __func__); + goto err; + } + /* Add sleep for SWR register read value to get updated. */ + usleep_range(100, 105); + /* Check for RD value */ + ret = regmap_bulk_read(msm_sdw->regmap, sdw_rd_data_base, + (u8 *)&val, 4); + if (ret < 0) { + dev_err(msm_sdw->dev, "%s: RD Data Failure\n", __func__); + goto err; + } + ret = val; +err: + /* read_unlock */ + mutex_unlock(&msm_sdw->sdw_read_lock); + return ret; +} + +static int msm_sdw_bulk_write(struct msm_sdw_priv *msm_sdw, + struct msm_sdw_reg_val *bulk_reg, + size_t len) +{ + int i, ret = 0; + unsigned short sdw_wr_addr_base; + unsigned short sdw_wr_data_base; + + sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0; + sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0; + + for (i = 0; i < len; i += 2) { + /* + * Add sleep as SWR slave write takes time. + * Allow for any previous pending write to complete. + */ + usleep_range(100, 105); + /* First Write the Data to register */ + ret = regmap_bulk_write(msm_sdw->regmap, + sdw_wr_data_base, bulk_reg[i].buf, 4); + if (ret < 0) { + dev_err(msm_sdw->dev, "%s: WR Data Failure\n", + __func__); + break; + } + /* Next Write Address */ + ret = regmap_bulk_write(msm_sdw->regmap, + sdw_wr_addr_base, bulk_reg[i+1].buf, 4); + if (ret < 0) { + dev_err(msm_sdw->dev, + "%s: WR Addr Failure: 0x%x\n", + __func__, (u32)(bulk_reg[i+1].buf[0])); + break; + } + } + return ret; +} + +static int msm_sdw_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) +{ + struct msm_sdw_priv *msm_sdw; + struct msm_sdw_reg_val *bulk_reg; + unsigned short sdw_wr_addr_base; + unsigned short sdw_wr_data_base; + int i, j, ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + + msm_sdw = (struct msm_sdw_priv *)handle; + if (len <= 0) { + dev_err(msm_sdw->dev, + "%s: Invalid size: %zu\n", __func__, len); + return -EINVAL; + } + + sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0; + sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0; + + bulk_reg = kzalloc((2 * len * sizeof(struct msm_sdw_reg_val)), + GFP_KERNEL); + if (!bulk_reg) + return -ENOMEM; + + for (i = 0, j = 0; i < (len * 2); i += 2, j++) { + bulk_reg[i].reg = sdw_wr_data_base; + bulk_reg[i].buf = (u8 *)(&val[j]); + bulk_reg[i].bytes = 4; + bulk_reg[i+1].reg = sdw_wr_addr_base; + bulk_reg[i+1].buf = (u8 *)(®[j]); + bulk_reg[i+1].bytes = 4; + } + mutex_lock(&msm_sdw->sdw_write_lock); + + ret = msm_sdw_bulk_write(msm_sdw, bulk_reg, (len * 2)); + if (ret) + dev_err(msm_sdw->dev, "%s: swrm bulk write failed, ret: %d\n", + __func__, ret); + + mutex_unlock(&msm_sdw->sdw_write_lock); + kfree(bulk_reg); + + return ret; +} + +static int msm_sdw_swrm_write(void *handle, int reg, int val) +{ + struct msm_sdw_priv *msm_sdw; + unsigned short sdw_wr_addr_base; + unsigned short sdw_wr_data_base; + struct msm_sdw_reg_val bulk_reg[2]; + int ret; + + if (!handle) { + pr_err("%s: NULL handle\n", __func__); + return -EINVAL; + } + msm_sdw = (struct msm_sdw_priv *)handle; + + sdw_wr_addr_base = MSM_SDW_AHB_BRIDGE_WR_ADDR_0; + sdw_wr_data_base = MSM_SDW_AHB_BRIDGE_WR_DATA_0; + + /* First Write the Data to register */ + bulk_reg[0].reg = sdw_wr_data_base; + bulk_reg[0].buf = (u8 *)(&val); + bulk_reg[0].bytes = 4; + bulk_reg[1].reg = sdw_wr_addr_base; + bulk_reg[1].buf = (u8 *)(®); + bulk_reg[1].bytes = 4; + + mutex_lock(&msm_sdw->sdw_write_lock); + + ret = msm_sdw_bulk_write(msm_sdw, bulk_reg, 2); + if (ret < 0) + dev_err(msm_sdw->dev, "%s: WR Data Failure\n", __func__); + + mutex_unlock(&msm_sdw->sdw_write_lock); + return ret; +} + +static int msm_sdw_swrm_clock(void *handle, bool enable) +{ + struct msm_sdw_priv *msm_sdw = (struct msm_sdw_priv *) handle; + + mutex_lock(&msm_sdw->sdw_clk_lock); + + dev_dbg(msm_sdw->dev, "%s: swrm clock %s\n", + __func__, (enable ? "enable" : "disable")); + if (enable) { + msm_sdw->sdw_clk_users++; + if (msm_sdw->sdw_clk_users == 1) { + msm_int_enable_sdw_cdc_clk(msm_sdw, 1, true); + msm_sdw_mclk_enable(msm_sdw, 1, true); + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_SWR_CONTROL, 0x01, 0x01); + msm_enable_sdw_npl_clk(msm_sdw, true); + msm_cdc_pinctrl_select_active_state( + msm_sdw->sdw_gpio_p); + } + } else { + msm_sdw->sdw_clk_users--; + if (msm_sdw->sdw_clk_users == 0) { + regmap_update_bits(msm_sdw->regmap, + MSM_SDW_CLK_RST_CTRL_SWR_CONTROL, + 0x01, 0x00); + msm_sdw_mclk_enable(msm_sdw, 0, true); + msm_int_enable_sdw_cdc_clk(msm_sdw, 0, true); + msm_enable_sdw_npl_clk(msm_sdw, false); + msm_cdc_pinctrl_select_sleep_state(msm_sdw->sdw_gpio_p); + } + } + dev_dbg(msm_sdw->dev, "%s: swrm clock users %d\n", + __func__, msm_sdw->sdw_clk_users); + mutex_unlock(&msm_sdw->sdw_clk_lock); + return 0; +} + +static int msm_sdw_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + dev_dbg(dai->codec->dev, "%s(): substream = %s stream = %d\n", + __func__, + substream->name, substream->stream); + return 0; +} + +static int msm_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u8 clk_fs_rate, fs_rate; + + dev_dbg(dai->codec->dev, + "%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n", + __func__, dai->name, dai->id, params_rate(params), + params_channels(params), params_format(params)); + + switch (params_rate(params)) { + case 8000: + clk_fs_rate = 0x00; + fs_rate = 0x00; + break; + case 16000: + clk_fs_rate = 0x01; + fs_rate = 0x01; + break; + case 32000: + clk_fs_rate = 0x02; + fs_rate = 0x03; + break; + case 48000: + clk_fs_rate = 0x03; + fs_rate = 0x04; + break; + case 96000: + clk_fs_rate = 0x04; + fs_rate = 0x05; + break; + case 192000: + clk_fs_rate = 0x05; + fs_rate = 0x06; + break; + default: + dev_err(dai->codec->dev, + "%s: Invalid sampling rate %d\n", __func__, + params_rate(params)); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_TX_I2S_CTL, 0x1C, + (clk_fs_rate << 2)); + } else { + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_RX_I2S_CTL, 0x1C, + (clk_fs_rate << 2)); + snd_soc_update_bits(dai->codec, + MSM_SDW_RX7_RX_PATH_CTL, 0x0F, + fs_rate); + snd_soc_update_bits(dai->codec, + MSM_SDW_RX8_RX_PATH_CTL, 0x0F, + fs_rate); + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_TX_I2S_CTL, 0x20, 0x20); + else + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_RX_I2S_CTL, 0x20, 0x20); + break; + case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_TX_I2S_CTL, 0x20, 0x00); + else + snd_soc_update_bits(dai->codec, + MSM_SDW_TOP_RX_I2S_CTL, 0x20, 0x00); + break; + default: + dev_err(dai->codec->dev, "%s: wrong format selected\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static void msm_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + dev_dbg(dai->codec->dev, + "%s(): substream = %s stream = %d\n", __func__, + substream->name, substream->stream); +} + +static ssize_t msm_sdw_codec_version_read(struct snd_info_entry *entry, + void *file_private_data, + struct file *file, + char __user *buf, size_t count, + loff_t pos) +{ + struct msm_sdw_priv *msm_sdw; + char buffer[MSM_SDW_VERSION_ENTRY_SIZE]; + int len = 0; + + msm_sdw = (struct msm_sdw_priv *) entry->private_data; + if (!msm_sdw) { + pr_err("%s: msm_sdw priv is null\n", __func__); + return -EINVAL; + } + + switch (msm_sdw->version) { + case MSM_SDW_VERSION_1_0: + len = snprintf(buffer, sizeof(buffer), "SDW-CDC_1_0\n"); + break; + default: + len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n"); + } + + return simple_read_from_buffer(buf, count, &pos, buffer, len); +} + +static struct snd_info_entry_ops msm_sdw_codec_info_ops = { + .read = msm_sdw_codec_version_read, +}; + +/* + * msm_sdw_codec_info_create_codec_entry - creates msm_sdw module + * @codec_root: The parent directory + * @codec: Codec instance + * + * Creates msm_sdw module and version entry under the given + * parent directory. + * + * Return: 0 on success or negative error code on failure. + */ +int msm_sdw_codec_info_create_codec_entry(struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + struct snd_info_entry *version_entry; + struct msm_sdw_priv *msm_sdw; + struct snd_soc_card *card; + + if (!codec_root || !codec) + return -EINVAL; + + msm_sdw = snd_soc_codec_get_drvdata(codec); + card = codec->component.card; + msm_sdw->entry = snd_info_create_subdir(codec_root->module, + "152c1000.msm-sdw-codec", + codec_root); + if (!msm_sdw->entry) { + dev_err(codec->dev, "%s: failed to create msm_sdw entry\n", + __func__); + return -ENOMEM; + } + + version_entry = snd_info_create_card_entry(card->snd_card, + "version", + msm_sdw->entry); + if (!version_entry) { + dev_err(codec->dev, "%s: failed to create msm_sdw version entry\n", + __func__); + return -ENOMEM; + } + + version_entry->private_data = msm_sdw; + version_entry->size = MSM_SDW_VERSION_ENTRY_SIZE; + version_entry->content = SNDRV_INFO_CONTENT_DATA; + version_entry->c.ops = &msm_sdw_codec_info_ops; + + if (snd_info_register(version_entry) < 0) { + snd_info_free_entry(version_entry); + return -ENOMEM; + } + msm_sdw->version_entry = version_entry; + + return 0; +} +EXPORT_SYMBOL(msm_sdw_codec_info_create_codec_entry); + +static struct snd_soc_dai_ops msm_sdw_dai_ops = { + .startup = msm_sdw_startup, + .shutdown = msm_sdw_shutdown, + .hw_params = msm_sdw_hw_params, +}; + +static struct snd_soc_dai_driver msm_sdw_dai[] = { + { + .name = "msm_sdw_i2s_rx1", + .id = AIF1_SDW_PB, + .playback = { + .stream_name = "AIF1_SDW Playback", + .rates = MSM_SDW_RATES, + .formats = MSM_SDW_FORMATS, + .rate_max = 192000, + .rate_min = 8000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &msm_sdw_dai_ops, + }, + { + .name = "msm_sdw_vifeedback", + .id = AIF1_SDW_VIFEED, + .capture = { + .stream_name = "VIfeed_SDW", + .rates = MSM_SDW_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_max = 48000, + .rate_min = 8000, + .channels_min = 2, + .channels_max = 4, + }, + .ops = &msm_sdw_dai_ops, + }, +}; + +static const char * const rx_mix1_text[] = { + "ZERO", "RX4", "RX5" +}; + +static const char * const msm_sdw_ear_spkr_pa_gain_text[] = { + "G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", + "G_4_DB", "G_5_DB", "G_6_DB" +}; + +static SOC_ENUM_SINGLE_EXT_DECL(msm_sdw_ear_spkr_pa_gain_enum, + msm_sdw_ear_spkr_pa_gain_text); +/* RX4 MIX1 */ +static const struct soc_enum rx4_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM_SDW_TOP_RX7_PATH_INPUT0_MUX, + 0, 3, rx_mix1_text); + +static const struct soc_enum rx4_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM_SDW_TOP_RX7_PATH_INPUT1_MUX, + 0, 3, rx_mix1_text); + +/* RX5 MIX1 */ +static const struct soc_enum rx5_mix1_inp1_chain_enum = + SOC_ENUM_SINGLE(MSM_SDW_TOP_RX8_PATH_INPUT0_MUX, + 0, 3, rx_mix1_text); + +static const struct soc_enum rx5_mix1_inp2_chain_enum = + SOC_ENUM_SINGLE(MSM_SDW_TOP_RX8_PATH_INPUT1_MUX, + 0, 3, rx_mix1_text); + +static const struct snd_kcontrol_new rx4_mix1_inp1_mux = + SOC_DAPM_ENUM("RX4 MIX1 INP1 Mux", rx4_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx4_mix1_inp2_mux = + SOC_DAPM_ENUM("RX4 MIX1 INP2 Mux", rx4_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new rx5_mix1_inp1_mux = + SOC_DAPM_ENUM("RX5 MIX1 INP1 Mux", rx5_mix1_inp1_chain_enum); + +static const struct snd_kcontrol_new rx5_mix1_inp2_mux = + SOC_DAPM_ENUM("RX5 MIX1 INP2 Mux", rx5_mix1_inp2_chain_enum); + +static const struct snd_kcontrol_new aif1_vi_mixer[] = { + SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, MSM_SDW_TX0, 1, 0, + msm_sdw_vi_feed_mixer_get, msm_sdw_vi_feed_mixer_put), + SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, MSM_SDW_TX1, 1, 0, + msm_sdw_vi_feed_mixer_get, msm_sdw_vi_feed_mixer_put), +}; + +static const struct snd_soc_dapm_widget msm_sdw_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("I2S RX4", "AIF1_SDW Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("I2S RX5", "AIF1_SDW Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_OUT_E("AIF1_SDW VI", "VIfeed_SDW", 0, SND_SOC_NOPM, + AIF1_SDW_VIFEED, 0, msm_sdw_codec_enable_vi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("AIF1_VI_SDW Mixer", SND_SOC_NOPM, AIF1_SDW_VIFEED, + 0, aif1_vi_mixer, ARRAY_SIZE(aif1_vi_mixer)), + + SND_SOC_DAPM_MUX_E("RX4 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx4_mix1_inp1_mux, msm_sdw_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX4 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx4_mix1_inp2_mux, msm_sdw_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX5 MIX1 INP1", SND_SOC_NOPM, 0, 0, + &rx5_mix1_inp1_mux, msm_sdw_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("RX5 MIX1 INP2", SND_SOC_NOPM, 0, 0, + &rx5_mix1_inp2_mux, msm_sdw_enable_swr, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER("RX4 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("RX5 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("RX INT4 INTERP", SND_SOC_NOPM, + COMP1, 0, NULL, 0, msm_sdw_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT5 INTERP", SND_SOC_NOPM, + COMP2, 0, NULL, 0, msm_sdw_codec_enable_interpolator, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER_E("RX INT4 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, msm_sdw_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("RX INT5 CHAIN", SND_SOC_NOPM, 0, 0, + NULL, 0, msm_sdw_codec_spk_boost_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_INPUT("VIINPUT_SDW"), + + SND_SOC_DAPM_OUTPUT("SPK1 OUT"), + SND_SOC_DAPM_OUTPUT("SPK2 OUT"), + + SND_SOC_DAPM_SUPPLY_S("SDW_CONN", -1, MSM_SDW_TOP_I2S_CLK, + 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("INT_MCLK1", -2, SND_SOC_NOPM, 0, 0, + msm_int_mclk1_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("SDW_RX_I2S_CLK", + MSM_SDW_TOP_RX_I2S_CTL, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SDW_TX_I2S_CLK", + MSM_SDW_TOP_TX_I2S_CTL, 0, 0, NULL, 0), +}; + +static const struct snd_kcontrol_new msm_sdw_snd_controls[] = { + SOC_ENUM_EXT("EAR SPKR PA Gain", msm_sdw_ear_spkr_pa_gain_enum, + msm_sdw_ear_spkr_pa_gain_get, + msm_sdw_ear_spkr_pa_gain_put), + SOC_SINGLE_SX_TLV("RX4 Digital Volume", MSM_SDW_RX7_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_SX_TLV("RX5 Digital Volume", MSM_SDW_RX8_RX_VOL_CTL, + 0, -84, 40, digital_gain), + SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMP1, 1, 0, + msm_sdw_get_compander, msm_sdw_set_compander), + SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMP2, 1, 0, + msm_sdw_get_compander, msm_sdw_set_compander), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + + {"AIF1_SDW VI", NULL, "SDW_TX_I2S_CLK"}, + {"SDW_TX_I2S_CLK", NULL, "INT_MCLK1"}, + {"SDW_TX_I2S_CLK", NULL, "SDW_CONN"}, + + /* VI Feedback */ + {"AIF1_VI_SDW Mixer", "SPKR_VI_1", "VIINPUT_SDW"}, + {"AIF1_VI_SDW Mixer", "SPKR_VI_2", "VIINPUT_SDW"}, + {"AIF1_SDW VI", NULL, "AIF1_VI_SDW Mixer"}, + + {"SDW_RX_I2S_CLK", NULL, "INT_MCLK1"}, + {"SDW_RX_I2S_CLK", NULL, "SDW_CONN"}, + {"I2S RX4", NULL, "SDW_RX_I2S_CLK"}, + {"I2S RX5", NULL, "SDW_RX_I2S_CLK"}, + + {"RX4 MIX1 INP1", "RX4", "I2S RX4"}, + {"RX4 MIX1 INP1", "RX5", "I2S RX5"}, + {"RX4 MIX1 INP2", "RX4", "I2S RX4"}, + {"RX4 MIX1 INP2", "RX5", "I2S RX5"}, + {"RX5 MIX1 INP1", "RX4", "I2S RX4"}, + {"RX5 MIX1 INP1", "RX5", "I2S RX5"}, + {"RX5 MIX1 INP2", "RX4", "I2S RX4"}, + {"RX5 MIX1 INP2", "RX5", "I2S RX5"}, + + {"RX4 MIX1", NULL, "RX4 MIX1 INP1"}, + {"RX4 MIX1", NULL, "RX4 MIX1 INP2"}, + {"RX5 MIX1", NULL, "RX5 MIX1 INP1"}, + {"RX5 MIX1", NULL, "RX5 MIX1 INP2"}, + + {"RX INT4 INTERP", NULL, "RX4 MIX1"}, + {"RX INT4 CHAIN", NULL, "RX INT4 INTERP"}, + {"SPK1 OUT", NULL, "RX INT4 CHAIN"}, + + {"RX INT5 INTERP", NULL, "RX5 MIX1"}, + {"RX INT5 CHAIN", NULL, "RX INT5 INTERP"}, + {"SPK2 OUT", NULL, "RX INT5 CHAIN"}, +}; + +static const struct msm_sdw_reg_mask_val msm_sdw_reg_init[] = { + {MSM_SDW_BOOST0_BOOST_CFG1, 0x3F, 0x12}, + {MSM_SDW_BOOST0_BOOST_CFG2, 0x1C, 0x08}, + {MSM_SDW_COMPANDER7_CTL7, 0x1E, 0x18}, + {MSM_SDW_BOOST1_BOOST_CFG1, 0x3F, 0x12}, + {MSM_SDW_BOOST1_BOOST_CFG2, 0x1C, 0x08}, + {MSM_SDW_COMPANDER8_CTL7, 0x1E, 0x18}, + {MSM_SDW_BOOST0_BOOST_CTL, 0x70, 0x50}, + {MSM_SDW_BOOST1_BOOST_CTL, 0x70, 0x50}, + {MSM_SDW_RX7_RX_PATH_CFG1, 0x08, 0x08}, + {MSM_SDW_RX8_RX_PATH_CFG1, 0x08, 0x08}, + {MSM_SDW_TOP_TOP_CFG1, 0x02, 0x02}, + {MSM_SDW_TOP_TOP_CFG1, 0x01, 0x01}, + {MSM_SDW_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_COMPANDER7_CTL3, 0x80, 0x80}, + {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x80}, + {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x01}, + {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x01}, + {MSM_SDW_RX7_RX_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_RX8_RX_PATH_CFG0, 0x01, 0x01}, + {MSM_SDW_RX7_RX_PATH_MIX_CFG, 0x01, 0x01}, + {MSM_SDW_RX8_RX_PATH_MIX_CFG, 0x01, 0x01}, +}; + +static void msm_sdw_init_reg(struct snd_soc_codec *codec) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(msm_sdw_reg_init); i++) + snd_soc_update_bits(codec, + msm_sdw_reg_init[i].reg, + msm_sdw_reg_init[i].mask, + msm_sdw_reg_init[i].val); +} + +static int msm_sdw_notifier_service_cb(struct notifier_block *nb, + unsigned long opcode, void *ptr) +{ + int i; + struct msm_sdw_priv *msm_sdw = container_of(nb, + struct msm_sdw_priv, + service_nb); + bool adsp_ready = false; + unsigned long timeout; + static bool initial_boot = true; + + pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode); + + mutex_lock(&msm_sdw->codec_mutex); + switch (opcode) { + case AUDIO_NOTIFIER_SERVICE_DOWN: + if (initial_boot) { + initial_boot = false; + break; + } + msm_sdw->int_mclk1_enabled = false; + msm_sdw->dev_up = false; + for (i = 0; i < msm_sdw->nr; i++) + swrm_wcd_notify(msm_sdw->sdw_ctrl_data[i].sdw_pdev, + SWR_DEVICE_DOWN, NULL); + break; + case AUDIO_NOTIFIER_SERVICE_UP: + if (initial_boot) + initial_boot = false; + if (!q6core_is_adsp_ready()) { + dev_dbg(msm_sdw->dev, "ADSP isn't ready\n"); + timeout = jiffies + + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS); + while (!time_after(jiffies, timeout)) { + if (!q6core_is_adsp_ready()) { + dev_dbg(msm_sdw->dev, + "ADSP isn't ready\n"); + } else { + dev_dbg(msm_sdw->dev, + "ADSP is ready\n"); + adsp_ready = true; + goto powerup; + } + } + } else { + adsp_ready = true; + dev_dbg(msm_sdw->dev, "%s: DSP is ready\n", __func__); + } +powerup: + if (adsp_ready) { + msm_sdw->dev_up = true; + msm_sdw_init_reg(msm_sdw->codec); + regcache_mark_dirty(msm_sdw->regmap); + regcache_sync(msm_sdw->regmap); + msm_sdw_set_spkr_mode(msm_sdw->codec, + msm_sdw->spkr_mode); + } + break; + default: + break; + } + mutex_unlock(&msm_sdw->codec_mutex); + return NOTIFY_OK; +} + +static int msm_sdw_codec_probe(struct snd_soc_codec *codec) +{ + struct msm_sdw_priv *msm_sdw; + int i, ret; + + msm_sdw = snd_soc_codec_get_drvdata(codec); + if (!msm_sdw) { + pr_err("%s:SDW priv data null\n", __func__); + return -EINVAL; + } + msm_sdw->codec = codec; + for (i = 0; i < COMP_MAX; i++) + msm_sdw->comp_enabled[i] = 0; + + msm_sdw->spkr_gain_offset = RX_GAIN_OFFSET_0_DB; + msm_sdw_init_reg(codec); + msm_sdw->version = MSM_SDW_VERSION_1_0; + + msm_sdw->service_nb.notifier_call = msm_sdw_notifier_service_cb; + ret = audio_notifier_register("msm_sdw", + AUDIO_NOTIFIER_ADSP_DOMAIN, + &msm_sdw->service_nb); + if (ret < 0) + dev_err(msm_sdw->dev, + "%s: Audio notifier register failed ret = %d\n", + __func__, ret); + return 0; +} + +static int msm_sdw_codec_remove(struct snd_soc_codec *codec) +{ + return 0; +} + +static struct regmap *msm_sdw_get_regmap(struct device *dev) +{ + struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev); + + return msm_sdw->regmap; +} + +static struct snd_soc_codec_driver soc_codec_dev_msm_sdw = { + .probe = msm_sdw_codec_probe, + .remove = msm_sdw_codec_remove, + .get_regmap = msm_sdw_get_regmap, + .component_driver = { + .controls = msm_sdw_snd_controls, + .num_controls = ARRAY_SIZE(msm_sdw_snd_controls), + .dapm_widgets = msm_sdw_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm_sdw_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + }, +}; + +static void msm_sdw_add_child_devices(struct work_struct *work) +{ + struct msm_sdw_priv *msm_sdw; + struct platform_device *pdev; + struct device_node *node; + struct msm_sdw_ctrl_data *sdw_ctrl_data = NULL, *temp; + int ret, ctrl_num = 0; + struct wcd_sdw_ctrl_platform_data *platdata; + char plat_dev_name[MSM_SDW_STRING_LEN]; + + msm_sdw = container_of(work, struct msm_sdw_priv, + msm_sdw_add_child_devices_work); + if (!msm_sdw) { + pr_err("%s: Memory for msm_sdw does not exist\n", + __func__); + return; + } + if (!msm_sdw->dev->of_node) { + dev_err(msm_sdw->dev, + "%s: DT node for msm_sdw does not exist\n", __func__); + return; + } + + platdata = &msm_sdw->sdw_plat_data; + + for_each_available_child_of_node(msm_sdw->dev->of_node, node) { + if (!strcmp(node->name, "swr_master")) + strlcpy(plat_dev_name, "msm_sdw_swr_ctrl", + (MSM_SDW_STRING_LEN - 1)); + else if (strnstr(node->name, "msm_cdc_pinctrl", + strlen("msm_cdc_pinctrl")) != NULL) + strlcpy(plat_dev_name, node->name, + (MSM_SDW_STRING_LEN - 1)); + else + continue; + + pdev = platform_device_alloc(plat_dev_name, -1); + if (!pdev) { + dev_err(msm_sdw->dev, "%s: pdev memory alloc failed\n", + __func__); + ret = -ENOMEM; + goto err; + } + pdev->dev.parent = msm_sdw->dev; + pdev->dev.of_node = node; + + if (!strcmp(node->name, "swr_master")) { + ret = platform_device_add_data(pdev, platdata, + sizeof(*platdata)); + if (ret) { + dev_err(&pdev->dev, + "%s: cannot add plat data ctrl:%d\n", + __func__, ctrl_num); + goto fail_pdev_add; + } + } + + ret = platform_device_add(pdev); + if (ret) { + dev_err(&pdev->dev, + "%s: Cannot add platform device\n", + __func__); + goto fail_pdev_add; + } + + if (!strcmp(node->name, "swr_master")) { + temp = krealloc(sdw_ctrl_data, + (ctrl_num + 1) * sizeof( + struct msm_sdw_ctrl_data), + GFP_KERNEL); + if (!temp) { + dev_err(&pdev->dev, "out of memory\n"); + ret = -ENOMEM; + goto err; + } + sdw_ctrl_data = temp; + sdw_ctrl_data[ctrl_num].sdw_pdev = pdev; + ctrl_num++; + dev_dbg(&pdev->dev, + "%s: Added soundwire ctrl device(s)\n", + __func__); + msm_sdw->nr = ctrl_num; + msm_sdw->sdw_ctrl_data = sdw_ctrl_data; + } + } + + return; +fail_pdev_add: + platform_device_put(pdev); +err: + return; +} + +static int msm_sdw_probe(struct platform_device *pdev) +{ + int ret = 0; + struct msm_sdw_priv *msm_sdw; + int adsp_state; + + adsp_state = apr_get_subsys_state(); + if (adsp_state != APR_SUBSYS_LOADED) { + dev_err(&pdev->dev, "Adsp is not loaded yet %d\n", + adsp_state); + return -EPROBE_DEFER; + } + + msm_sdw = devm_kzalloc(&pdev->dev, sizeof(struct msm_sdw_priv), + GFP_KERNEL); + if (!msm_sdw) + return -ENOMEM; + dev_set_drvdata(&pdev->dev, msm_sdw); + msm_sdw->dev_up = true; + + msm_sdw->dev = &pdev->dev; + INIT_WORK(&msm_sdw->msm_sdw_add_child_devices_work, + msm_sdw_add_child_devices); + msm_sdw->sdw_plat_data.handle = (void *) msm_sdw; + msm_sdw->sdw_plat_data.read = msm_sdw_swrm_read; + msm_sdw->sdw_plat_data.write = msm_sdw_swrm_write; + msm_sdw->sdw_plat_data.bulk_write = msm_sdw_swrm_bulk_write; + msm_sdw->sdw_plat_data.clk = msm_sdw_swrm_clock; + msm_sdw->sdw_plat_data.handle_irq = msm_sdwm_handle_irq; + ret = of_property_read_u32(pdev->dev.of_node, "reg", + &msm_sdw->sdw_base_addr); + if (ret) { + dev_err(&pdev->dev, "%s: could not find %s entry in dt\n", + __func__, "reg"); + goto err_sdw_cdc; + } + + msm_sdw->sdw_gpio_p = of_parse_phandle(pdev->dev.of_node, + "qcom,cdc-sdw-gpios", 0); + msm_sdw->sdw_base = ioremap(msm_sdw->sdw_base_addr, + MSM_SDW_MAX_REGISTER); + msm_sdw->read_dev = __msm_sdw_reg_read; + msm_sdw->write_dev = __msm_sdw_reg_write; + + msm_sdw->regmap = msm_sdw_regmap_init(msm_sdw->dev, + &msm_sdw_regmap_config); + msm_sdw->sdw_irq = platform_get_irq_byname(pdev, "swr_master_irq"); + if (msm_sdw->sdw_irq < 0) { + dev_err(msm_sdw->dev, "%s() error getting irq handle: %d\n", + __func__, msm_sdw->sdw_irq); + ret = -ENODEV; + goto err_sdw_cdc; + } + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_msm_sdw, + msm_sdw_dai, ARRAY_SIZE(msm_sdw_dai)); + if (ret) { + dev_err(&pdev->dev, "%s: Codec registration failed, ret = %d\n", + __func__, ret); + goto err_sdw_cdc; + } + /* initialize the int_mclk1 */ + msm_sdw->sdw_cdc_core_clk.clk_set_minor_version = + AFE_API_VERSION_I2S_CONFIG; + msm_sdw->sdw_cdc_core_clk.clk_id = + Q6AFE_LPASS_CLK_ID_INT_MCLK_1; + msm_sdw->sdw_cdc_core_clk.clk_freq_in_hz = + INT_MCLK1_FREQ; + msm_sdw->sdw_cdc_core_clk.clk_attri = + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; + msm_sdw->sdw_cdc_core_clk.clk_root = + Q6AFE_LPASS_CLK_ROOT_DEFAULT; + msm_sdw->sdw_cdc_core_clk.enable = 0; + + /* initialize the sdw_npl_clk */ + msm_sdw->sdw_npl_clk.clk_set_minor_version = + AFE_API_VERSION_I2S_CONFIG; + msm_sdw->sdw_npl_clk.clk_id = + AFE_CLOCK_SET_CLOCK_ID_SWR_NPL_CLK; + msm_sdw->sdw_npl_clk.clk_freq_in_hz = SDW_NPL_FREQ; + msm_sdw->sdw_npl_clk.clk_attri = + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO; + msm_sdw->sdw_npl_clk.clk_root = + Q6AFE_LPASS_CLK_ROOT_DEFAULT; + msm_sdw->sdw_npl_clk.enable = 0; + + INIT_DELAYED_WORK(&msm_sdw->disable_int_mclk1_work, + msm_disable_int_mclk1); + mutex_init(&msm_sdw->cdc_int_mclk1_mutex); + mutex_init(&msm_sdw->sdw_npl_clk_mutex); + mutex_init(&msm_sdw->io_lock); + mutex_init(&msm_sdw->sdw_read_lock); + mutex_init(&msm_sdw->sdw_write_lock); + mutex_init(&msm_sdw->sdw_clk_lock); + mutex_init(&msm_sdw->codec_mutex); + schedule_work(&msm_sdw->msm_sdw_add_child_devices_work); + + dev_dbg(&pdev->dev, "%s: msm_sdw driver probe done\n", __func__); + return ret; + +err_sdw_cdc: + devm_kfree(&pdev->dev, msm_sdw); + return ret; +} + +static int msm_sdw_remove(struct platform_device *pdev) +{ + struct msm_sdw_priv *msm_sdw; + + msm_sdw = dev_get_drvdata(&pdev->dev); + + mutex_destroy(&msm_sdw->io_lock); + mutex_destroy(&msm_sdw->sdw_read_lock); + mutex_destroy(&msm_sdw->sdw_write_lock); + mutex_destroy(&msm_sdw->sdw_clk_lock); + mutex_destroy(&msm_sdw->codec_mutex); + mutex_destroy(&msm_sdw->cdc_int_mclk1_mutex); + devm_kfree(&pdev->dev, msm_sdw); + snd_soc_unregister_codec(&pdev->dev); + return 0; +} + +static const struct of_device_id msm_sdw_codec_dt_match[] = { + { .compatible = "qcom,msm-sdw-codec", }, + {} +}; + +static struct platform_driver msm_sdw_codec_driver = { + .probe = msm_sdw_probe, + .remove = msm_sdw_remove, + .driver = { + .name = "msm_sdw_codec", + .owner = THIS_MODULE, + .of_match_table = msm_sdw_codec_dt_match, + }, +}; +module_platform_driver(msm_sdw_codec_driver); + +MODULE_DESCRIPTION("MSM Soundwire Codec driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_cdc_utils.c b/sound/soc/codecs/msm_sdw/msm_sdw_cdc_utils.c new file mode 100644 index 000000000000..9a5c85bf0c6c --- /dev/null +++ b/sound/soc/codecs/msm_sdw/msm_sdw_cdc_utils.c @@ -0,0 +1,211 @@ +/* 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 "msm_sdw.h" + +#define REG_BYTES 2 +#define VAL_BYTES 1 +/* + * Page Register Address that APP Proc uses to + * access WCD9335 Codec registers is identified + * as 0x00 + */ +#define PAGE_REG_ADDR 0x00 + +/* + * msm_sdw_page_write: + * Retrieve page number from register and + * write that page number to the page address. + * Called under io_lock acquisition. + * + * @msm_sdw: pointer to msm_sdw + * @reg: Register address from which page number is retrieved + * + * Returns 0 for success and negative error code for failure. + */ +int msm_sdw_page_write(struct msm_sdw_priv *msm_sdw, unsigned short reg) +{ + int ret = 0; + u8 pg_num, prev_pg_num; + + pg_num = msm_sdw_page_map[reg]; + if (msm_sdw->prev_pg_valid) { + prev_pg_num = msm_sdw->prev_pg; + if (prev_pg_num != pg_num) { + ret = msm_sdw->write_dev(msm_sdw, PAGE_REG_ADDR, 1, + (void *) &pg_num); + if (ret < 0) { + dev_err(msm_sdw->dev, + "page write error, pg_num: 0x%x\n", + pg_num); + } else { + msm_sdw->prev_pg = pg_num; + dev_dbg(msm_sdw->dev, + "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + } else { + ret = msm_sdw->write_dev(msm_sdw, PAGE_REG_ADDR, 1, + (void *) &pg_num); + if (ret < 0) { + dev_err(msm_sdw->dev, + "page write error, pg_num: 0x%x\n", pg_num); + } else { + msm_sdw->prev_pg = pg_num; + msm_sdw->prev_pg_valid = true; + dev_dbg(msm_sdw->dev, "%s: Page 0x%x Write to 0x00\n", + __func__, pg_num); + } + } + return ret; +} +EXPORT_SYMBOL(msm_sdw_page_write); + +static int regmap_bus_read(void *context, const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev); + unsigned short c_reg; + int ret, i; + + if (!msm_sdw) { + dev_err(dev, "%s: msm_sdw is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + if (!msm_sdw->dev_up) { + dev_dbg_ratelimited(dev, "%s: No read allowed. dev_up = %d\n", + __func__, msm_sdw->dev_up); + return 0; + } + + mutex_lock(&msm_sdw->io_lock); + c_reg = *(u16 *)reg; + ret = msm_sdw_page_write(msm_sdw, c_reg); + if (ret) + goto err; + ret = msm_sdw->read_dev(msm_sdw, c_reg, val_size, val); + if (ret < 0) + dev_err(dev, "%s: Codec read failed (%d), reg: 0x%x, size:%zd\n", + __func__, ret, c_reg, val_size); + else { + for (i = 0; i < val_size; i++) + dev_dbg(dev, "%s: Read 0x%02x from 0x%x\n", + __func__, ((u8 *)val)[i], c_reg + i); + } +err: + mutex_unlock(&msm_sdw->io_lock); + + return ret; +} + +static int regmap_bus_gather_write(void *context, + const void *reg, size_t reg_size, + const void *val, size_t val_size) +{ + struct device *dev = context; + struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev); + unsigned short c_reg; + int ret, i; + + if (!msm_sdw) { + dev_err(dev, "%s: msm_sdw is NULL\n", __func__); + return -EINVAL; + } + if (!reg || !val) { + dev_err(dev, "%s: reg or val is NULL\n", __func__); + return -EINVAL; + } + if (reg_size != REG_BYTES) { + dev_err(dev, "%s: register size %zd bytes, not supported\n", + __func__, reg_size); + return -EINVAL; + } + if (!msm_sdw->dev_up) { + dev_dbg_ratelimited(dev, "%s: No write allowed. dev_up = %d\n", + __func__, msm_sdw->dev_up); + return 0; + } + + mutex_lock(&msm_sdw->io_lock); + c_reg = *(u16 *)reg; + ret = msm_sdw_page_write(msm_sdw, c_reg); + if (ret) + goto err; + + for (i = 0; i < val_size; i++) + dev_dbg(dev, "Write %02x to 0x%x\n", ((u8 *)val)[i], + c_reg + i*4); + + ret = msm_sdw->write_dev(msm_sdw, c_reg, val_size, (void *) val); + if (ret < 0) + dev_err(dev, + "%s: Codec write failed (%d), reg:0x%x, size:%zd\n", + __func__, ret, c_reg, val_size); + +err: + mutex_unlock(&msm_sdw->io_lock); + return ret; +} + +static int regmap_bus_write(void *context, const void *data, size_t count) +{ + struct device *dev = context; + struct msm_sdw_priv *msm_sdw = dev_get_drvdata(dev); + + if (!msm_sdw) + return -EINVAL; + + WARN_ON(count < REG_BYTES); + + return regmap_bus_gather_write(context, data, REG_BYTES, + data + REG_BYTES, + count - REG_BYTES); + +} + +static struct regmap_bus regmap_bus_config = { + .write = regmap_bus_write, + .gather_write = regmap_bus_gather_write, + .read = regmap_bus_read, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +/* + * msm_sdw_regmap_init: + * Initialize msm_sdw register map + * + * @dev: pointer to wcd device + * @config: pointer to register map config + * + * Returns pointer to regmap structure for success + * or NULL in case of failure. + */ +struct regmap *msm_sdw_regmap_init(struct device *dev, + const struct regmap_config *config) +{ + return devm_regmap_init(dev, ®map_bus_config, dev, config); +} +EXPORT_SYMBOL(msm_sdw_regmap_init); diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_registers.h b/sound/soc/codecs/msm_sdw/msm_sdw_registers.h new file mode 100644 index 000000000000..1b7b0b01a6b2 --- /dev/null +++ b/sound/soc/codecs/msm_sdw/msm_sdw_registers.h @@ -0,0 +1,126 @@ +/* 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 MSM_SDW_REGISTERS_H +#define MSM_SDW_REGISTERS_H + +#define MSM_SDW_PAGE_REGISTER 0x0000 + +/* Page-A Registers */ +#define MSM_SDW_TX9_SPKR_PROT_PATH_CTL 0x0308 +#define MSM_SDW_TX9_SPKR_PROT_PATH_CFG0 0x030c +#define MSM_SDW_TX10_SPKR_PROT_PATH_CTL 0x0318 +#define MSM_SDW_TX10_SPKR_PROT_PATH_CFG0 0x031c +#define MSM_SDW_TX11_SPKR_PROT_PATH_CTL 0x0328 +#define MSM_SDW_TX11_SPKR_PROT_PATH_CFG0 0x032c +#define MSM_SDW_TX12_SPKR_PROT_PATH_CTL 0x0338 +#define MSM_SDW_TX12_SPKR_PROT_PATH_CFG0 0x033c + +/* Page-B Registers */ +#define MSM_SDW_COMPANDER7_CTL0 0x0024 +#define MSM_SDW_COMPANDER7_CTL1 0x0028 +#define MSM_SDW_COMPANDER7_CTL2 0x002c +#define MSM_SDW_COMPANDER7_CTL3 0x0030 +#define MSM_SDW_COMPANDER7_CTL4 0x0034 +#define MSM_SDW_COMPANDER7_CTL5 0x0038 +#define MSM_SDW_COMPANDER7_CTL6 0x003c +#define MSM_SDW_COMPANDER7_CTL7 0x0040 +#define MSM_SDW_COMPANDER8_CTL0 0x0044 +#define MSM_SDW_COMPANDER8_CTL1 0x0048 +#define MSM_SDW_COMPANDER8_CTL2 0x004c +#define MSM_SDW_COMPANDER8_CTL3 0x0050 +#define MSM_SDW_COMPANDER8_CTL4 0x0054 +#define MSM_SDW_COMPANDER8_CTL5 0x0058 +#define MSM_SDW_COMPANDER8_CTL6 0x005c +#define MSM_SDW_COMPANDER8_CTL7 0x0060 +#define MSM_SDW_RX7_RX_PATH_CTL 0x01a4 +#define MSM_SDW_RX7_RX_PATH_CFG0 0x01a8 +#define MSM_SDW_RX7_RX_PATH_CFG1 0x01ac +#define MSM_SDW_RX7_RX_PATH_CFG2 0x01b0 +#define MSM_SDW_RX7_RX_VOL_CTL 0x01b4 +#define MSM_SDW_RX7_RX_PATH_MIX_CTL 0x01b8 +#define MSM_SDW_RX7_RX_PATH_MIX_CFG 0x01bc +#define MSM_SDW_RX7_RX_VOL_MIX_CTL 0x01c0 +#define MSM_SDW_RX7_RX_PATH_SEC0 0x01c4 +#define MSM_SDW_RX7_RX_PATH_SEC1 0x01c8 +#define MSM_SDW_RX7_RX_PATH_SEC2 0x01cc +#define MSM_SDW_RX7_RX_PATH_SEC3 0x01d0 +#define MSM_SDW_RX7_RX_PATH_SEC5 0x01d8 +#define MSM_SDW_RX7_RX_PATH_SEC6 0x01dc +#define MSM_SDW_RX7_RX_PATH_SEC7 0x01e0 +#define MSM_SDW_RX7_RX_PATH_MIX_SEC0 0x01e4 +#define MSM_SDW_RX7_RX_PATH_MIX_SEC1 0x01e8 +#define MSM_SDW_RX8_RX_PATH_CTL 0x0384 +#define MSM_SDW_RX8_RX_PATH_CFG0 0x0388 +#define MSM_SDW_RX8_RX_PATH_CFG1 0x038c +#define MSM_SDW_RX8_RX_PATH_CFG2 0x0390 +#define MSM_SDW_RX8_RX_VOL_CTL 0x0394 +#define MSM_SDW_RX8_RX_PATH_MIX_CTL 0x0398 +#define MSM_SDW_RX8_RX_PATH_MIX_CFG 0x039c +#define MSM_SDW_RX8_RX_VOL_MIX_CTL 0x03a0 +#define MSM_SDW_RX8_RX_PATH_SEC0 0x03a4 +#define MSM_SDW_RX8_RX_PATH_SEC1 0x03a8 +#define MSM_SDW_RX8_RX_PATH_SEC2 0x03ac +#define MSM_SDW_RX8_RX_PATH_SEC3 0x03b0 +#define MSM_SDW_RX8_RX_PATH_SEC5 0x03b8 +#define MSM_SDW_RX8_RX_PATH_SEC6 0x03bc +#define MSM_SDW_RX8_RX_PATH_SEC7 0x03c0 +#define MSM_SDW_RX8_RX_PATH_MIX_SEC0 0x03c4 +#define MSM_SDW_RX8_RX_PATH_MIX_SEC1 0x03c8 + +/* Page-C Registers */ +#define MSM_SDW_BOOST0_BOOST_PATH_CTL 0x0064 +#define MSM_SDW_BOOST0_BOOST_CTL 0x0068 +#define MSM_SDW_BOOST0_BOOST_CFG1 0x006c +#define MSM_SDW_BOOST0_BOOST_CFG2 0x0070 +#define MSM_SDW_BOOST1_BOOST_PATH_CTL 0x0084 +#define MSM_SDW_BOOST1_BOOST_CTL 0x0088 +#define MSM_SDW_BOOST1_BOOST_CFG1 0x008c +#define MSM_SDW_BOOST1_BOOST_CFG2 0x0090 +#define MSM_SDW_AHB_BRIDGE_WR_DATA_0 0x00a4 +#define MSM_SDW_AHB_BRIDGE_WR_DATA_1 0x00a8 +#define MSM_SDW_AHB_BRIDGE_WR_DATA_2 0x00ac +#define MSM_SDW_AHB_BRIDGE_WR_DATA_3 0x00b0 +#define MSM_SDW_AHB_BRIDGE_WR_ADDR_0 0x00b4 +#define MSM_SDW_AHB_BRIDGE_WR_ADDR_1 0x00b8 +#define MSM_SDW_AHB_BRIDGE_WR_ADDR_2 0x00bc +#define MSM_SDW_AHB_BRIDGE_WR_ADDR_3 0x00c0 +#define MSM_SDW_AHB_BRIDGE_RD_ADDR_0 0x00c4 +#define MSM_SDW_AHB_BRIDGE_RD_ADDR_1 0x00c8 +#define MSM_SDW_AHB_BRIDGE_RD_ADDR_2 0x00cc +#define MSM_SDW_AHB_BRIDGE_RD_ADDR_3 0x00d0 +#define MSM_SDW_AHB_BRIDGE_RD_DATA_0 0x00d4 +#define MSM_SDW_AHB_BRIDGE_RD_DATA_1 0x00d8 +#define MSM_SDW_AHB_BRIDGE_RD_DATA_2 0x00dc +#define MSM_SDW_AHB_BRIDGE_RD_DATA_3 0x00e0 +#define MSM_SDW_AHB_BRIDGE_ACCESS_CFG 0x00e4 +#define MSM_SDW_AHB_BRIDGE_ACCESS_STATUS 0x00e8 + +/* Page-D Registers */ +#define MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL 0x0104 +#define MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL 0x0108 +#define MSM_SDW_CLK_RST_CTRL_SWR_CONTROL 0x010c +#define MSM_SDW_TOP_TOP_CFG0 0x0204 +#define MSM_SDW_TOP_TOP_CFG1 0x0208 +#define MSM_SDW_TOP_RX_I2S_CTL 0x020c +#define MSM_SDW_TOP_TX_I2S_CTL 0x0210 +#define MSM_SDW_TOP_I2S_CLK 0x0214 +#define MSM_SDW_TOP_RX7_PATH_INPUT0_MUX 0x0218 +#define MSM_SDW_TOP_RX7_PATH_INPUT1_MUX 0x021c +#define MSM_SDW_TOP_RX8_PATH_INPUT0_MUX 0x0220 +#define MSM_SDW_TOP_RX8_PATH_INPUT1_MUX 0x0224 +#define MSM_SDW_TOP_FREQ_MCLK 0x0228 +#define MSM_SDW_TOP_DEBUG_BUS_SEL 0x022c +#define MSM_SDW_TOP_DEBUG_EN 0x0230 +#define MSM_SDW_TOP_I2S_RESET 0x0234 +#define MSM_SDW_TOP_BLOCKS_RESET 0x0238 + +#endif diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_regmap.c b/sound/soc/codecs/msm_sdw/msm_sdw_regmap.c new file mode 100644 index 000000000000..22663384ec35 --- /dev/null +++ b/sound/soc/codecs/msm_sdw/msm_sdw_regmap.c @@ -0,0 +1,161 @@ +/* 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 "msm_sdw.h" + +static const struct reg_default msm_sdw_defaults[] = { + /* Page #10 registers */ + { MSM_SDW_PAGE_REGISTER, 0x00 }, + { MSM_SDW_TX9_SPKR_PROT_PATH_CTL, 0x02 }, + { MSM_SDW_TX9_SPKR_PROT_PATH_CFG0, 0x00 }, + { MSM_SDW_TX10_SPKR_PROT_PATH_CTL, 0x02 }, + { MSM_SDW_TX10_SPKR_PROT_PATH_CFG0, 0x00 }, + { MSM_SDW_TX11_SPKR_PROT_PATH_CTL, 0x02 }, + { MSM_SDW_TX11_SPKR_PROT_PATH_CFG0, 0x00 }, + { MSM_SDW_TX12_SPKR_PROT_PATH_CTL, 0x02 }, + { MSM_SDW_TX12_SPKR_PROT_PATH_CFG0, 0x00 }, + /* Page #11 registers */ + { MSM_SDW_COMPANDER7_CTL0, 0x60 }, + { MSM_SDW_COMPANDER7_CTL1, 0xdb }, + { MSM_SDW_COMPANDER7_CTL2, 0xff }, + { MSM_SDW_COMPANDER7_CTL3, 0x35 }, + { MSM_SDW_COMPANDER7_CTL4, 0xff }, + { MSM_SDW_COMPANDER7_CTL5, 0x00 }, + { MSM_SDW_COMPANDER7_CTL6, 0x01 }, + { MSM_SDW_COMPANDER8_CTL0, 0x60 }, + { MSM_SDW_COMPANDER8_CTL1, 0xdb }, + { MSM_SDW_COMPANDER8_CTL2, 0xff }, + { MSM_SDW_COMPANDER8_CTL3, 0x35 }, + { MSM_SDW_COMPANDER8_CTL4, 0xff }, + { MSM_SDW_COMPANDER8_CTL5, 0x00 }, + { MSM_SDW_COMPANDER8_CTL6, 0x01 }, + { MSM_SDW_RX7_RX_PATH_CTL, 0x04 }, + { MSM_SDW_RX7_RX_PATH_CFG0, 0x00 }, + { MSM_SDW_RX7_RX_PATH_CFG2, 0x8f }, + { MSM_SDW_RX7_RX_VOL_CTL, 0x00 }, + { MSM_SDW_RX7_RX_PATH_MIX_CTL, 0x04 }, + { MSM_SDW_RX7_RX_VOL_MIX_CTL, 0x00 }, + { MSM_SDW_RX7_RX_PATH_SEC2, 0x00 }, + { MSM_SDW_RX7_RX_PATH_SEC3, 0x00 }, + { MSM_SDW_RX7_RX_PATH_SEC5, 0x00 }, + { MSM_SDW_RX7_RX_PATH_SEC6, 0x00 }, + { MSM_SDW_RX7_RX_PATH_SEC7, 0x00 }, + { MSM_SDW_RX7_RX_PATH_MIX_SEC1, 0x00 }, + { MSM_SDW_RX8_RX_PATH_CTL, 0x04 }, + { MSM_SDW_RX8_RX_PATH_CFG0, 0x00 }, + { MSM_SDW_RX8_RX_PATH_CFG2, 0x8f }, + { MSM_SDW_RX8_RX_VOL_CTL, 0x00 }, + { MSM_SDW_RX8_RX_PATH_MIX_CTL, 0x04 }, + { MSM_SDW_RX8_RX_VOL_MIX_CTL, 0x00 }, + { MSM_SDW_RX8_RX_PATH_SEC2, 0x00 }, + { MSM_SDW_RX8_RX_PATH_SEC3, 0x00 }, + { MSM_SDW_RX8_RX_PATH_SEC5, 0x00 }, + { MSM_SDW_RX8_RX_PATH_SEC6, 0x00 }, + { MSM_SDW_RX8_RX_PATH_SEC7, 0x00 }, + { MSM_SDW_RX8_RX_PATH_MIX_SEC1, 0x00 }, + /* Page #12 registers */ + { MSM_SDW_BOOST0_BOOST_PATH_CTL, 0x00 }, + { MSM_SDW_BOOST0_BOOST_CTL, 0xb2 }, + { MSM_SDW_BOOST0_BOOST_CFG1, 0x00 }, + { MSM_SDW_BOOST0_BOOST_CFG2, 0x00 }, + { MSM_SDW_BOOST1_BOOST_PATH_CTL, 0x00 }, + { MSM_SDW_BOOST1_BOOST_CTL, 0xb2 }, + { MSM_SDW_BOOST1_BOOST_CFG1, 0x00 }, + { MSM_SDW_BOOST1_BOOST_CFG2, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_DATA_0, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_DATA_1, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_DATA_2, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_DATA_3, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_ADDR_0, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_ADDR_1, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_ADDR_2, 0x00 }, + { MSM_SDW_AHB_BRIDGE_WR_ADDR_3, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_ADDR_0, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_ADDR_1, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_ADDR_2, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_ADDR_3, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_DATA_0, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_DATA_1, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_DATA_2, 0x00 }, + { MSM_SDW_AHB_BRIDGE_RD_DATA_3, 0x00 }, + { MSM_SDW_AHB_BRIDGE_ACCESS_CFG, 0x0f }, + { MSM_SDW_AHB_BRIDGE_ACCESS_STATUS, 0x03 }, + /* Page #13 registers */ + { MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL, 0x00 }, + { MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL, 0x00 }, + { MSM_SDW_CLK_RST_CTRL_SWR_CONTROL, 0x00 }, + { MSM_SDW_TOP_TOP_CFG0, 0x00 }, + { MSM_SDW_TOP_TOP_CFG1, 0x00 }, + { MSM_SDW_TOP_RX_I2S_CTL, 0x0C }, + { MSM_SDW_TOP_TX_I2S_CTL, 0x00 }, + { MSM_SDW_TOP_I2S_CLK, 0x00 }, + { MSM_SDW_TOP_RX7_PATH_INPUT0_MUX, 0x00 }, + { MSM_SDW_TOP_RX7_PATH_INPUT1_MUX, 0x00 }, + { MSM_SDW_TOP_RX8_PATH_INPUT0_MUX, 0x00 }, + { MSM_SDW_TOP_RX8_PATH_INPUT1_MUX, 0x00 }, + { MSM_SDW_TOP_FREQ_MCLK, 0x00 }, + { MSM_SDW_TOP_DEBUG_BUS_SEL, 0x00 }, + { MSM_SDW_TOP_DEBUG_EN, 0x00 }, + { MSM_SDW_TOP_I2S_RESET, 0x00 }, + { MSM_SDW_TOP_BLOCKS_RESET, 0x00 }, +}; + +static bool msm_sdw_is_readable_register(struct device *dev, unsigned int reg) +{ + return msm_sdw_reg_readable[reg]; +} + +static bool msm_sdw_is_writeable_register(struct device *dev, unsigned int reg) +{ + return msm_sdw_reg_writeable[reg]; +} + +static bool msm_sdw_is_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MSM_SDW_AHB_BRIDGE_WR_DATA_0: + case MSM_SDW_AHB_BRIDGE_WR_DATA_1: + case MSM_SDW_AHB_BRIDGE_WR_DATA_2: + case MSM_SDW_AHB_BRIDGE_WR_DATA_3: + case MSM_SDW_AHB_BRIDGE_WR_ADDR_0: + case MSM_SDW_AHB_BRIDGE_WR_ADDR_1: + case MSM_SDW_AHB_BRIDGE_WR_ADDR_2: + case MSM_SDW_AHB_BRIDGE_WR_ADDR_3: + case MSM_SDW_AHB_BRIDGE_RD_DATA_0: + case MSM_SDW_AHB_BRIDGE_RD_DATA_1: + case MSM_SDW_AHB_BRIDGE_RD_DATA_2: + case MSM_SDW_AHB_BRIDGE_RD_DATA_3: + case MSM_SDW_AHB_BRIDGE_RD_ADDR_0: + case MSM_SDW_AHB_BRIDGE_RD_ADDR_1: + case MSM_SDW_AHB_BRIDGE_RD_ADDR_2: + case MSM_SDW_AHB_BRIDGE_RD_ADDR_3: + case MSM_SDW_CLK_RST_CTRL_MCLK_CONTROL: + case MSM_SDW_CLK_RST_CTRL_FS_CNT_CONTROL: + return true; + default: + return false; + } +} + +const struct regmap_config msm_sdw_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .reg_stride = 4, + .cache_type = REGCACHE_RBTREE, + .reg_defaults = msm_sdw_defaults, + .num_reg_defaults = ARRAY_SIZE(msm_sdw_defaults), + .max_register = MSM_SDW_MAX_REGISTER, + .writeable_reg = msm_sdw_is_writeable_register, + .volatile_reg = msm_sdw_is_volatile_register, + .readable_reg = msm_sdw_is_readable_register, +}; From 84f7f7314410065b7fb0cd4100e1073b6e2ca1a0 Mon Sep 17 00:00:00 2001 From: Asish Bhattacharya Date: Tue, 25 Jul 2017 16:29:27 +0530 Subject: [PATCH 005/276] audio-lnx: Add latest snapshot for audio drivers. Propagate the changes based on latest snapshot for audio kernel source tree. Change-Id: I15cdbf2651ee8bf70a56b08013e1fbce16859d9b Signed-off-by: Asish Bhattacharya --- drivers/mfd/wcd9xxx-core.c | 49 ++- drivers/pinctrl/qcom/pinctrl-lpi.c | 3 +- include/linux/mfd/wcd9xxx/core.h | 2 +- include/uapi/sound/devdep_params.h | 10 + sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c | 24 +- sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c | 16 +- sound/soc/codecs/wcd-dsp-mgr.c | 3 +- sound/soc/codecs/wcd-mbhc-v2.c | 44 ++- sound/soc/codecs/wcd-mbhc-v2.h | 18 +- sound/soc/codecs/wcd9335.c | 89 ++++-- sound/soc/codecs/wcd934x/wcd934x-mbhc.c | 56 +++- sound/soc/codecs/wcd934x/wcd934x-routing.h | 64 ++++ sound/soc/codecs/wcd934x/wcd934x.c | 187 ++++++++++++ sound/soc/codecs/wcd934x/wcd934x.h | 1 + sound/soc/codecs/wcd_cpe_services.c | 289 +----------------- sound/soc/msm/qdsp6v2/audio_calibration.c | 14 +- sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c | 150 ++++++++- sound/soc/msm/sdm660-common.c | 39 +-- sound/soc/msm/sdm660-ext-dai-links.c | 176 +++++------ sound/soc/msm/sdm660-external.c | 55 ++-- sound/soc/msm/sdm660-internal.c | 176 +++++------ sound/soc/msm/sdm845.c | 160 +++++++++- 22 files changed, 1041 insertions(+), 584 deletions(-) diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c index b373acb11005..232c290bfb55 100644 --- a/drivers/mfd/wcd9xxx-core.c +++ b/drivers/mfd/wcd9xxx-core.c @@ -228,7 +228,7 @@ static int wcd9xxx_slim_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg, if (!wcd9xxx->dev_up) { dev_dbg_ratelimited( - wcd9xxx->dev, "%s: No read allowed. dev_up = %d\n", + wcd9xxx->dev, "%s: No read allowed. dev_up = %lu\n", __func__, wcd9xxx->dev_up); return 0; } @@ -268,7 +268,7 @@ static int wcd9xxx_slim_write_device(struct wcd9xxx *wcd9xxx, if (!wcd9xxx->dev_up) { dev_dbg_ratelimited( - wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + wcd9xxx->dev, "%s: No write allowed. dev_up = %lu\n", __func__, wcd9xxx->dev_up); return 0; } @@ -345,7 +345,7 @@ int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg, if (!wcd9xxx->dev_up) { dev_dbg_ratelimited( - wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + wcd9xxx->dev, "%s: No write allowed. dev_up = %lu\n", __func__, wcd9xxx->dev_up); ret = 0; goto done; @@ -426,7 +426,7 @@ int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx, if (!wcd9xxx->dev_up) { dev_dbg_ratelimited( - wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", + wcd9xxx->dev, "%s: No write allowed. dev_up = %lu\n", __func__, wcd9xxx->dev_up); return 0; } @@ -1479,12 +1479,27 @@ static int wcd9xxx_slim_device_reset(struct slim_device *sldev) return -EINVAL; } - dev_info(wcd9xxx->dev, "%s: device reset, dev_up = %d\n", - __func__, wcd9xxx->dev_up); - if (wcd9xxx->dev_up) - return 0; + /* + * Wait for 500 ms for device down to complete. Observed delay + * of ~200ms for device down to complete after being called, + * due to context switch issue. + */ + ret = wait_on_bit_timeout(&wcd9xxx->dev_up, 0, + TASK_INTERRUPTIBLE, + msecs_to_jiffies(500)); + if (ret) + pr_err("%s: slim device down not complete in 500 msec\n", + __func__); mutex_lock(&wcd9xxx->reset_lock); + + dev_info(wcd9xxx->dev, "%s: device reset, dev_up = %lu\n", + __func__, wcd9xxx->dev_up); + if (wcd9xxx->dev_up) { + mutex_unlock(&wcd9xxx->reset_lock); + return 0; + } + ret = wcd9xxx_reset(wcd9xxx->dev); if (ret) dev_err(wcd9xxx->dev, "%s: Resetting Codec failed\n", __func__); @@ -1502,8 +1517,8 @@ static int wcd9xxx_slim_device_up(struct slim_device *sldev) pr_err("%s: wcd9xxx is NULL\n", __func__); return -EINVAL; } - dev_info(wcd9xxx->dev, "%s: slim device up, dev_up = %d\n", - __func__, wcd9xxx->dev_up); + dev_info(wcd9xxx->dev, "%s: slim device up, dev_up = %lu\n", + __func__, wcd9xxx->dev_up); if (wcd9xxx->dev_up) return 0; @@ -1525,18 +1540,20 @@ static int wcd9xxx_slim_device_down(struct slim_device *sldev) return -EINVAL; } - dev_info(wcd9xxx->dev, "%s: device down, dev_up = %d\n", - __func__, wcd9xxx->dev_up); - if (!wcd9xxx->dev_up) - return 0; + mutex_lock(&wcd9xxx->reset_lock); - wcd9xxx->dev_up = false; + dev_info(wcd9xxx->dev, "%s: device down, dev_up = %lu\n", + __func__, wcd9xxx->dev_up); + if (!wcd9xxx->dev_up) { + mutex_unlock(&wcd9xxx->reset_lock); + return 0; + } - mutex_lock(&wcd9xxx->reset_lock); if (wcd9xxx->dev_down) wcd9xxx->dev_down(wcd9xxx); wcd9xxx_irq_exit(&wcd9xxx->core_res); wcd9xxx_reset_low(wcd9xxx->dev); + wcd9xxx->dev_up = false; mutex_unlock(&wcd9xxx->reset_lock); return 0; diff --git a/drivers/pinctrl/qcom/pinctrl-lpi.c b/drivers/pinctrl/qcom/pinctrl-lpi.c index fedd5f0aee22..11f954e1eb6c 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpi.c +++ b/drivers/pinctrl/qcom/pinctrl-lpi.c @@ -448,6 +448,7 @@ static void lpi_gpio_dbg_show_one(struct seq_file *s, unsigned int offset, unsigned int gpio) { + struct lpi_gpio_state *state = gpiochip_get_data(chip); struct pinctrl_pin_desc pindesc; struct lpi_gpio_pad *pad; unsigned int func; @@ -463,7 +464,7 @@ static void lpi_gpio_dbg_show_one(struct seq_file *s, "pull up" }; - pctldev = pctldev ? : to_gpio_state(chip)->ctrl; + pctldev = pctldev ? : state->ctrl; pindesc = pctldev->desc->pins[offset]; pad = pctldev->desc->pins[offset].drv_data; ctl_reg = lpi_gpio_read(pad, LPI_GPIO_REG_DIR_CTL); diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h index b4c1be40ff31..b994010f5222 100644 --- a/include/linux/mfd/wcd9xxx/core.h +++ b/include/linux/mfd/wcd9xxx/core.h @@ -350,7 +350,7 @@ struct wcd9xxx { int (*post_reset)(struct wcd9xxx *wcd9xxx); void *ssr_priv; - bool dev_up; + unsigned long dev_up; u32 num_of_supplies; struct regulator_bulk_data *supplies; diff --git a/include/uapi/sound/devdep_params.h b/include/uapi/sound/devdep_params.h index 6697eca288fe..66a9cacfcab9 100644 --- a/include/uapi/sound/devdep_params.h +++ b/include/uapi/sound/devdep_params.h @@ -53,4 +53,14 @@ struct dts_eagle_param_desc { uint32_t device; } __packed; +#define HWDEP_FE_BASE 3000 /*unique base for FE hw dep nodes*/ +struct snd_pcm_mmap_fd { + int32_t dir; + int32_t fd; + int32_t size; + int32_t actual_size; +}; + +#define SNDRV_PCM_IOCTL_MMAP_DATA_FD _IOWR('U', 0xd2, struct snd_pcm_mmap_fd) + #endif diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c index a8fcd347b38b..f126d35dee81 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -2055,6 +2055,9 @@ static const char * const rdac2_mux_text[] = { "ZERO", "RX2", "RX1" }; +static const struct snd_kcontrol_new adc1_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + static const struct soc_enum rdac2_mux_enum = SOC_ENUM_SINGLE(MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL, 0, 3, rdac2_mux_text); @@ -3105,7 +3108,8 @@ static const struct snd_soc_dapm_route audio_map[] = { {"ADC2 MUX", "INP2", "ADC2_INP2"}, {"ADC2 MUX", "INP3", "ADC2_INP3"}, - {"ADC1", NULL, "AMIC1"}, + {"ADC1", NULL, "ADC1_INP1"}, + {"ADC1_INP1", "Switch", "AMIC1"}, {"ADC2_INP2", NULL, "AMIC2"}, {"ADC2_INP3", NULL, "AMIC3"}, @@ -3446,6 +3450,8 @@ static const struct snd_soc_dapm_widget msm_anlg_cdc_dapm_widgets[] = { SND_SOC_DAPM_SPK("Ext Spk", msm_anlg_cdc_codec_enable_spk_ext_pa), + SND_SOC_DAPM_SWITCH("ADC1_INP1", SND_SOC_NOPM, 0, 0, + &adc1_switch), SND_SOC_DAPM_SUPPLY("RX1 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("RX2 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, @@ -4052,7 +4058,7 @@ int msm_anlg_codec_info_create_codec_entry(struct snd_info_entry *codec_root, sdm660_cdc_priv = snd_soc_codec_get_drvdata(codec); card = codec->component.card; - sdm660_cdc_priv->entry = snd_register_module_info(codec_root->module, + sdm660_cdc_priv->entry = snd_info_create_subdir(codec_root->module, "spmi0-03", codec_root); if (!sdm660_cdc_priv->entry) { @@ -4320,13 +4326,15 @@ static struct snd_soc_codec_driver soc_codec_dev_sdm660_cdc = { .suspend = msm_anlg_cdc_suspend, .resume = msm_anlg_cdc_resume, .reg_word_size = 1, - .controls = msm_anlg_cdc_snd_controls, - .num_controls = ARRAY_SIZE(msm_anlg_cdc_snd_controls), - .dapm_widgets = msm_anlg_cdc_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(msm_anlg_cdc_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), .get_regmap = msm_anlg_get_regmap, + .component_driver = { + .controls = msm_anlg_cdc_snd_controls, + .num_controls = ARRAY_SIZE(msm_anlg_cdc_snd_controls), + .dapm_widgets = msm_anlg_cdc_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm_anlg_cdc_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + }, }; static int msm_anlg_cdc_init_supplies(struct sdm660_cdc_priv *sdm660_cdc, diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c index 3f9c0b4a5b83..68a1d8d47b39 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -1157,7 +1157,7 @@ int msm_dig_codec_info_create_codec_entry(struct snd_info_entry *codec_root, msm_dig = snd_soc_codec_get_drvdata(codec); card = codec->component.card; - msm_dig->entry = snd_register_module_info(codec_root->module, + msm_dig->entry = snd_info_create_subdir(codec_root->module, "msm_digital_codec", codec_root); if (!msm_dig->entry) { @@ -2037,13 +2037,15 @@ static struct snd_soc_codec_driver soc_msm_dig_codec = { .remove = msm_dig_cdc_soc_remove, .suspend = msm_dig_cdc_suspend, .resume = msm_dig_cdc_resume, - .controls = msm_dig_snd_controls, - .num_controls = ARRAY_SIZE(msm_dig_snd_controls), - .dapm_widgets = msm_dig_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(msm_dig_dapm_widgets), - .dapm_routes = audio_dig_map, - .num_dapm_routes = ARRAY_SIZE(audio_dig_map), .get_regmap = msm_digital_get_regmap, + .component_driver = { + .controls = msm_dig_snd_controls, + .num_controls = ARRAY_SIZE(msm_dig_snd_controls), + .dapm_widgets = msm_dig_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(msm_dig_dapm_widgets), + .dapm_routes = audio_dig_map, + .num_dapm_routes = ARRAY_SIZE(audio_dig_map), + }, }; const struct regmap_config msm_digital_regmap_config = { diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c index 661db2b66324..a6d46aef9724 100644 --- a/sound/soc/codecs/wcd-dsp-mgr.c +++ b/sound/soc/codecs/wcd-dsp-mgr.c @@ -26,7 +26,8 @@ static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type); /* Component related macros */ -#define WDSP_GET_COMPONENT(wdsp, x) (&(wdsp->cmpnts[x])) +#define WDSP_GET_COMPONENT(wdsp, x) ((x >= WDSP_CMPNT_TYPE_MAX || x < 0) ? \ + NULL : (&(wdsp->cmpnts[x]))) #define WDSP_GET_CMPNT_TYPE_STR(x) wdsp_get_cmpnt_type_string(x) /* diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c index 3b2426dc7234..eb67de92871d 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/sound/soc/codecs/wcd-mbhc-v2.c @@ -329,6 +329,7 @@ static int wcd_event_notify(struct notifier_block *self, unsigned long val, /* Disable micbias, pullup & enable cs */ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); mutex_unlock(&mbhc->hphl_pa_lock); + clear_bit(WCD_MBHC_ANC0_OFF_ACK, &mbhc->hph_anc_state); break; case WCD_EVENT_PRE_HPHR_PA_OFF: mutex_lock(&mbhc->hphr_pa_lock); @@ -346,6 +347,7 @@ static int wcd_event_notify(struct notifier_block *self, unsigned long val, /* Disable micbias, pullup & enable cs */ wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS); mutex_unlock(&mbhc->hphr_pa_lock); + clear_bit(WCD_MBHC_ANC1_OFF_ACK, &mbhc->hph_anc_state); break; case WCD_EVENT_PRE_HPHL_PA_ON: set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state); @@ -439,6 +441,25 @@ static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc) __func__); usleep_range(wg_time * 1000, wg_time * 1000 + 50); } + + if (test_and_clear_bit(WCD_MBHC_ANC0_OFF_ACK, + &mbhc->hph_anc_state)) { + usleep_range(20000, 20100); + pr_debug("%s: HPHL ANC clear flag and enable ANC_EN\n", + __func__); + if (mbhc->mbhc_cb->update_anc_state) + mbhc->mbhc_cb->update_anc_state(mbhc->codec, true, 0); + } + + if (test_and_clear_bit(WCD_MBHC_ANC1_OFF_ACK, + &mbhc->hph_anc_state)) { + usleep_range(20000, 20100); + pr_debug("%s: HPHR ANC clear flag and enable ANC_EN\n", + __func__); + if (mbhc->mbhc_cb->update_anc_state) + mbhc->mbhc_cb->update_anc_state(mbhc->codec, true, 1); + } + } static bool wcd_mbhc_is_hph_pa_on(struct wcd_mbhc *mbhc) @@ -471,6 +492,20 @@ static void wcd_mbhc_set_and_turnoff_hph_padac(struct wcd_mbhc *mbhc) } WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 0); usleep_range(wg_time * 1000, wg_time * 1000 + 50); + + + if (mbhc->mbhc_cb->is_anc_on && mbhc->mbhc_cb->is_anc_on(mbhc)) { + usleep_range(20000, 20100); + pr_debug("%s ANC is on, setting ANC_OFF_ACK\n", __func__); + set_bit(WCD_MBHC_ANC0_OFF_ACK, &mbhc->hph_anc_state); + set_bit(WCD_MBHC_ANC1_OFF_ACK, &mbhc->hph_anc_state); + if (mbhc->mbhc_cb->update_anc_state) { + mbhc->mbhc_cb->update_anc_state(mbhc->codec, false, 0); + mbhc->mbhc_cb->update_anc_state(mbhc->codec, false, 1); + } else { + pr_debug("%s ANC is off\n", __func__); + } + } } int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl, @@ -581,7 +616,8 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, jack_type == SND_JACK_LINEOUT) && (mbhc->hph_status && mbhc->hph_status != jack_type)) { - if (mbhc->micbias_enable) { + if (mbhc->micbias_enable && + mbhc->hph_status == SND_JACK_HEADSET) { if (mbhc->mbhc_cb->mbhc_micbias_control) mbhc->mbhc_cb->mbhc_micbias_control( codec, MIC_BIAS_2, @@ -1108,7 +1144,7 @@ static irqreturn_t wcd_mbhc_release_handler(int irq, void *data) * For ADC MBHC, ADC_COMPLETE interrupt will be generated * in this case. So skip the check here. */ - if (!WCD_MBHC_DETECTION && + if (mbhc->mbhc_detection_logic == WCD_DETECTION_LEGACY && mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) { wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET); goto exit; @@ -1922,7 +1958,7 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, init_waitqueue_head(&mbhc->wait_btn_press); mutex_init(&mbhc->codec_resource_lock); - switch (WCD_MBHC_DETECTION) { + switch (mbhc->mbhc_detection_logic) { case WCD_DETECTION_LEGACY: wcd_mbhc_legacy_init(mbhc); break; @@ -1931,7 +1967,7 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, break; default: pr_err("%s: Unknown detection logic type %d\n", - __func__, WCD_MBHC_DETECTION); + __func__, mbhc->mbhc_detection_logic); break; } diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h index 7ed06c355990..c8714fc3abbc 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.h +++ b/sound/soc/codecs/wcd-mbhc-v2.h @@ -157,12 +157,6 @@ enum wcd_mbhc_detect_logic { WCD_DETECTION_ADC, }; -#ifdef CONFIG_SND_SOC_WCD_MBHC_ADC -#define WCD_MBHC_DETECTION WCD_DETECTION_ADC -#else -#define WCD_MBHC_DETECTION WCD_DETECTION_LEGACY -#endif - enum wcd_mbhc_cs_mb_en_flag { WCD_MBHC_EN_CS = 0, WCD_MBHC_EN_MB, @@ -240,6 +234,11 @@ enum pa_dac_ack_flags { WCD_MBHC_HPHR_PA_OFF_ACK, }; +enum anc_ack_flags { + WCD_MBHC_ANC0_OFF_ACK = 0, + WCD_MBHC_ANC1_OFF_ACK, +}; + enum wcd_mbhc_btn_det_mem { WCD_MBHC_BTN_DET_V_BTN_LOW, WCD_MBHC_BTN_DET_V_BTN_HIGH @@ -488,6 +487,9 @@ struct wcd_mbhc_cb { void (*hph_pull_down_ctrl)(struct snd_soc_codec *, bool); void (*mbhc_moisture_config)(struct wcd_mbhc *); bool (*hph_register_recovery)(struct wcd_mbhc *); + void (*update_anc_state)(struct snd_soc_codec *codec, + bool enable, int anc_num); + bool (*is_anc_on)(struct wcd_mbhc *mbhc); }; struct wcd_mbhc_fn { @@ -538,6 +540,7 @@ struct wcd_mbhc { /* track PA/DAC state to sync with userspace */ unsigned long hph_pa_dac_state; + unsigned long hph_anc_state; unsigned long event_state; unsigned long jiffies_atreport; @@ -565,6 +568,9 @@ struct wcd_mbhc { struct mutex hphl_pa_lock; struct mutex hphr_pa_lock; + /* Holds mbhc detection method - ADC/Legacy */ + unsigned int mbhc_detection_logic; + unsigned long intr_status; bool is_hph_ocp_pending; diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c index 329aa7a4c466..dedeaea55675 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/sound/soc/codecs/wcd9335.c @@ -348,7 +348,6 @@ enum { AUDIO_NOMINAL, CPE_NOMINAL, HPH_PA_DELAY, - SB_CLK_GEAR, ANC_MIC_AMIC1, ANC_MIC_AMIC2, ANC_MIC_AMIC3, @@ -818,7 +817,10 @@ struct tasha_priv { int rx_8_count; bool clk_mode; bool clk_internal; - + /* Lock to prevent multiple functions voting at same time */ + struct mutex sb_clk_gear_lock; + /* Count for functions voting or un-voting */ + u32 ref_count; /* Lock to protect mclk enablement */ struct mutex mclk_lock; }; @@ -2012,6 +2014,32 @@ static void tasha_mbhc_moisture_config(struct wcd_mbhc *mbhc) tasha_mbhc_hph_l_pull_up_control(codec, mbhc->moist_iref); } +static void tasha_update_anc_state(struct snd_soc_codec *codec, bool enable, + int anc_num) +{ + if (enable) + snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CFG0 + + (20 * anc_num), 0x10, 0x10); + else + snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CFG0 + + (20 * anc_num), 0x10, 0x00); +} + +static bool tasha_is_anc_on(struct wcd_mbhc *mbhc) +{ + bool anc_on = false; + u16 ancl, ancr; + + ancl = + (snd_soc_read(mbhc->codec, WCD9335_CDC_RX1_RX_PATH_CFG0)) & 0x10; + ancr = + (snd_soc_read(mbhc->codec, WCD9335_CDC_RX2_RX_PATH_CFG0)) & 0x10; + + anc_on = !!(ancl | ancr); + + return anc_on; +} + static const struct wcd_mbhc_cb mbhc_cb = { .request_irq = tasha_mbhc_request_irq, .irq_control = tasha_mbhc_irq_control, @@ -2034,6 +2062,8 @@ static const struct wcd_mbhc_cb mbhc_cb = { .mbhc_gnd_det_ctrl = tasha_mbhc_gnd_det_ctrl, .hph_pull_down_ctrl = tasha_mbhc_hph_pull_down_ctrl, .mbhc_moisture_config = tasha_mbhc_moisture_config, + .update_anc_state = tasha_update_anc_state, + .is_anc_on = tasha_is_anc_on, }; static int tasha_get_anc_slot(struct snd_kcontrol *kcontrol, @@ -2948,10 +2978,7 @@ static int tasha_codec_enable_slimrx(struct snd_soc_dapm_widget *w, &dai->grph); break; case SND_SOC_DAPM_PRE_PMD: - if (!test_bit(SB_CLK_GEAR, &tasha_p->status_mask)) { - tasha_codec_vote_max_bw(codec, true); - set_bit(SB_CLK_GEAR, &tasha_p->status_mask); - } + tasha_codec_vote_max_bw(codec, true); break; case SND_SOC_DAPM_POST_PMD: ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list, @@ -5263,10 +5290,7 @@ static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: - if (!test_bit(SB_CLK_GEAR, &tasha->status_mask)) { - tasha_codec_vote_max_bw(codec, true); - set_bit(SB_CLK_GEAR, &tasha->status_mask); - } + tasha_codec_vote_max_bw(codec, true); /* Reset if needed */ tasha_codec_enable_prim_interpolator(codec, reg, event); break; @@ -11127,11 +11151,8 @@ static void tasha_shutdown(struct snd_pcm_substream *substream, if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) return; - if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && - test_bit(SB_CLK_GEAR, &tasha->status_mask)) { + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) tasha_codec_vote_max_bw(dai->codec, false); - clear_bit(SB_CLK_GEAR, &tasha->status_mask); - } } static int tasha_set_decimator_rate(struct snd_soc_dai *dai, @@ -11366,15 +11387,11 @@ static int tasha_set_interpolator_rate(struct snd_soc_dai *dai, static int tasha_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec); - pr_debug("%s(): substream = %s stream = %d\n", __func__, substream->name, substream->stream); - if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && - test_bit(SB_CLK_GEAR, &tasha->status_mask)) { + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) tasha_codec_vote_max_bw(dai->codec, false); - clear_bit(SB_CLK_GEAR, &tasha->status_mask); - } return 0; } @@ -13082,13 +13099,29 @@ static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec, if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) return 0; - if (vote) - bw_ops = SLIM_BW_CLK_GEAR_9; - else - bw_ops = SLIM_BW_UNVOTE; + mutex_lock(&tasha->sb_clk_gear_lock); + if (vote) { + tasha->ref_count++; + if (tasha->ref_count == 1) { + bw_ops = SLIM_BW_CLK_GEAR_9; + tasha_codec_slim_reserve_bw(codec, + bw_ops, true); + } + } else if (!vote && tasha->ref_count > 0) { + tasha->ref_count--; + if (tasha->ref_count == 0) { + bw_ops = SLIM_BW_UNVOTE; + tasha_codec_slim_reserve_bw(codec, + bw_ops, true); + } + }; - return tasha_codec_slim_reserve_bw(codec, - bw_ops, true); + dev_dbg(codec->dev, "%s Value of counter after vote or un-vote is %d\n", + __func__, tasha->ref_count); + + mutex_unlock(&tasha->sb_clk_gear_lock); + + return 0; } static int tasha_cpe_err_irq_control(struct snd_soc_codec *codec, @@ -13271,6 +13304,8 @@ static int tasha_post_reset_cb(struct wcd9xxx *wcd9xxx) if (ret < 0) dev_err(codec->dev, "%s: invalid pdata\n", __func__); + /* Reset reference counter for voting for max bw */ + tasha->ref_count = 0; /* MBHC Init */ wcd_mbhc_deinit(&tasha->mbhc); tasha->mbhc_started = false; @@ -14053,6 +14088,7 @@ static int tasha_probe(struct platform_device *pdev) mutex_init(&tasha->swr_read_lock); mutex_init(&tasha->swr_write_lock); mutex_init(&tasha->swr_clk_lock); + mutex_init(&tasha->sb_clk_gear_lock); mutex_init(&tasha->mclk_lock); cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region), @@ -14157,6 +14193,7 @@ static int tasha_remove(struct platform_device *pdev) mutex_destroy(&tasha->mclk_lock); devm_kfree(&pdev->dev, tasha); snd_soc_unregister_codec(&pdev->dev); + mutex_destroy(&tasha->sb_clk_gear_lock); return 0; } diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.c b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c index a1a5e2d65062..ea19caa14f8e 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-mbhc.c +++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c @@ -829,6 +829,32 @@ static bool tavil_hph_register_recovery(struct wcd_mbhc *mbhc) return wcd934x_mbhc->is_hph_recover; } +static void tavil_update_anc_state(struct snd_soc_codec *codec, bool enable, + int anc_num) +{ + if (enable) + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CFG0 + + (20 * anc_num), 0x10, 0x10); + else + snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CFG0 + + (20 * anc_num), 0x10, 0x00); +} + +static bool tavil_is_anc_on(struct wcd_mbhc *mbhc) +{ + bool anc_on = false; + u16 ancl, ancr; + + ancl = + (snd_soc_read(mbhc->codec, WCD934X_CDC_RX1_RX_PATH_CFG0)) & 0x10; + ancr = + (snd_soc_read(mbhc->codec, WCD934X_CDC_RX2_RX_PATH_CFG0)) & 0x10; + + anc_on = !!(ancl | ancr); + + return anc_on; +} + static const struct wcd_mbhc_cb mbhc_cb = { .request_irq = tavil_mbhc_request_irq, .irq_control = tavil_mbhc_irq_control, @@ -852,6 +878,8 @@ static const struct wcd_mbhc_cb mbhc_cb = { .hph_pull_down_ctrl = tavil_mbhc_hph_pull_down_ctrl, .mbhc_moisture_config = tavil_mbhc_moisture_config, .hph_register_recovery = tavil_hph_register_recovery, + .update_anc_state = tavil_update_anc_state, + .is_anc_on = tavil_is_anc_on, }; static struct regulator *tavil_codec_find_ondemand_regulator( @@ -998,19 +1026,26 @@ int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, struct snd_soc_codec *codec) { int ret; + struct wcd_mbhc *wcd_mbhc; if (!mbhc || !codec) return -EINVAL; - wcd_mbhc_deinit(&mbhc->wcd_mbhc); - ret = wcd_mbhc_init(&mbhc->wcd_mbhc, codec, &mbhc_cb, &intr_ids, + wcd_mbhc = &mbhc->wcd_mbhc; + if (wcd_mbhc == NULL) { + pr_err("%s: wcd_mbhc is NULL\n", __func__); + return -EINVAL; + } + + wcd_mbhc_deinit(wcd_mbhc); + ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb, &intr_ids, wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED); if (ret) { dev_err(codec->dev, "%s: mbhc initialization failed\n", __func__); goto done; } - if (!WCD_MBHC_DETECTION) { + if (wcd_mbhc->mbhc_detection_logic == WCD_DETECTION_LEGACY) { snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04); snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01); } @@ -1033,6 +1068,7 @@ int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, struct snd_soc_codec *codec, { struct regulator *supply; struct wcd934x_mbhc *wcd934x_mbhc; + struct wcd_mbhc *wcd_mbhc; int ret; wcd934x_mbhc = devm_kzalloc(codec->dev, sizeof(struct wcd934x_mbhc), @@ -1043,8 +1079,18 @@ int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, struct snd_soc_codec *codec, wcd934x_mbhc->wcd9xxx = dev_get_drvdata(codec->dev->parent); wcd934x_mbhc->fw_data = fw_data; BLOCKING_INIT_NOTIFIER_HEAD(&wcd934x_mbhc->notifier); + wcd_mbhc = &wcd934x_mbhc->wcd_mbhc; + if (wcd_mbhc == NULL) { + pr_err("%s: wcd_mbhc is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + + + /* Setting default mbhc detection logic to ADC for Tavil */ + wcd_mbhc->mbhc_detection_logic = WCD_DETECTION_ADC; - ret = wcd_mbhc_init(&wcd934x_mbhc->wcd_mbhc, codec, &mbhc_cb, + ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb, &intr_ids, wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED); if (ret) { @@ -1070,7 +1116,7 @@ int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, struct snd_soc_codec *codec, snd_soc_add_codec_controls(codec, hph_type_detect_controls, ARRAY_SIZE(hph_type_detect_controls)); - if (!WCD_MBHC_DETECTION) { + if (wcd_mbhc->mbhc_detection_logic == WCD_DETECTION_LEGACY) { snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04); snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01); } diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h index afd93b2cf56d..93a1ad3bab90 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-routing.h +++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h @@ -117,6 +117,70 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { const struct snd_soc_dapm_route tavil_audio_map[] = { + /* WDMA3 */ + {"WDMA3 PORT0 MUX", "DEC0", "ADC MUX0"}, + {"WDMA3 PORT0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, + {"WDMA3 PORT1 MUX", "DEC1", "ADC MUX1"}, + {"WDMA3 PORT1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"}, + {"WDMA3 PORT2 MUX", "DEC2", "ADC MUX2"}, + {"WDMA3 PORT2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"}, + {"WDMA3 PORT3 MUX", "DEC3", "ADC MUX3"}, + {"WDMA3 PORT3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"}, + {"WDMA3 PORT4 MUX", "DEC4", "ADC MUX4"}, + {"WDMA3 PORT4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"}, + {"WDMA3 PORT5 MUX", "DEC5", "ADC MUX5"}, + {"WDMA3 PORT5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"}, + {"WDMA3 PORT6 MUX", "DEC6", "ADC MUX6"}, + {"WDMA3 PORT6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"}, + + {"WDMA3 CH0 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH0 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH0 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH0 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH0 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH0 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH0 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH0 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH0 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3 CH1 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH1 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH1 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH1 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH1 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH1 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH1 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH1 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH1 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3 CH2 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH2 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH2 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH2 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH2 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH2 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH2 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH2 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH2 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3 CH3 MUX", "PORT_0", "WDMA3 PORT0 MUX"}, + {"WDMA3 CH3 MUX", "PORT_1", "WDMA3 PORT1 MUX"}, + {"WDMA3 CH3 MUX", "PORT_2", "WDMA3 PORT2 MUX"}, + {"WDMA3 CH3 MUX", "PORT_3", "WDMA3 PORT3 MUX"}, + {"WDMA3 CH3 MUX", "PORT_4", "WDMA3 PORT4 MUX"}, + {"WDMA3 CH3 MUX", "PORT_5", "WDMA3 PORT5 MUX"}, + {"WDMA3 CH3 MUX", "PORT_6", "WDMA3 PORT6 MUX"}, + {"WDMA3 CH3 MUX", "PORT_7", "ADC MUX7"}, + {"WDMA3 CH3 MUX", "PORT_8", "ADC MUX8"}, + + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH0 MUX"}, + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH1 MUX"}, + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH2 MUX"}, + {"WDMA3_CH_MIXER", NULL, "WDMA3 CH3 MUX"}, + + {"WDMA3_ON_OFF", "Switch", "WDMA3_CH_MIXER"}, + {"WDMA3_OUT", NULL, "WDMA3_ON_OFF"}, + /* MAD */ {"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"}, {"MAD_SEL MUX", "MSM", "MADINPUT"}, diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c index ca16ed8c8ae3..3079cca65e38 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/sound/soc/codecs/wcd934x/wcd934x.c @@ -180,6 +180,8 @@ enum { ANC_MIC_AMIC2, ANC_MIC_AMIC3, ANC_MIC_AMIC4, + CLK_INTERNAL, + CLK_MODE, }; enum { @@ -1071,6 +1073,40 @@ static int tavil_codec_enable_anc(struct snd_soc_dapm_widget *w, return ret; } +static int tavil_get_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + if (test_bit(CLK_MODE, &tavil_p->status_mask)) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + + dev_dbg(codec->dev, "%s: is_low_power_clock: %s\n", __func__, + test_bit(CLK_MODE, &tavil_p->status_mask) ? "true" : "false"); + + return 0; +} + +static int tavil_put_clkmode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + if (ucontrol->value.enumerated.item[0]) + set_bit(CLK_MODE, &tavil_p->status_mask); + else + clear_bit(CLK_MODE, &tavil_p->status_mask); + + dev_dbg(codec->dev, "%s: is_low_power_clock: %s\n", __func__, + test_bit(CLK_MODE, &tavil_p->status_mask) ? "true" : "false"); + + return 0; +} + static int tavil_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2809,6 +2845,35 @@ static int tavil_get_asrc_mode(struct tavil_priv *tavil, int asrc, return asrc_mode; } +static int tavil_codec_wdma3_ctl(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Fix to 16KHz */ + snd_soc_update_bits(codec, WCD934X_DMA_WDMA_CTL_3, + 0xF0, 0x10); + /* Select mclk_1 */ + snd_soc_update_bits(codec, WCD934X_DMA_WDMA_CTL_3, + 0x02, 0x00); + /* Enable DMA */ + snd_soc_update_bits(codec, WCD934X_DMA_WDMA_CTL_3, + 0x01, 0x01); + break; + + case SND_SOC_DAPM_POST_PMD: + /* Disable DMA */ + snd_soc_update_bits(codec, WCD934X_DMA_WDMA_CTL_3, + 0x01, 0x00); + break; + + }; + + return 0; +} + static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, int asrc_in, int event) { @@ -5547,6 +5612,9 @@ static const char *const tavil_anc_func_text[] = {"OFF", "ON"}; static const struct soc_enum tavil_anc_func_enum = SOC_ENUM_SINGLE_EXT(2, tavil_anc_func_text); +static const char *const tavil_clkmode_text[] = {"EXTERNAL", "INTERNAL"}; +static SOC_ENUM_SINGLE_EXT_DECL(tavil_clkmode_enum, tavil_clkmode_text); + /* Cutoff frequency for high pass filter */ static const char * const cf_text[] = { "CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ" @@ -5726,6 +5794,9 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = { SOC_ENUM_EXT("ANC Function", tavil_anc_func_enum, tavil_get_anc_func, tavil_put_anc_func), + SOC_ENUM_EXT("CLK MODE", tavil_clkmode_enum, tavil_get_clkmode, + tavil_put_clkmode), + SOC_ENUM("TX0 HPF cut off", cf_dec0_enum), SOC_ENUM("TX1 HPF cut off", cf_dec1_enum), SOC_ENUM("TX2 HPF cut off", cf_dec2_enum), @@ -6165,6 +6236,39 @@ static const char * const native_mux_text[] = { "OFF", "ON", }; +static const char *const wdma3_port0_text[] = { + "RX_MIX_TX0", "DEC0" +}; + +static const char *const wdma3_port1_text[] = { + "RX_MIX_TX1", "DEC1" +}; + +static const char *const wdma3_port2_text[] = { + "RX_MIX_TX2", "DEC2" +}; + +static const char *const wdma3_port3_text[] = { + "RX_MIX_TX3", "DEC3" +}; + +static const char *const wdma3_port4_text[] = { + "RX_MIX_TX4", "DEC4" +}; + +static const char *const wdma3_port5_text[] = { + "RX_MIX_TX5", "DEC5" +}; + +static const char *const wdma3_port6_text[] = { + "RX_MIX_TX6", "DEC6" +}; + +static const char *const wdma3_ch_text[] = { + "PORT_0", "PORT_1", "PORT_2", "PORT_3", "PORT_4", + "PORT_5", "PORT_6", "PORT_7", "PORT_8", +}; + static const struct snd_kcontrol_new aif4_vi_mixer[] = { SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, WCD934X_TX14, 1, 0, tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put), @@ -6570,6 +6674,20 @@ WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text); WCD_DAPM_ENUM(anc0_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text); WCD_DAPM_ENUM(anc1_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 3, anc1_fb_mux_text); + +WCD_DAPM_ENUM(wdma3_port0, WCD934X_DMA_WDMA3_PRT_CFG, 0, wdma3_port0_text); +WCD_DAPM_ENUM(wdma3_port1, WCD934X_DMA_WDMA3_PRT_CFG, 1, wdma3_port1_text); +WCD_DAPM_ENUM(wdma3_port2, WCD934X_DMA_WDMA3_PRT_CFG, 2, wdma3_port2_text); +WCD_DAPM_ENUM(wdma3_port3, WCD934X_DMA_WDMA3_PRT_CFG, 3, wdma3_port3_text); +WCD_DAPM_ENUM(wdma3_port4, WCD934X_DMA_WDMA3_PRT_CFG, 4, wdma3_port4_text); +WCD_DAPM_ENUM(wdma3_port5, WCD934X_DMA_WDMA3_PRT_CFG, 5, wdma3_port5_text); +WCD_DAPM_ENUM(wdma3_port6, WCD934X_DMA_WDMA3_PRT_CFG, 6, wdma3_port6_text); + +WCD_DAPM_ENUM(wdma3_ch0, WCD934X_DMA_CH_0_1_CFG_WDMA_3, 0, wdma3_ch_text); +WCD_DAPM_ENUM(wdma3_ch1, WCD934X_DMA_CH_0_1_CFG_WDMA_3, 4, wdma3_ch_text); +WCD_DAPM_ENUM(wdma3_ch2, WCD934X_DMA_CH_2_3_CFG_WDMA_3, 0, wdma3_ch_text); +WCD_DAPM_ENUM(wdma3_ch3, WCD934X_DMA_CH_2_3_CFG_WDMA_3, 4, wdma3_ch_text); + static const struct snd_kcontrol_new anc_ear_switch = SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); @@ -6637,6 +6755,9 @@ static const struct snd_kcontrol_new rx_int4_asrc_switch[] = { SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0), }; +static const struct snd_kcontrol_new wdma3_onoff_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); + static int tavil_dsd_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -7319,6 +7440,28 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { SND_SOC_DAPM_MUX_E("ASRC3 MUX", SND_SOC_NOPM, ASRC3, 0, &asrc3_mux, tavil_codec_enable_asrc_resampler, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* WDMA3 widgets */ + WCD_DAPM_MUX("WDMA3 PORT0 MUX", 0, wdma3_port0), + WCD_DAPM_MUX("WDMA3 PORT1 MUX", 1, wdma3_port1), + WCD_DAPM_MUX("WDMA3 PORT2 MUX", 2, wdma3_port2), + WCD_DAPM_MUX("WDMA3 PORT3 MUX", 3, wdma3_port3), + WCD_DAPM_MUX("WDMA3 PORT4 MUX", 4, wdma3_port4), + WCD_DAPM_MUX("WDMA3 PORT5 MUX", 5, wdma3_port5), + WCD_DAPM_MUX("WDMA3 PORT6 MUX", 6, wdma3_port6), + + WCD_DAPM_MUX("WDMA3 CH0 MUX", 0, wdma3_ch0), + WCD_DAPM_MUX("WDMA3 CH1 MUX", 4, wdma3_ch1), + WCD_DAPM_MUX("WDMA3 CH2 MUX", 0, wdma3_ch2), + WCD_DAPM_MUX("WDMA3 CH3 MUX", 4, wdma3_ch3), + + SND_SOC_DAPM_MIXER("WDMA3_CH_MIXER", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH_E("WDMA3_ON_OFF", SND_SOC_NOPM, 0, 0, + &wdma3_onoff_switch, tavil_codec_wdma3_ctl, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_OUTPUT("WDMA3_OUT"), }; static int tavil_get_channel_map(struct snd_soc_dai *dai, @@ -8316,6 +8459,50 @@ static int tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec, return ret; } +/* + * tavil_cdc_mclk_tx_enable: Enable/Disable codec's clock for TX path + * @codec: Handle to codec + * @enable: Indicates whether clock should be enabled or disabled + */ +int tavil_cdc_mclk_tx_enable(struct snd_soc_codec *codec, bool enable) +{ + struct tavil_priv *tavil_p; + int ret = 0; + bool clk_mode; + bool clk_internal; + + if (!codec) + return -EINVAL; + + tavil_p = snd_soc_codec_get_drvdata(codec); + clk_mode = test_bit(CLK_MODE, &tavil_p->status_mask); + clk_internal = test_bit(CLK_INTERNAL, &tavil_p->status_mask); + + dev_dbg(codec->dev, "%s: clkmode: %d, enable: %d, clk_internal: %d\n", + __func__, clk_mode, enable, clk_internal); + + if (clk_mode || clk_internal) { + if (enable) { + wcd_resmgr_enable_master_bias(tavil_p->resmgr); + tavil_dig_core_power_collapse(tavil_p, POWER_RESUME); + tavil_vote_svs(tavil_p, true); + ret = tavil_codec_internal_rco_ctrl(codec, enable); + set_bit(CLK_INTERNAL, &tavil_p->status_mask); + } else { + clear_bit(CLK_INTERNAL, &tavil_p->status_mask); + tavil_codec_internal_rco_ctrl(codec, enable); + tavil_vote_svs(tavil_p, false); + tavil_dig_core_power_collapse(tavil_p, POWER_COLLAPSE); + wcd_resmgr_disable_master_bias(tavil_p->resmgr); + } + } else { + ret = __tavil_cdc_mclk_enable(tavil_p, enable); + } + + return ret; +} +EXPORT_SYMBOL(tavil_cdc_mclk_tx_enable); + static const struct wcd_resmgr_cb tavil_resmgr_cb = { .cdc_rco_ctrl = __tavil_codec_internal_rco_ctrl, }; diff --git a/sound/soc/codecs/wcd934x/wcd934x.h b/sound/soc/codecs/wcd934x/wcd934x.h index c3bf50a4ffdb..27c21f103533 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.h +++ b/sound/soc/codecs/wcd934x/wcd934x.h @@ -137,6 +137,7 @@ struct tavil_reg_mask_val { extern void *tavil_get_afe_config(struct snd_soc_codec *codec, enum afe_config_type config_type); extern int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable); +extern int tavil_cdc_mclk_tx_enable(struct snd_soc_codec *codec, bool enable); extern int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode); extern int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset); extern struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev); diff --git a/sound/soc/codecs/wcd_cpe_services.c b/sound/soc/codecs/wcd_cpe_services.c index 0028ebc08d5f..ad8962b6f1fe 100644 --- a/sound/soc/codecs/wcd_cpe_services.c +++ b/sound/soc/codecs/wcd_cpe_services.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include "wcd_cpe_services.h" #include "wcd_cmi_api.h" @@ -46,9 +45,6 @@ #define LISTEN_CTL_SPE_VAL 0x0 #define LISTEN_CTL_MSM_VAL 0x1 -#define TOMTOM_A_SVASS_SPE_INBOX(N) (TOMTOM_A_SVASS_SPE_INBOX_0 + (N)) -#define TOMTOM_A_SVASS_SPE_OUTBOX(N) (TOMTOM_A_SVASS_SPE_OUTBOX_0 + (N)) - #define WCD9335_CPE_SS_SPE_DRAM_OFFSET 0x48000 #define WCD9335_CPE_SS_SPE_DRAM_SIZE 0x34000 #define WCD9335_CPE_SS_SPE_IRAM_OFFSET 0x80000 @@ -316,8 +312,7 @@ static int cpe_register_write(u32 reg, u32 val) { int ret = 0; - if (reg != TOMTOM_A_SVASS_MEM_BANK && - reg != WCD9335_CPE_SS_MEM_BANK_0) + if (reg != WCD9335_CPE_SS_MEM_BANK_0) pr_debug("%s: reg = 0x%x, value = 0x%x\n", __func__, reg, val); @@ -2149,73 +2144,27 @@ enum cpe_svc_result cpe_svc_ftm_test(void *cpe_handle, u32 *status) static enum cpe_svc_result cpe_tgt_tomtom_boot(int debug_mode) { - enum cpe_svc_result rc = CPE_SVC_SUCCESS; - - if (!debug_mode) - rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_WDOG_CFG, - 0x3F, 0x31); - else - pr_info("%s: CPE in debug mode, WDOG disabled\n", - __func__); - - rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, - 0x02, 0x00); - rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, - 0x0C, 0x04); - rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_CFG, - 0x01, 0x01); - - return rc; + return CPE_SVC_SUCCESS; } static u32 cpe_tgt_tomtom_is_cpar_init_done(void) { - u8 status = 0; - - cpe_register_read(TOMTOM_A_SVASS_STATUS, &status); - return status & 0x01; + return 0; } static u32 cpe_tgt_tomtom_is_active(void) { - u8 status = 0; - - cpe_register_read(TOMTOM_A_SVASS_STATUS, &status); - return status & 0x04; + return 0; } static enum cpe_svc_result cpe_tgt_tomtom_reset(void) { - enum cpe_svc_result rc = CPE_SVC_SUCCESS; - - rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_WDOG_CFG, - 0x30, 0x00); - - rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_CFG, - 0x01, 0x00); - rc = cpe_update_bits(TOMTOM_A_MEM_LEAKAGE_CTL, - 0x07, 0x03); - rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, - 0x08, 0x08); - rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL, - 0x02, 0x02); - return rc; + return CPE_SVC_SUCCESS; } enum cpe_svc_result cpe_tgt_tomtom_voicetx(bool enable) { - enum cpe_svc_result rc = CPE_SVC_SUCCESS; - u8 val = 0; - - if (enable) - val = 0x02; - else - val = 0x00; - rc = cpe_update_bits(TOMTOM_A_SVASS_CFG, - 0x02, val); - val = 0; - cpe_register_read(TOMTOM_A_SVASS_CFG, &val); - return rc; + return CPE_SVC_SUCCESS; } enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable) @@ -2235,251 +2184,37 @@ enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable) static enum cpe_svc_result cpe_tgt_tomtom_read_mailbox(u8 *buffer, size_t size) { - enum cpe_svc_result rc = CPE_SVC_SUCCESS; - u32 cnt = 0; - - if (size >= TOMTOM_A_SVASS_SPE_OUTBOX_SIZE) - size = TOMTOM_A_SVASS_SPE_OUTBOX_SIZE - 1; - for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) { - rc = cpe_register_read(TOMTOM_A_SVASS_SPE_OUTBOX(cnt), - &(buffer[cnt])); - } - return rc; + return CPE_SVC_SUCCESS; } static enum cpe_svc_result cpe_tgt_tomtom_write_mailbox(u8 *buffer, size_t size) { - enum cpe_svc_result rc = CPE_SVC_SUCCESS; - u32 cnt = 0; - - if (size >= TOMTOM_A_SVASS_SPE_INBOX_SIZE) - size = TOMTOM_A_SVASS_SPE_INBOX_SIZE - 1; - for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) { - rc = cpe_register_write(TOMTOM_A_SVASS_SPE_INBOX(cnt), - buffer[cnt]); - } - - if (rc == CPE_SVC_SUCCESS) - rc = cpe_register_write(TOMTOM_A_SVASS_SPE_INBOX_TRG, 1); - - return rc; -} - -static enum cpe_svc_result cpe_get_mem_addr(struct cpe_info *t_info, - const struct cpe_svc_mem_segment *mem_seg, - u32 *addr, u8 *mem) -{ - enum cpe_svc_result rc = CPE_SVC_SUCCESS; - u32 offset, mem_sz, address; - u8 mem_type; - - switch (mem_seg->type) { - - case CPE_SVC_DATA_MEM: - mem_type = MEM_ACCESS_DRAM_VAL; - offset = TOMTOM_A_SVASS_SPE_DRAM_OFFSET; - mem_sz = TOMTOM_A_SVASS_SPE_DRAM_SIZE; - break; - - case CPE_SVC_INSTRUCTION_MEM: - mem_type = MEM_ACCESS_IRAM_VAL; - offset = TOMTOM_A_SVASS_SPE_IRAM_OFFSET; - mem_sz = TOMTOM_A_SVASS_SPE_IRAM_SIZE; - break; - - default: - pr_err("%s: Invalid mem type = %u\n", - __func__, mem_seg->type); - return CPE_SVC_INVALID_HANDLE; - } - - if (mem_seg->cpe_addr < offset) { - pr_err("%s: Invalid addr %x for mem type %u\n", - __func__, mem_seg->cpe_addr, mem_type); - return CPE_SVC_INVALID_HANDLE; - } - - address = mem_seg->cpe_addr - offset; - if (address + mem_seg->size > mem_sz) { - pr_err("%s: wrong size %zu, start address %x, mem_type %u\n", - __func__, mem_seg->size, address, mem_type); - return CPE_SVC_INVALID_HANDLE; - } - - (*addr) = address; - (*mem) = mem_type; - - return rc; + return CPE_SVC_SUCCESS; } static enum cpe_svc_result cpe_tgt_tomtom_read_RAM(struct cpe_info *t_info, struct cpe_svc_mem_segment *mem_seg) { - enum cpe_svc_result rc = CPE_SVC_SUCCESS; - u8 mem_reg_val = 0; - u32 cnt = 0; - bool autoinc; - u8 mem = MEM_ACCESS_NONE_VAL; - u32 addr = 0; - u32 ptr_update = true; - - if (!mem_seg) { - pr_err("%s: Invalid mem segment\n", - __func__); - return CPE_SVC_INVALID_HANDLE; - } - - rc = cpe_get_mem_addr(t_info, mem_seg, &addr, &mem); - - if (rc != CPE_SVC_SUCCESS) { - pr_err("%s: Cannot obtain address, mem_type %u\n", - __func__, mem_seg->type); - return rc; - } - - rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); - autoinc = cpe_register_read_autoinc_supported(); - if (autoinc) - mem_reg_val |= 0x04; - - mem_reg_val |= 0x08; - mem_reg_val |= mem; - - do { - if (!autoinc || ptr_update) { - rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR0, - (addr & 0xFF)); - rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR1, - ((addr >> 8) & 0xFF)); - rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR2, - ((addr >> 16) & 0xFF)); - - rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, - mem_reg_val); - - ptr_update = false; - } - rc = cpe_register_read(TOMTOM_A_SVASS_MEM_BANK, - &mem_seg->data[cnt]); - - if (!autoinc) - rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); - } while (++cnt < mem_seg->size); - - rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); - - return rc; + return CPE_SVC_SUCCESS; } static enum cpe_svc_result cpe_tgt_tomtom_write_RAM(struct cpe_info *t_info, const struct cpe_svc_mem_segment *mem_seg) { - enum cpe_svc_result rc = CPE_SVC_SUCCESS; - u8 mem_reg_val = 0; - u8 mem = MEM_ACCESS_NONE_VAL; - u32 addr = 0; - u8 *temp_ptr = NULL; - u32 temp_size = 0; - bool autoinc; - - if (!mem_seg) { - pr_err("%s: Invalid mem segment\n", - __func__); - return CPE_SVC_INVALID_HANDLE; - } - - rc = cpe_get_mem_addr(t_info, mem_seg, &addr, &mem); - - if (rc != CPE_SVC_SUCCESS) { - pr_err("%s: Cannot obtain address, mem_type %u\n", - __func__, mem_seg->type); - return rc; - } - - autoinc = cpe_register_read_autoinc_supported(); - if (autoinc) - mem_reg_val |= 0x04; - mem_reg_val |= mem; - - rc = cpe_update_bits(TOMTOM_A_SVASS_MEM_CTL, - 0x0F, mem_reg_val); - - rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR0, - (addr & 0xFF)); - rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR1, - ((addr >> 8) & 0xFF)); - - rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR2, - ((addr >> 16) & 0xFF)); - - temp_size = 0; - temp_ptr = mem_seg->data; - - while (temp_size <= mem_seg->size) { - u32 to_write = (mem_seg->size >= temp_size+CHUNK_SIZE) - ? CHUNK_SIZE : (mem_seg->size-temp_size); - - if (t_info->state == CPE_STATE_OFFLINE) { - pr_err("%s: CPE is offline\n", __func__); - return CPE_SVC_FAILED; - } - - cpe_register_write_repeat(TOMTOM_A_SVASS_MEM_BANK, - temp_ptr, to_write); - temp_size += CHUNK_SIZE; - temp_ptr += CHUNK_SIZE; - } - - rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0); - return rc; + return CPE_SVC_SUCCESS; } static enum cpe_svc_result cpe_tgt_tomtom_route_notification( enum cpe_svc_module module, enum cpe_svc_route_dest dest) { - enum cpe_svc_result rc = CPE_SVC_SUCCESS; - u8 ctl_reg_val = 0; - - switch (module) { - case CPE_SVC_LISTEN_PROC: - switch (dest) { - case CPE_SVC_EXTERNAL: - ctl_reg_val = LISTEN_CTL_MSM_VAL; - break; - case CPE_SVC_INTERNAL: - ctl_reg_val = LISTEN_CTL_SPE_VAL; - break; - default: - pr_err("%s: Invalid dest %d\n", - __func__, dest); - return CPE_SVC_FAILED; - } - - rc = cpe_update_bits(TOMTOM_A_SVASS_CFG, - 0x01, ctl_reg_val); - break; - default: - pr_err("%s: Invalid module %d\n", - __func__, module); - rc = CPE_SVC_FAILED; - break; - } - - return rc; + return CPE_SVC_SUCCESS; } static enum cpe_svc_result cpe_tgt_tomtom_set_debug_mode(u32 enable) { - enum cpe_svc_result rc = CPE_SVC_SUCCESS; - u8 dbg_reg_val = 0x00; - - if (enable) - dbg_reg_val = 0x08; - rc = cpe_update_bits(TOMTOM_A_SVASS_DEBUG, - 0x08, dbg_reg_val); - return rc; + return CPE_SVC_SUCCESS; } static const struct cpe_svc_hw_cfg *cpe_tgt_tomtom_get_cpe_info(void) diff --git a/sound/soc/msm/qdsp6v2/audio_calibration.c b/sound/soc/msm/qdsp6v2/audio_calibration.c index 808a0e4b72d1..d709b091a799 100644 --- a/sound/soc/msm/qdsp6v2/audio_calibration.c +++ b/sound/soc/msm/qdsp6v2/audio_calibration.c @@ -460,6 +460,12 @@ static long audio_cal_shared_ioctl(struct file *file, unsigned int cmd, data->cal_type.cal_hdr.buffer_number); ret = -EINVAL; goto done; + } else if ((data->hdr.cal_type_size + sizeof(data->hdr)) > size) { + pr_err("%s: cal type hdr size %zd + cal type size %d is greater than user buffer size %d\n", + __func__, sizeof(data->hdr), data->hdr.cal_type_size, + size); + ret = -EFAULT; + goto done; } @@ -497,13 +503,7 @@ static long audio_cal_shared_ioctl(struct file *file, unsigned int cmd, 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, + if (copy_to_user(arg, data, sizeof(data->hdr) + data->hdr.cal_type_size)) { pr_err("%s: Could not copy cal type to user\n", __func__); diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c index 325d642b7d7c..75a2bffaa3ce 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c +++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c @@ -30,9 +30,12 @@ #include #include #include +#include + #include #include #include +#include #include "msm-pcm-q6-v2.h" #include "msm-pcm-routing-v2.h" @@ -421,6 +424,42 @@ static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } + +static int msm_pcm_mmap_fd(struct snd_pcm_substream *substream, + struct snd_pcm_mmap_fd *mmap_fd) +{ + struct msm_audio *prtd; + struct audio_port_data *apd; + struct audio_buffer *ab; + int dir = -1; + + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return -EFAULT; + } + + prtd = substream->runtime->private_data; + if (!prtd || !prtd->audio_client || !prtd->mmap_flag) { + pr_err("%s no audio client or not an mmap session\n", __func__); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = IN; + else + dir = OUT; + + apd = prtd->audio_client->port; + ab = &(apd[dir].buf[0]); + mmap_fd->fd = ion_share_dma_buf_fd(ab->client, ab->handle); + if (mmap_fd->fd >= 0) { + mmap_fd->dir = dir; + mmap_fd->actual_size = ab->actual_size; + mmap_fd->size = ab->size; + } + return mmap_fd->fd < 0 ? -EFAULT : 0; +} + static int msm_pcm_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg) { @@ -445,6 +484,15 @@ static int msm_pcm_ioctl(struct snd_pcm_substream *substream, return snd_pcm_lib_ioctl(substream, cmd, arg); } +#ifdef CONFIG_COMPAT +static int msm_pcm_compat_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + /* we only handle RESET which is common for both modes */ + return msm_pcm_ioctl(substream, cmd, arg); +} +#endif + static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -994,6 +1042,101 @@ static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd) return 0; } +static int msm_pcm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct snd_pcm *pcm = hw->private_data; + struct snd_pcm_mmap_fd __user *_mmap_fd = NULL; + struct snd_pcm_mmap_fd mmap_fd; + struct snd_pcm_substream *substream = NULL; + int32_t dir = -1; + + switch (cmd) { + case SNDRV_PCM_IOCTL_MMAP_DATA_FD: + _mmap_fd = (struct snd_pcm_mmap_fd __user *)arg; + if (get_user(dir, (int32_t __user *)&(_mmap_fd->dir))) { + pr_err("%s: error copying mmap_fd from user\n", + __func__); + ret = -EFAULT; + break; + } + if (dir != OUT && dir != IN) { + pr_err("%s invalid stream dir\n", __func__); + ret = -EINVAL; + break; + } + substream = pcm->streams[dir].substream; + if (!substream) { + pr_err("%s substream not found\n", __func__); + ret = -ENODEV; + break; + } + pr_debug("%s : %s MMAP Data fd\n", __func__, + dir == 0 ? "P" : "C"); + if (msm_pcm_mmap_fd(substream, &mmap_fd) < 0) { + pr_err("%s: error getting fd\n", + __func__); + ret = -EFAULT; + break; + } + if (put_user(mmap_fd.fd, &_mmap_fd->fd) || + put_user(mmap_fd.size, &_mmap_fd->size) || + put_user(mmap_fd.actual_size, &_mmap_fd->actual_size)) { + pr_err("%s: error copying fd\n", __func__); + return -EFAULT; + } + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static int msm_pcm_hwdep_compat_ioctl(struct snd_hwdep *hw, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + /* we only support mmap fd. Handling is common in both modes */ + return msm_pcm_hwdep_ioctl(hw, file, cmd, arg); +} +#else +static int msm_pcm_hwdep_compat_ioctl(struct snd_hwdep *hw, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + return -EINVAL; +} +#endif + +static int msm_pcm_add_hwdep_dev(struct snd_soc_pcm_runtime *runtime) +{ + struct snd_hwdep *hwdep; + int rc; + char id[] = "NOIRQ_NN"; + + snprintf(id, sizeof(id), "NOIRQ_%d", runtime->pcm->device); + pr_debug("%s: pcm dev %d\n", __func__, runtime->pcm->device); + rc = snd_hwdep_new(runtime->card->snd_card, + &id[0], + HWDEP_FE_BASE + runtime->pcm->device, + &hwdep); + if (!hwdep || rc < 0) { + pr_err("%s: hwdep intf failed to create %s - hwdep\n", __func__, + id); + return rc; + } + + hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_BE; /* for lack of a FE iface */ + hwdep->private_data = runtime->pcm; /* of type struct snd_pcm */ + hwdep->ops.ioctl = msm_pcm_hwdep_ioctl; + hwdep->ops.ioctl_compat = msm_pcm_hwdep_compat_ioctl; + return 0; +} static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) { @@ -1027,7 +1170,9 @@ static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd) pr_err("%s: Could not add app type controls failed %d\n", __func__, ret); } - + ret = msm_pcm_add_hwdep_dev(rtd); + if (ret) + pr_err("%s: Could not add hw dep node\n", __func__); pcm->nonatomic = true; exit: return ret; @@ -1040,6 +1185,9 @@ static const struct snd_pcm_ops msm_pcm_ops = { .copy = msm_pcm_copy, .hw_params = msm_pcm_hw_params, .ioctl = msm_pcm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = msm_pcm_compat_ioctl, +#endif .trigger = msm_pcm_trigger, .pointer = msm_pcm_pointer, .mmap = msm_pcm_mmap, diff --git a/sound/soc/msm/sdm660-common.c b/sound/soc/msm/sdm660-common.c index eddcb45e9150..b34b04b83044 100644 --- a/sound/soc/msm/sdm660-common.c +++ b/sound/soc/msm/sdm660-common.c @@ -12,6 +12,9 @@ #include #include +#include +#include +#include #include #include #include @@ -190,7 +193,7 @@ struct msm_wsa881x_dev_info { 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 bool msm_swap_gnd_mic(struct snd_soc_codec *codec, bool active); static struct wcd_mbhc_config mbhc_cfg = { .read_fw_bin = false, @@ -2038,16 +2041,16 @@ static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit) } } -static int msm_ext_disp_get_idx_from_beid(int32_t be_id) +static int msm_ext_disp_get_idx_from_beid(int32_t id) { int idx; - switch (be_id) { + switch (id) { 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); + pr_err("%s: Incorrect ext_disp id %d\n", __func__, id); idx = -EINVAL; break; } @@ -2077,7 +2080,7 @@ int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, pr_debug("%s: format = %d, rate = %d\n", __func__, params_format(params), params_rate(params)); - switch (dai_link->be_id) { + switch (dai_link->id) { case MSM_BACKEND_DAI_USB_RX: param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, usb_rx_cfg.bit_format); @@ -2093,8 +2096,8 @@ int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, break; case MSM_BACKEND_DAI_DISPLAY_PORT_RX: - idx = msm_ext_disp_get_idx_from_beid(dai_link->be_id); - if (IS_ERR_VALUE(idx)) { + idx = msm_ext_disp_get_idx_from_beid(dai_link->id); + if (idx < 0) { pr_err("%s: Incorrect ext disp idx %d\n", __func__, idx); rc = idx; @@ -2341,11 +2344,11 @@ void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(msm_aux_pcm_snd_shutdown); -static int msm_get_port_id(int be_id) +static int msm_get_port_id(int id) { int afe_port_id; - switch (be_id) { + switch (id) { case MSM_BACKEND_DAI_PRI_MI2S_RX: afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX; break; @@ -2371,7 +2374,7 @@ static int msm_get_port_id(int be_id) afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; break; default: - pr_err("%s: Invalid be_id: %d\n", __func__, be_id); + pr_err("%s: Invalid id: %d\n", __func__, id); afe_port_id = -EINVAL; } @@ -2422,7 +2425,7 @@ static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) int port_id = 0; int index = cpu_dai->id; - port_id = msm_get_port_id(rtd->dai_link->be_id); + port_id = msm_get_port_id(rtd->dai_link->id); if (port_id < 0) { dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); ret = port_id; @@ -2461,7 +2464,7 @@ 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 port_id = msm_get_port_id(rtd->dai_link->be_id); + int port_id = msm_get_port_id(rtd->dai_link->id); int index = cpu_dai->id; unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; @@ -2539,7 +2542,7 @@ void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) { int ret; struct snd_soc_pcm_runtime *rtd = substream->private_data; - int port_id = msm_get_port_id(rtd->dai_link->be_id); + int port_id = msm_get_port_id(rtd->dai_link->id); int index = rtd->cpu_dai->id; pr_debug("%s(): substream = %s stream = %d\n", __func__, @@ -2594,7 +2597,7 @@ static int msm_prepare_us_euro(struct snd_soc_card *card) return ret; } -static bool msm_swap_gnd_mic(struct snd_soc_codec *codec) +static bool msm_swap_gnd_mic(struct snd_soc_codec *codec, bool active) { struct snd_soc_card *card = codec->component.card; struct msm_asoc_mach_data *pdata = @@ -2699,13 +2702,13 @@ static int msm_populate_dai_link_component_of_node( dai_link[i].codec_name = NULL; } if (pdata->snd_card_val == INT_SND_CARD) { - if ((dai_link[i].be_id == + if ((dai_link[i].id == MSM_BACKEND_DAI_INT0_MI2S_RX) || - (dai_link[i].be_id == + (dai_link[i].id == MSM_BACKEND_DAI_INT1_MI2S_RX) || - (dai_link[i].be_id == + (dai_link[i].id == MSM_BACKEND_DAI_INT2_MI2S_TX) || - (dai_link[i].be_id == + (dai_link[i].id == MSM_BACKEND_DAI_INT3_MI2S_TX)) { index = of_property_match_string(cdev->of_node, "asoc-codec-names", diff --git a/sound/soc/msm/sdm660-ext-dai-links.c b/sound/soc/msm/sdm660-ext-dai-links.c index 1c03d8c9e797..77d3875d0a06 100644 --- a/sound/soc/msm/sdm660-ext-dai-links.c +++ b/sound/soc/msm/sdm660-ext-dai-links.c @@ -86,8 +86,8 @@ static int msm_wcn_hw_params(struct snd_pcm_substream *substream, goto exit; } - dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) be_id %d\n", - __func__, tx_ch_cnt, dai_link->be_id); + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) id %d\n", + __func__, tx_ch_cnt, dai_link->id); ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); @@ -279,7 +279,7 @@ static struct snd_soc_dai_link msm_ext_tasha_fe_dai[] = { .platform_name = "msm-pcm-hostless", .codec_name = "tasha_codec", .codec_dai_name = "tasha_vifeedback", - .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .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, @@ -368,7 +368,7 @@ static struct snd_soc_dai_link msm_ext_tavil_fe_dai[] = { .platform_name = "msm-pcm-hostless", .codec_name = "tavil_codec", .codec_dai_name = "tavil_vifeedback", - .be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX, + .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, @@ -411,7 +411,7 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { .codec_dai_name = "tasha_mix_rx1", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .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 */ @@ -428,7 +428,7 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { .codec_dai_name = "tasha_tx1", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .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, @@ -442,7 +442,7 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { .codec_dai_name = "tasha_mix_rx1", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .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 */ @@ -458,7 +458,7 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { .codec_dai_name = "tasha_tx3", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .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, @@ -472,7 +472,7 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { .codec_dai_name = "tasha_mix_rx1", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .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 */ @@ -489,7 +489,7 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { .no_pcm = 1, .dpcm_capture = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .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, @@ -503,7 +503,7 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { .codec_dai_name = "tasha_mix_rx1", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .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 */ @@ -519,7 +519,7 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { .codec_dai_name = "tasha_rx3", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .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 */ @@ -536,7 +536,7 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { .codec_dai_name = "tasha_mad1", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .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, @@ -550,7 +550,7 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { .codec_dai_name = "tasha_rx4", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .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 */ @@ -569,7 +569,7 @@ static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { .codec_dai_name = "tavil_rx1", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX, + .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 */ @@ -586,7 +586,7 @@ static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { .codec_dai_name = "tavil_tx1", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX, + .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, @@ -600,7 +600,7 @@ static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { .codec_dai_name = "tavil_rx1", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX, + .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 */ @@ -616,7 +616,7 @@ static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { .codec_dai_name = "tavil_tx3", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX, + .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, @@ -630,7 +630,7 @@ static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { .codec_dai_name = "tavil_rx2", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_2_RX, + .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, @@ -645,7 +645,7 @@ static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { .codec_dai_name = "tavil_rx1", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX, + .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 */ @@ -661,7 +661,7 @@ static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { .codec_dai_name = "tavil_tx1", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX, + .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, @@ -675,7 +675,7 @@ static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { .codec_dai_name = "tavil_rx1", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX, + .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 */ @@ -691,7 +691,7 @@ static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { .codec_dai_name = "tavil_rx3", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX, + .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 */ @@ -708,7 +708,7 @@ static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { .codec_dai_name = "tavil_mad1", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX, + .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, @@ -722,7 +722,7 @@ static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { .codec_dai_name = "tavil_rx4", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX, + .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 */ @@ -748,7 +748,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .dpcm_capture = 1, /* this dai link has playback support */ .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 }, {/* hw:x,1 */ .name = MSM_DAILINK_NAME(Media2), @@ -765,7 +765,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, /* this dai link has playback support */ .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, }, {/* hw:x,2 */ .name = "VoiceMMode1", @@ -782,7 +782,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_VOICEMMODE1, + .id = MSM_FRONTEND_DAI_VOICEMMODE1, }, {/* hw:x,3 */ .name = "MSM VoIP", @@ -799,7 +799,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, /* this dai link has playback support */ .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_VOIP, + .id = MSM_FRONTEND_DAI_VOIP, }, {/* hw:x,4 */ .name = MSM_DAILINK_NAME(ULL), @@ -815,7 +815,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, /* this dai link has playback support */ .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, }, /* Hostless PCM purpose */ {/* hw:x,5 */ @@ -871,7 +871,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4, + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, }, {/* hw:x,9*/ .name = "AUXPCM Hostless", @@ -953,7 +953,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, /* this dai link has playback support */ .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, }, /* LSM FE */ {/* hw:x,14 */ @@ -970,7 +970,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM1, + .id = MSM_FRONTEND_DAI_LSM1, }, {/* hw:x,15 */ .name = MSM_DAILINK_NAME(Compress2), @@ -987,7 +987,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, /* this dai link has playback support */ .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7, + .id = MSM_FRONTEND_DAI_MULTIMEDIA7, }, {/* hw:x,16 */ .name = MSM_DAILINK_NAME(Compress3), @@ -1004,7 +1004,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10, + .id = MSM_FRONTEND_DAI_MULTIMEDIA10, }, {/* hw:x,17 */ .name = MSM_DAILINK_NAME(ULL_NOIRQ), @@ -1021,7 +1021,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, }, {/* hw:x,18 */ .name = "HDMI_RX_HOSTLESS", @@ -1053,7 +1053,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_VOICEMMODE2, + .id = MSM_FRONTEND_DAI_VOICEMMODE2, }, {/* hw:x,20 */ .name = "Listen 2 Audio Service", @@ -1069,7 +1069,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM2, + .id = MSM_FRONTEND_DAI_LSM2, }, {/* hw:x,21 */ .name = "Listen 3 Audio Service", @@ -1085,7 +1085,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM3, + .id = MSM_FRONTEND_DAI_LSM3, }, {/* hw:x,22 */ .name = "Listen 4 Audio Service", @@ -1101,7 +1101,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM4, + .id = MSM_FRONTEND_DAI_LSM4, }, {/* hw:x,23 */ .name = "Listen 5 Audio Service", @@ -1117,7 +1117,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM5, + .id = MSM_FRONTEND_DAI_LSM5, }, {/* hw:x,24 */ .name = "Listen 6 Audio Service", @@ -1133,7 +1133,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM6 + .id = MSM_FRONTEND_DAI_LSM6 }, {/* hw:x,25 */ .name = "Listen 7 Audio Service", @@ -1149,7 +1149,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM7, + .id = MSM_FRONTEND_DAI_LSM7, }, {/* hw:x,26 */ .name = "Listen 8 Audio Service", @@ -1165,7 +1165,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM8, + .id = MSM_FRONTEND_DAI_LSM8, }, {/* hw:x,27 */ .name = MSM_DAILINK_NAME(Media9), @@ -1182,7 +1182,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9, + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, }, {/* hw:x,28 */ .name = MSM_DAILINK_NAME(Compress4), @@ -1199,7 +1199,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11, + .id = MSM_FRONTEND_DAI_MULTIMEDIA11, }, {/* hw:x,29 */ .name = MSM_DAILINK_NAME(Compress5), @@ -1216,7 +1216,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12, + .id = MSM_FRONTEND_DAI_MULTIMEDIA12, }, {/* hw:x,30 */ .name = MSM_DAILINK_NAME(Compress6), @@ -1233,7 +1233,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13, + .id = MSM_FRONTEND_DAI_MULTIMEDIA13, }, {/* hw:x,31 */ .name = MSM_DAILINK_NAME(Compress7), @@ -1250,7 +1250,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14, + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, }, {/* hw:x,32 */ .name = MSM_DAILINK_NAME(Compress8), @@ -1267,7 +1267,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, + .id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, {/* hw:x,33 */ .name = MSM_DAILINK_NAME(Compress9), @@ -1284,7 +1284,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16, + .id = MSM_FRONTEND_DAI_MULTIMEDIA16, }, {/* hw:x,34 */ .name = "SLIMBUS_8 Hostless", @@ -1332,7 +1332,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_suspend = 1, .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, }, }; @@ -1346,7 +1346,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_AFE_PCM_RX, + .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, @@ -1361,7 +1361,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_AFE_PCM_TX, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -1375,7 +1375,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, .be_hw_params_fixup = msm_ext_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -1389,7 +1389,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, .be_hw_params_fixup = msm_ext_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -1403,7 +1403,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, .be_hw_params_fixup = msm_ext_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -1417,7 +1417,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, .be_hw_params_fixup = msm_ext_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -1430,7 +1430,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_USB_RX, + .id = MSM_BACKEND_DAI_USB_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -1444,7 +1444,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_USB_TX, + .id = MSM_BACKEND_DAI_USB_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -1457,7 +1457,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .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, @@ -1471,7 +1471,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .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, @@ -1485,7 +1485,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .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, @@ -1499,7 +1499,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .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, @@ -1513,7 +1513,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .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, @@ -1527,7 +1527,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .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, @@ -1541,7 +1541,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .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, @@ -1555,7 +1555,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .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, @@ -1572,7 +1572,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .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, @@ -1587,7 +1587,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .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, @@ -1601,7 +1601,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .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, @@ -1616,7 +1616,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .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, @@ -1630,7 +1630,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .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, @@ -1645,7 +1645,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .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, @@ -1659,7 +1659,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .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, @@ -1674,7 +1674,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .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, @@ -1692,7 +1692,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_AUXPCM_RX, + .id = MSM_BACKEND_DAI_AUXPCM_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -1707,7 +1707,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_AUXPCM_TX, + .id = MSM_BACKEND_DAI_AUXPCM_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -1723,7 +1723,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -1738,7 +1738,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -1754,7 +1754,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -1769,7 +1769,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -1785,7 +1785,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -1800,7 +1800,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -1822,7 +1822,7 @@ static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .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 */ @@ -1838,7 +1838,7 @@ static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { .codec_dai_name = "btfm_bt_sco_slim_tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .id = MSM_BACKEND_DAI_SLIMBUS_7_TX, .be_hw_params_fixup = msm_ext_be_hw_params_fixup, .ops = &msm_wcn_ops, .ignore_suspend = 1, @@ -1852,7 +1852,7 @@ static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { .codec_dai_name = "btfm_fm_slim_tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .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, @@ -1871,7 +1871,7 @@ static struct snd_soc_dai_link ext_disp_be_dai_link[] = { .codec_dai_name = "msm_dp_audio_codec_rx_dai", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, + .id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, diff --git a/sound/soc/msm/sdm660-external.c b/sound/soc/msm/sdm660-external.c index 2c3d7fc3e63f..84d1c2ea9b18 100644 --- a/sound/soc/msm/sdm660-external.c +++ b/sound/soc/msm/sdm660-external.c @@ -747,11 +747,11 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { msm_bt_sample_rate_put), }; -static int msm_slim_get_ch_from_beid(int32_t be_id) +static int msm_slim_get_ch_from_beid(int32_t id) { int ch_id = 0; - switch (be_id) { + switch (id) { case MSM_BACKEND_DAI_SLIMBUS_0_RX: ch_id = SLIM_RX_0; break; @@ -821,14 +821,14 @@ int msm_ext_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, pr_debug("%s: format = %d, rate = %d\n", __func__, params_format(params), params_rate(params)); - switch (dai_link->be_id) { + switch (dai_link->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); + idx = msm_slim_get_ch_from_beid(dai_link->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; @@ -837,7 +837,7 @@ int msm_ext_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, 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); + idx = msm_slim_get_ch_from_beid(dai_link->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; @@ -938,15 +938,15 @@ int msm_snd_hw_params(struct snd_pcm_substream *substream, __func__, ret); goto err_ch_map; } - if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_5_RX) { + if (dai_link->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) { + } else if (dai_link->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) { + } else if (dai_link->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; @@ -973,19 +973,19 @@ int msm_snd_hw_params(struct snd_pcm_substream *substream, goto err_ch_map; } /* For _tx1 case */ - if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_0_TX) + if (dai_link->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) + else if (dai_link->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) + else if (dai_link->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", + pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), id (%d)\n", __func__, slim_tx_cfg[0].channels, user_set_tx_ch, - tx_ch_cnt, dai_link->be_id); + tx_ch_cnt, dai_link->id); ret = snd_soc_dai_set_channel_map(cpu_dai, user_set_tx_ch, tx_ch, 0, 0); @@ -1097,8 +1097,8 @@ int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream, goto end; } - pr_debug("%s: tx_ch_cnt(%d) be_id %d\n", - __func__, tx_ch_cnt, dai_link->be_id); + pr_debug("%s: tx_ch_cnt(%d) id %d\n", + __func__, tx_ch_cnt, dai_link->id); ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt, tx_ch, 0, 0); @@ -1495,7 +1495,7 @@ int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) 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_soc_component *aux_comp; struct snd_card *card; struct snd_info_entry *entry; struct msm_asoc_mach_data *pdata = @@ -1678,13 +1678,20 @@ int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) * 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_aux && rtd_aux->component) - if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) || - !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) { + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->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", @@ -1698,12 +1705,16 @@ int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) 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)) { + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->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", diff --git a/sound/soc/msm/sdm660-internal.c b/sound/soc/msm/sdm660-internal.c index 802137ba4b50..a57d6f611942 100644 --- a/sound/soc/msm/sdm660-internal.c +++ b/sound/soc/msm/sdm660-internal.c @@ -539,11 +539,11 @@ static int enable_spk_ext_pa(struct snd_soc_codec *codec, int enable) return 0; } -static int int_mi2s_get_idx_from_beid(int32_t be_id) +static int int_mi2s_get_idx_from_beid(int32_t id) { int idx = 0; - switch (be_id) { + switch (id) { case MSM_BACKEND_DAI_INT0_MI2S_RX: idx = INT0_MI2S; break; @@ -596,13 +596,13 @@ static int int_mi2s_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, pr_debug("%s: format = %d, rate = %d\n", __func__, params_format(params), params_rate(params)); - switch (dai_link->be_id) { + switch (dai_link->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); + idx = int_mi2s_get_idx_from_beid(dai_link->id); rate->min = rate->max = int_mi2s_cfg[idx].sample_rate; channels->min = channels->max = int_mi2s_cfg[idx].channels; @@ -625,7 +625,7 @@ static int msm_btfm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); - switch (dai_link->be_id) { + switch (dai_link->id) { case MSM_BACKEND_DAI_SLIMBUS_7_RX: case MSM_BACKEND_DAI_SLIMBUS_7_TX: param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, @@ -982,11 +982,11 @@ static int msm_int_mclk0_event(struct snd_soc_dapm_widget *w, return 0; } -static int int_mi2s_get_port_id(int be_id) +static int int_mi2s_get_port_id(int id) { int afe_port_id; - switch (be_id) { + switch (id) { case MSM_BACKEND_DAI_INT0_MI2S_RX: afe_port_id = AFE_PORT_ID_INT0_MI2S_RX; break; @@ -1003,7 +1003,7 @@ static int int_mi2s_get_port_id(int be_id) afe_port_id = AFE_PORT_ID_INT5_MI2S_TX; break; default: - pr_err("%s: Invalid be_id: %d\n", __func__, be_id); + pr_err("%s: Invalid id: %d\n", __func__, id); afe_port_id = -EINVAL; } @@ -1073,7 +1073,7 @@ static int int_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable) int port_id = 0; int index; - port_id = int_mi2s_get_port_id(rtd->dai_link->be_id); + port_id = int_mi2s_get_port_id(rtd->dai_link->id); if (port_id < 0) { dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__); ret = port_id; @@ -1303,7 +1303,7 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) card = rtd->card->snd_card; if (!codec_root) - codec_root = snd_register_module_info(card->module, "codecs", + codec_root = snd_info_create_subdir(card->module, "codecs", card->proc_root); if (!codec_root) { pr_debug("%s: Cannot create codecs module entry\n", @@ -1323,7 +1323,7 @@ static int msm_sdw_audrx_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux; + struct snd_soc_component *aux_comp; struct snd_card *card; snd_soc_add_codec_controls(codec, msm_sdw_controls, @@ -1342,16 +1342,22 @@ static int msm_sdw_audrx_init(struct snd_soc_pcm_runtime *rtd) * Send speaker configuration only for WSA8810. * Default 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)) { + pr_debug("%s: Number of aux devices: %d\n", + __func__, rtd->card->num_aux_devs); + if (rtd->card->num_aux_devs && + !list_empty(&rtd->card->aux_comp_list)) { + aux_comp = list_first_entry(&rtd->card->aux_comp_list, + struct snd_soc_component, list_aux); + if (!strcmp(aux_comp->name, WSA8810_NAME_1) || + !strcmp(aux_comp->name, WSA8810_NAME_2)) { msm_sdw_set_spkr_mode(rtd->codec, SPKR_MODE_1); msm_sdw_set_spkr_gain_offset(rtd->codec, RX_GAIN_OFFSET_M1P5_DB); + } } card = rtd->card->snd_card; if (!codec_root) - codec_root = snd_register_module_info(card->module, "codecs", + codec_root = snd_info_create_subdir(card->module, "codecs", card->proc_root); if (!codec_root) { pr_debug("%s: Cannot create codecs module entry\n", @@ -1396,8 +1402,8 @@ static int msm_wcn_hw_params(struct snd_pcm_substream *substream, goto exit; } - dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) be_id %d\n", - __func__, tx_ch_cnt, dai_link->be_id); + dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) id %d\n", + __func__, tx_ch_cnt, dai_link->id); ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch); @@ -1669,7 +1675,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .dpcm_capture = 1, /* this dai link has playback support */ .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1 + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 }, {/* hw:x,1 */ .name = MSM_DAILINK_NAME(Media2), @@ -1686,7 +1692,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, /* this dai link has playback support */ .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2, + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, }, {/* hw:x,2 */ .name = "VoiceMMode1", @@ -1703,7 +1709,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_VOICEMMODE1, + .id = MSM_FRONTEND_DAI_VOICEMMODE1, }, {/* hw:x,3 */ .name = "MSM VoIP", @@ -1720,7 +1726,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, /* this dai link has playback support */ .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_VOIP, + .id = MSM_FRONTEND_DAI_VOIP, }, {/* hw:x,4 */ .name = MSM_DAILINK_NAME(ULL), @@ -1736,7 +1742,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, /* this dai link has playback support */ .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3, + .id = MSM_FRONTEND_DAI_MULTIMEDIA3, }, /* Hostless PCM purpose */ {/* hw:x,5 */ @@ -1792,7 +1798,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA4, + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, }, {/* hw:x,9*/ .name = "AUXPCM Hostless", @@ -1873,7 +1879,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, /* this dai link has playback support */ .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, }, /* LSM FE */ {/* hw:x,14 */ @@ -1890,7 +1896,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM1, + .id = MSM_FRONTEND_DAI_LSM1, }, {/* hw:x,15 */ .name = MSM_DAILINK_NAME(Compress2), @@ -1905,7 +1911,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .ignore_suspend = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA7, + .id = MSM_FRONTEND_DAI_MULTIMEDIA7, }, {/* hw:x,16 */ .name = MSM_DAILINK_NAME(Compress3), @@ -1922,7 +1928,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA10, + .id = MSM_FRONTEND_DAI_MULTIMEDIA10, }, {/* hw:x,17 */ .name = MSM_DAILINK_NAME(ULL_NOIRQ), @@ -1939,7 +1945,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .id = MSM_FRONTEND_DAI_MULTIMEDIA8, }, {/* hw:x,18 */ .name = "HDMI_RX_HOSTLESS", @@ -1971,7 +1977,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_VOICEMMODE2, + .id = MSM_FRONTEND_DAI_VOICEMMODE2, }, {/* hw:x,20 */ .name = "Listen 2 Audio Service", @@ -1987,7 +1993,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM2, + .id = MSM_FRONTEND_DAI_LSM2, }, {/* hw:x,21 */ .name = "Listen 3 Audio Service", @@ -2003,7 +2009,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM3, + .id = MSM_FRONTEND_DAI_LSM3, }, {/* hw:x,22 */ .name = "Listen 4 Audio Service", @@ -2019,7 +2025,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM4, + .id = MSM_FRONTEND_DAI_LSM4, }, {/* hw:x,23 */ .name = "Listen 5 Audio Service", @@ -2035,7 +2041,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM5, + .id = MSM_FRONTEND_DAI_LSM5, }, {/* hw:x,24 */ .name = "Listen 6 Audio Service", @@ -2051,7 +2057,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM6 + .id = MSM_FRONTEND_DAI_LSM6 }, {/* hw:x,25 */ .name = "Listen 7 Audio Service", @@ -2067,7 +2073,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM7, + .id = MSM_FRONTEND_DAI_LSM7, }, {/* hw:x,26 */ .name = "Listen 8 Audio Service", @@ -2083,7 +2089,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_pmdown_time = 1, .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", - .be_id = MSM_FRONTEND_DAI_LSM8, + .id = MSM_FRONTEND_DAI_LSM8, }, {/* hw:x,27 */ .name = MSM_DAILINK_NAME(Media9), @@ -2100,7 +2106,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA9, + .id = MSM_FRONTEND_DAI_MULTIMEDIA9, }, {/* hw:x,28 */ .name = MSM_DAILINK_NAME(Compress4), @@ -2117,7 +2123,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA11, + .id = MSM_FRONTEND_DAI_MULTIMEDIA11, }, {/* hw:x,29 */ .name = MSM_DAILINK_NAME(Compress5), @@ -2134,7 +2140,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA12, + .id = MSM_FRONTEND_DAI_MULTIMEDIA12, }, {/* hw:x,30 */ .name = MSM_DAILINK_NAME(Compress6), @@ -2151,7 +2157,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA13, + .id = MSM_FRONTEND_DAI_MULTIMEDIA13, }, {/* hw:x,31 */ .name = MSM_DAILINK_NAME(Compress7), @@ -2168,7 +2174,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA14, + .id = MSM_FRONTEND_DAI_MULTIMEDIA14, }, {/* hw:x,32 */ .name = MSM_DAILINK_NAME(Compress8), @@ -2185,7 +2191,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA15, + .id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, {/* hw:x,33 */ .name = MSM_DAILINK_NAME(Compress9), @@ -2202,7 +2208,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, .ignore_pmdown_time = 1, /* this dai link has playback support */ - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA16, + .id = MSM_FRONTEND_DAI_MULTIMEDIA16, }, {/* hw:x,34 */ .name = "SLIMBUS_8 Hostless", @@ -2302,7 +2308,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_suspend = 1, .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, .ignore_pmdown_time = 1, - .be_id = MSM_FRONTEND_DAI_MULTIMEDIA6, + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, }, }; @@ -2315,7 +2321,7 @@ static struct snd_soc_dai_link msm_int_wsa_dai[] = { .platform_name = "msm-pcm-hostless", .codec_name = "msm_sdw_codec", .codec_dai_name = "msm_sdw_vifeedback", - .be_id = MSM_BACKEND_DAI_INT5_MI2S_TX, + .id = MSM_BACKEND_DAI_INT5_MI2S_TX, .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, .ops = &msm_sdw_mi2s_be_ops, .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, @@ -2338,7 +2344,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .dpcm_playback = 1, .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | ASYNC_DPCM_SND_SOC_HW_PARAMS, - .be_id = MSM_BACKEND_DAI_INT0_MI2S_RX, + .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, @@ -2355,7 +2361,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .dpcm_capture = 1, .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | ASYNC_DPCM_SND_SOC_HW_PARAMS, - .be_id = MSM_BACKEND_DAI_INT3_MI2S_TX, + .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, @@ -2371,7 +2377,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .dpcm_capture = 1, .async_ops = ASYNC_DPCM_SND_SOC_PREPARE | ASYNC_DPCM_SND_SOC_HW_PARAMS, - .be_id = MSM_BACKEND_DAI_INT2_MI2S_TX, + .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, @@ -2385,7 +2391,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_AFE_PCM_RX, + .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, @@ -2400,7 +2406,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_AFE_PCM_TX, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -2414,7 +2420,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, .be_hw_params_fixup = msm_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -2428,7 +2434,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, .be_hw_params_fixup = msm_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -2442,7 +2448,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, .be_hw_params_fixup = msm_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -2456,7 +2462,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, + .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, .be_hw_params_fixup = msm_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -2469,7 +2475,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_USB_RX, + .id = MSM_BACKEND_DAI_USB_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -2483,7 +2489,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_USB_TX, + .id = MSM_BACKEND_DAI_USB_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_suspend = 1, }, @@ -2496,7 +2502,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .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, @@ -2510,7 +2516,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .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, @@ -2524,7 +2530,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .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, @@ -2538,7 +2544,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .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, @@ -2552,7 +2558,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0, + .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, @@ -2566,7 +2572,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0, + .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, @@ -2580,7 +2586,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0, + .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, @@ -2594,7 +2600,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, + .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, @@ -2611,7 +2617,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .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, @@ -2626,7 +2632,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .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, @@ -2640,7 +2646,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .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, @@ -2655,7 +2661,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .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, @@ -2669,7 +2675,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + .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, @@ -2684,7 +2690,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX, + .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, @@ -2698,7 +2704,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + .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, @@ -2713,7 +2719,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, + .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, @@ -2731,7 +2737,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_AUXPCM_RX, + .id = MSM_BACKEND_DAI_AUXPCM_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -2746,7 +2752,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_AUXPCM_TX, + .id = MSM_BACKEND_DAI_AUXPCM_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -2762,7 +2768,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -2777,7 +2783,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -2793,7 +2799,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -2808,7 +2814,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, + .id = MSM_BACKEND_DAI_TERT_AUXPCM_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -2824,7 +2830,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, @@ -2839,7 +2845,7 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .codec_dai_name = "msm-stub-tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + .id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_suspend = 1, .ignore_pmdown_time = 1, @@ -2862,7 +2868,7 @@ static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { .codec_dai_name = "btfm_bt_sco_a2dp_slim_rx", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_7_RX, + .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 */ @@ -2878,7 +2884,7 @@ static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { .codec_dai_name = "btfm_bt_sco_slim_tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_7_TX, + .id = MSM_BACKEND_DAI_SLIMBUS_7_TX, .be_hw_params_fixup = msm_btfm_be_hw_params_fixup, .ops = &msm_wcn_ops, .ignore_suspend = 1, @@ -2892,7 +2898,7 @@ static struct snd_soc_dai_link msm_wcn_be_dai_links[] = { .codec_dai_name = "btfm_fm_slim_tx", .no_pcm = 1, .dpcm_capture = 1, - .be_id = MSM_BACKEND_DAI_SLIMBUS_8_TX, + .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, @@ -2910,7 +2916,7 @@ static struct snd_soc_dai_link msm_wsa_be_dai_links[] = { .codec_dai_name = "msm_sdw_i2s_rx1", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_INT4_MI2S_RX, + .id = MSM_BACKEND_DAI_INT4_MI2S_RX, .init = &msm_sdw_audrx_init, .be_hw_params_fixup = int_mi2s_be_hw_params_fixup, .ops = &msm_sdw_mi2s_be_ops, @@ -2929,7 +2935,7 @@ static struct snd_soc_dai_link ext_disp_be_dai_link[] = { .codec_dai_name = "msm_dp_audio_codec_rx_dai", .no_pcm = 1, .dpcm_playback = 1, - .be_id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, + .id = MSM_BACKEND_DAI_DISPLAY_PORT_RX, .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ignore_pmdown_time = 1, .ignore_suspend = 1, diff --git a/sound/soc/msm/sdm845.c b/sound/soc/msm/sdm845.c index d3c4e05c65c8..3be194cdc48d 100644 --- a/sound/soc/msm/sdm845.c +++ b/sound/soc/msm/sdm845.c @@ -495,6 +495,8 @@ 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(mi2s_rx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_rx_format, bit_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_tx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text); static struct platform_device *spdev; @@ -538,10 +540,10 @@ static struct wcd_mbhc_config wcd_mbhc_cfg = { }; 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"}, + {"MIC BIAS1", NULL, "MCLK TX"}, + {"MIC BIAS2", NULL, "MCLK TX"}, + {"MIC BIAS3", NULL, "MCLK TX"}, + {"MIC BIAS4", NULL, "MCLK TX"}, }; static struct afe_clk_set mi2s_clk[MI2S_MAX] = { @@ -2252,7 +2254,7 @@ static int mi2s_get_sample_rate(int value) return sample_rate; } -static int mi2s_get_format(int value) +static int mi2s_auxpcm_get_format(int value) { int format; @@ -2276,7 +2278,7 @@ static int mi2s_get_format(int value) return format; } -static int mi2s_get_format_value(int format) +static int mi2s_auxpcm_get_format_value(int format) { int value; @@ -2441,7 +2443,7 @@ static int msm_mi2s_rx_format_get(struct snd_kcontrol *kcontrol, return idx; ucontrol->value.enumerated.item[0] = - mi2s_get_format_value(mi2s_rx_cfg[idx].bit_format); + mi2s_auxpcm_get_format_value(mi2s_rx_cfg[idx].bit_format); pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, idx, mi2s_rx_cfg[idx].bit_format, @@ -2459,7 +2461,7 @@ static int msm_mi2s_rx_format_put(struct snd_kcontrol *kcontrol, return idx; mi2s_rx_cfg[idx].bit_format = - mi2s_get_format(ucontrol->value.enumerated.item[0]); + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, idx, mi2s_rx_cfg[idx].bit_format, @@ -2477,7 +2479,7 @@ static int msm_mi2s_tx_format_get(struct snd_kcontrol *kcontrol, return idx; ucontrol->value.enumerated.item[0] = - mi2s_get_format_value(mi2s_tx_cfg[idx].bit_format); + mi2s_auxpcm_get_format_value(mi2s_tx_cfg[idx].bit_format); pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, idx, mi2s_tx_cfg[idx].bit_format, @@ -2495,7 +2497,7 @@ static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol, return idx; mi2s_tx_cfg[idx].bit_format = - mi2s_get_format(ucontrol->value.enumerated.item[0]); + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, idx, mi2s_tx_cfg[idx].bit_format, @@ -2504,6 +2506,78 @@ static int msm_mi2s_tx_format_put(struct snd_kcontrol *kcontrol, return 0; } +static int msm_aux_pcm_rx_format_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] = + mi2s_auxpcm_get_format_value(aux_pcm_rx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_rx_format_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].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__, + idx, aux_pcm_rx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_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] = + mi2s_auxpcm_get_format_value(aux_pcm_tx_cfg[idx].bit_format); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_aux_pcm_tx_format_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].bit_format = + mi2s_auxpcm_get_format(ucontrol->value.enumerated.item[0]); + + pr_debug("%s: idx[%d]_tx_format = %d, item = %d\n", __func__, + idx, aux_pcm_tx_cfg[idx].bit_format, + ucontrol->value.enumerated.item[0]); + + return 0; +} + static int msm_hifi_ctrl(struct snd_soc_codec *codec) { struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); @@ -2765,6 +2839,22 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { msm_mi2s_rx_format_get, msm_mi2s_rx_format_put), SOC_ENUM_EXT("QUAT_MI2S_TX Format", mi2s_tx_format, msm_mi2s_tx_format_get, msm_mi2s_tx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("PRIM_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("SEC_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("TERT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_RX Format", aux_pcm_rx_format, + msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put), + SOC_ENUM_EXT("QUAT_AUX_PCM_TX Format", aux_pcm_tx_format, + msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get, msm_hifi_put), }; @@ -2784,6 +2874,38 @@ static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, 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), "tavil_codec")) { + ret = tavil_cdc_mclk_tx_enable(codec, enable); + } else { + dev_err(codec->dev, "%s: unknown codec to enable TX 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) { @@ -2840,7 +2962,7 @@ static const struct snd_soc_dapm_widget msm_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0, - NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + 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_2 amp", NULL), @@ -3134,6 +3256,8 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, break; case MSM_BACKEND_DAI_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[PRIM_AUX_PCM].bit_format); rate->min = rate->max = aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; channels->min = channels->max = @@ -3141,6 +3265,8 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, break; case MSM_BACKEND_DAI_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[PRIM_AUX_PCM].bit_format); rate->min = rate->max = aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate; channels->min = channels->max = @@ -3148,6 +3274,8 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, break; case MSM_BACKEND_DAI_SEC_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[SEC_AUX_PCM].bit_format); rate->min = rate->max = aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate; channels->min = channels->max = @@ -3155,6 +3283,8 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, break; case MSM_BACKEND_DAI_SEC_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[SEC_AUX_PCM].bit_format); rate->min = rate->max = aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate; channels->min = channels->max = @@ -3162,6 +3292,8 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, break; case MSM_BACKEND_DAI_TERT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[TERT_AUX_PCM].bit_format); rate->min = rate->max = aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate; channels->min = channels->max = @@ -3169,6 +3301,8 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, break; case MSM_BACKEND_DAI_TERT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[TERT_AUX_PCM].bit_format); rate->min = rate->max = aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate; channels->min = channels->max = @@ -3176,6 +3310,8 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, break; case MSM_BACKEND_DAI_QUAT_AUXPCM_RX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_rx_cfg[QUAT_AUX_PCM].bit_format); rate->min = rate->max = aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate; channels->min = channels->max = @@ -3183,6 +3319,8 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, break; case MSM_BACKEND_DAI_QUAT_AUXPCM_TX: + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + aux_pcm_tx_cfg[QUAT_AUX_PCM].bit_format); rate->min = rate->max = aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate; channels->min = channels->max = From 605b42f92c7892864eb0a4ad42c043f656281d76 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Tue, 1 Aug 2017 22:02:15 +0530 Subject: [PATCH 006/276] audio-lnx: Rename folders to new flat structure. Kernel audio drivers can be categorised into below folders. asoc - ALSA based drivers, asoc/codecs - codec drivers, ipc - APR IPC communication drivers, dsp - DSP low level drivers/Audio ION/ADSP Loader, dsp/codecs - Native encoders and decoders, soc - SoC based drivers(pinctrl/regmap/soundwire) Restructure drivers to above folder format. Include directories also follow above format. Change-Id: I8fa0857baaacd47db126fb5c1f1f5ed7e886dbc0 Signed-off-by: Laxminath Kasam --- Makefile | 6 +- {sound/soc/msm => asoc}/Makefile | 18 +- {sound/soc => asoc}/codecs/Makefile | 8 +- {sound/soc => asoc}/codecs/audio-ext-clk-up.c | 2 +- {sound/soc => asoc}/codecs/audio-ext-clk-up.h | 0 {sound/soc => asoc}/codecs/audio-ext-clk.c | 0 .../linux/mfd/wcd9xxx => asoc/codecs}/core.h | 0 {include/sound => asoc/codecs}/cpe_cmi.h | 0 {include/sound => asoc/codecs}/cpe_core.h | 0 {include/sound => asoc/codecs}/cpe_err.h | 0 .../mfd => asoc/codecs}/msm-cdc-pinctrl.c | 2 +- .../mfd => asoc/codecs}/msm-cdc-pinctrl.h | 0 {drivers/mfd => asoc/codecs}/msm-cdc-supply.c | 2 +- .../mfd => asoc/codecs}/msm-cdc-supply.h | 0 .../soc => asoc}/codecs/msm_hdmi_codec_rx.c | 0 {sound/soc => asoc}/codecs/msm_sdw/Makefile | 0 .../codecs/msm_sdw/msm-sdw-tables.c | 0 {sound/soc => asoc}/codecs/msm_sdw/msm_sdw.h | 0 .../soc => asoc}/codecs/msm_sdw/msm_sdw_cdc.c | 0 .../codecs/msm_sdw/msm_sdw_cdc_utils.c | 0 .../codecs/msm_sdw/msm_sdw_registers.h | 0 .../codecs/msm_sdw/msm_sdw_regmap.c | 0 {sound/soc => asoc}/codecs/msm_stub.c | 0 .../linux/mfd/wcd9xxx => asoc/codecs}/pdata.h | 2 +- .../soc => asoc}/codecs/sdm660_cdc/Makefile | 0 .../codecs/sdm660_cdc/msm-analog-cdc.c | 12 +- .../codecs/sdm660_cdc/msm-analog-cdc.h | 2 +- .../codecs/sdm660_cdc/msm-cdc-common.h | 0 .../codecs/sdm660_cdc/msm-digital-cdc.c | 8 +- .../codecs/sdm660_cdc/msm-digital-cdc.h | 0 .../codecs/sdm660_cdc/sdm660-cdc-irq.c | 0 .../codecs/sdm660_cdc/sdm660-cdc-irq.h | 0 .../codecs/sdm660_cdc/sdm660-cdc-registers.h | 0 .../codecs/sdm660_cdc/sdm660-regmap.c | 0 {sound/soc => asoc}/codecs/wcd-dsp-mgr.c | 0 {sound/soc => asoc}/codecs/wcd-dsp-utils.c | 0 {sound/soc => asoc}/codecs/wcd-dsp-utils.h | 0 {sound/soc => asoc}/codecs/wcd-mbhc-adc.c | 0 {sound/soc => asoc}/codecs/wcd-mbhc-adc.h | 0 {sound/soc => asoc}/codecs/wcd-mbhc-legacy.c | 0 {sound/soc => asoc}/codecs/wcd-mbhc-legacy.h | 0 {sound/soc => asoc}/codecs/wcd-mbhc-v2-api.h | 0 {sound/soc => asoc}/codecs/wcd-mbhc-v2.c | 2 +- {sound/soc => asoc}/codecs/wcd-mbhc-v2.h | 0 .../soc => asoc}/codecs/wcd-spi-registers.h | 0 {sound/soc => asoc}/codecs/wcd-spi.c | 0 {drivers/mfd => asoc/codecs}/wcd9335-regmap.c | 4 +- {drivers/mfd => asoc/codecs}/wcd9335-tables.c | 2 +- {sound/soc => asoc}/codecs/wcd9335.c | 14 +- {sound/soc => asoc}/codecs/wcd9335.h | 4 +- .../irq.h => asoc/codecs/wcd9335_irq.h | 0 .../codecs/wcd9335_registers.h | 0 {sound/soc => asoc}/codecs/wcd934x/Makefile | 3 +- .../soc => asoc}/codecs/wcd934x/wcd934x-dsd.c | 2 +- .../soc => asoc}/codecs/wcd934x/wcd934x-dsd.h | 0 .../codecs/wcd934x/wcd934x-dsp-cntl.c | 6 +- .../codecs/wcd934x/wcd934x-dsp-cntl.h | 0 .../codecs/wcd934x/wcd934x-mbhc.c | 10 +- .../codecs/wcd934x/wcd934x-mbhc.h | 0 .../codecs/wcd934x}/wcd934x-regmap.c | 6 +- .../codecs/wcd934x/wcd934x-routing.h | 0 .../codecs/wcd934x}/wcd934x-tables.c | 2 +- {sound/soc => asoc}/codecs/wcd934x/wcd934x.c | 14 +- {sound/soc => asoc}/codecs/wcd934x/wcd934x.h | 4 +- .../codecs/wcd934x/wcd934x_irq.h | 0 .../soc => asoc}/codecs/wcd9xxx-common-v2.c | 2 +- .../soc => asoc}/codecs/wcd9xxx-common-v2.h | 0 .../mfd => asoc/codecs}/wcd9xxx-core-init.c | 6 +- {drivers/mfd => asoc/codecs}/wcd9xxx-core.c | 16 +- {drivers/mfd => asoc/codecs}/wcd9xxx-irq.c | 6 +- .../mfd/wcd9xxx => asoc/codecs}/wcd9xxx-irq.h | 2 +- {drivers/mfd => asoc/codecs}/wcd9xxx-regmap.h | 2 +- .../soc => asoc}/codecs/wcd9xxx-resmgr-v2.c | 6 +- .../soc => asoc}/codecs/wcd9xxx-resmgr-v2.h | 2 +- {drivers/mfd => asoc/codecs}/wcd9xxx-rst.c | 14 +- .../mfd => asoc/codecs}/wcd9xxx-slimslave.c | 2 +- .../codecs}/wcd9xxx-slimslave.h | 8 +- {sound/soc => asoc}/codecs/wcd9xxx-soc-init.c | 0 {drivers/mfd => asoc/codecs}/wcd9xxx-utils.c | 12 +- .../wcd9xxx => asoc/codecs}/wcd9xxx-utils.h | 4 +- {sound/soc => asoc}/codecs/wcd_cmi_api.h | 0 {sound/soc => asoc}/codecs/wcd_cpe_core.c | 10 +- {sound/soc => asoc}/codecs/wcd_cpe_core.h | 0 {sound/soc => asoc}/codecs/wcd_cpe_services.c | 5 +- {sound/soc => asoc}/codecs/wcd_cpe_services.h | 0 {sound/soc => asoc}/codecs/wcdcal-hwdep.c | 0 {sound/soc => asoc}/codecs/wcdcal-hwdep.h | 0 {sound/soc => asoc}/codecs/wsa881x-analog.c | 0 {sound/soc => asoc}/codecs/wsa881x-analog.h | 0 {sound/soc => asoc}/codecs/wsa881x-irq.c | 0 {sound/soc => asoc}/codecs/wsa881x-irq.h | 0 .../codecs/wsa881x-registers-analog.h | 0 .../soc => asoc}/codecs/wsa881x-registers.h | 0 .../codecs/wsa881x-regmap-analog.c | 0 {sound/soc => asoc}/codecs/wsa881x-regmap.c | 0 .../codecs/wsa881x-tables-analog.c | 0 {sound/soc => asoc}/codecs/wsa881x-tables.c | 0 .../soc => asoc}/codecs/wsa881x-temp-sensor.c | 0 .../soc => asoc}/codecs/wsa881x-temp-sensor.h | 0 {sound/soc => asoc}/codecs/wsa881x.c | 4 +- {sound/soc => asoc}/codecs/wsa881x.h | 0 {sound/soc/msm => asoc}/device_event.h | 0 .../msm-audio-effects-q6-v2.c | 6 +- {sound/soc/msm => asoc}/msm-audio-pinctrl.c | 0 {sound/soc/msm => asoc}/msm-audio-pinctrl.h | 0 .../msm/qdsp6v2 => asoc}/msm-compress-q6-v2.c | 11 +- {sound/soc/msm => asoc}/msm-cpe-lsm.c | 4 +- {sound/soc/msm => asoc}/msm-dai-fe.c | 0 .../msm/qdsp6v2 => asoc}/msm-dai-q6-hdmi-v2.c | 6 +- .../soc/msm/qdsp6v2 => asoc}/msm-dai-q6-v2.c | 8 +- {include/sound => asoc}/msm-dai-q6-v2.h | 0 .../soc/msm/qdsp6v2 => asoc}/msm-dai-slim.c | 2 +- .../msm/qdsp6v2 => asoc}/msm-dai-stub-v2.c | 0 .../msm/qdsp6v2 => asoc}/msm-dolby-common.h | 1 - .../qdsp6v2 => asoc}/msm-dolby-dap-config.h | 1 - .../msm/qdsp6v2 => asoc}/msm-ds2-dap-config.c | 4 +- .../msm/qdsp6v2 => asoc}/msm-ds2-dap-config.h | 1 - .../qdsp6v2 => asoc}/msm-dts-srs-tm-config.c | 8 +- .../soc/msm/qdsp6v2 => asoc}/msm-lsm-client.c | 4 +- .../soc/msm/qdsp6v2 => asoc}/msm-pcm-afe-v2.c | 4 +- .../soc/msm/qdsp6v2 => asoc}/msm-pcm-afe-v2.h | 4 +- .../msm/qdsp6v2 => asoc}/msm-pcm-dtmf-v2.c | 4 +- .../qdsp6v2 => asoc}/msm-pcm-host-voice-v2.c | 4 +- {sound/soc/msm => asoc}/msm-pcm-hostless.c | 0 .../qdsp6v2 => asoc}/msm-pcm-loopback-v2.c | 6 +- .../msm/qdsp6v2 => asoc}/msm-pcm-q6-noirq.c | 4 +- .../soc/msm/qdsp6v2 => asoc}/msm-pcm-q6-v2.c | 4 +- .../soc/msm/qdsp6v2 => asoc}/msm-pcm-q6-v2.h | 4 +- .../qdsp6v2 => asoc}/msm-pcm-routing-devdep.c | 0 .../qdsp6v2 => asoc}/msm-pcm-routing-devdep.h | 0 .../msm/qdsp6v2 => asoc}/msm-pcm-routing-v2.c | 18 +- .../msm/qdsp6v2 => asoc}/msm-pcm-routing-v2.h | 2 +- .../msm/qdsp6v2 => asoc}/msm-pcm-voice-v2.c | 2 +- .../msm/qdsp6v2 => asoc}/msm-pcm-voice-v2.h | 2 +- .../msm/qdsp6v2 => asoc}/msm-pcm-voip-v2.c | 2 +- .../msm/qdsp6v2 => asoc}/msm-qti-pp-config.c | 8 +- .../msm/qdsp6v2 => asoc}/msm-qti-pp-config.h | 1 - {include/sound => asoc}/msm-slim-dma.h | 0 .../msm-transcode-loopback-q6-v2.c | 7 +- {sound/soc/msm => asoc}/msm8996.c | 0 {sound/soc/msm => asoc}/msm8998.c | 18 +- {sound/soc/msm => asoc}/sdm660-common.c | 10 +- {sound/soc/msm => asoc}/sdm660-common.h | 4 +- .../soc/msm => asoc}/sdm660-ext-dai-links.c | 4 +- {sound/soc/msm => asoc}/sdm660-external.c | 12 +- {sound/soc/msm => asoc}/sdm660-external.h | 0 {sound/soc/msm => asoc}/sdm660-internal.c | 10 +- {sound/soc/msm => asoc}/sdm660-internal.h | 0 {sound/soc/msm => asoc}/sdm845.c | 16 +- drivers/Makefile | 7 - drivers/base/Makefile | 2 - drivers/base/regmap/Makefile | 2 - drivers/base/regmap/internal.h | 1 - drivers/mfd/Makefile | 8 - drivers/misc/Makefile | 5 - drivers/misc/qcom/Kconfig | 19 - drivers/misc/qcom/Makefile | 1 - drivers/misc/qcom/qdsp6v2/ultrasound/Makefile | 2 - drivers/pinctrl/Makefile | 2 - drivers/pinctrl/core.h | 1 - drivers/pinctrl/pinctrl-utils.h | 1 - drivers/pinctrl/qcom/Makefile | 3 - drivers/soc/Makefile | 5 - drivers/soc/qcom/Makefile | 1 - drivers/soc/qcom/qdsp6v2/Makefile | 8 - drivers/soundwire/Kconfig | 17 - drivers/soundwire/Makefile | 5 - dsp/Makefile | 10 + .../soc/qcom/qdsp6v2 => dsp}/adsp-loader.c | 2 +- {sound/soc/msm/qdsp6v2 => dsp}/adsp_err.c | 2 +- .../qdsp6v2/dsp_debug.h => dsp/adsp_err.h | 17 +- .../soc/msm/qdsp6v2 => dsp}/audio_cal_utils.c | 2 +- .../msm/qdsp6v2 => dsp}/audio_calibration.c | 6 +- .../soc/qcom/qdsp6v2 => dsp}/audio_notifier.c | 6 +- {drivers/soc/qcom/qdsp6v2 => dsp}/audio_pdr.c | 2 +- {include/linux/qdsp6v2 => dsp}/audio_pdr.h | 0 .../soc/msm/qdsp6v2 => dsp}/audio_slimslave.c | 0 {drivers/soc/qcom/qdsp6v2 => dsp}/audio_ssr.c | 2 +- {include/linux/qdsp6v2 => dsp}/audio_ssr.h | 0 .../soc/qcom/qdsp6v2 => dsp}/cdsp-loader.c | 0 .../misc/qcom/qdsp6v2 => dsp/codecs}/Makefile | 1 - .../misc/qcom/qdsp6v2 => dsp/codecs}/aac_in.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/amrnb_in.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/amrwb_in.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/audio_aac.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/audio_alac.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/audio_amrnb.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/audio_amrwb.c | 0 .../qdsp6v2 => dsp/codecs}/audio_amrwbplus.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/audio_ape.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/audio_evrc.c | 0 .../qdsp6v2 => dsp/codecs}/audio_g711alaw.c | 0 .../qdsp6v2 => dsp/codecs}/audio_g711mlaw.c | 0 .../codecs}/audio_hwacc_effects.c | 2 +- .../qcom/qdsp6v2 => dsp/codecs}/audio_mp3.c | 0 .../qdsp6v2 => dsp/codecs}/audio_multi_aac.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/audio_qcelp.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/audio_utils.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/audio_utils.h | 0 .../qdsp6v2 => dsp/codecs}/audio_utils_aio.c | 2 +- .../qdsp6v2 => dsp/codecs}/audio_utils_aio.h | 0 .../qcom/qdsp6v2 => dsp/codecs}/audio_wma.c | 0 .../qdsp6v2 => dsp/codecs}/audio_wmapro.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/evrc_in.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/g711alaw_in.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/g711mlaw_in.c | 0 .../qdsp6v2 => dsp/codecs}/q6audio_common.h | 4 +- .../qcom/qdsp6v2 => dsp/codecs}/q6audio_v2.c | 0 .../qdsp6v2 => dsp/codecs}/q6audio_v2_aio.c | 0 .../qcom/qdsp6v2 => dsp/codecs}/qcelp_in.c | 0 .../soc/qcom/qdsp6v2 => dsp}/msm_audio_ion.c | 4 +- {sound/soc/msm/qdsp6v2 => dsp}/q6adm.c | 16 +- {sound/soc/msm/qdsp6v2 => dsp}/q6afe.c | 15 +- {sound/soc/msm/qdsp6v2 => dsp}/q6asm.c | 14 +- {sound/soc/msm/qdsp6v2 => dsp}/q6audio-v2.c | 4 +- {sound/soc/msm/qdsp6v2 => dsp}/q6core.c | 6 +- {sound/soc/msm/qdsp6v2 => dsp}/q6lsm.c | 14 +- .../qcom/qdsp6v2/ultrasound => dsp}/q6usm.c | 7 +- .../qcom/qdsp6v2/ultrasound => dsp}/q6usm.h | 2 +- {sound/soc/msm/qdsp6v2 => dsp}/q6voice.c | 18 +- {sound/soc/msm/qdsp6v2 => dsp}/rtac.c | 17 +- .../qcom/qdsp6v2/ultrasound => dsp}/usf.c | 4 +- {include/linux/qdsp6v2 => dsp}/usf.h | 0 .../qcom/qdsp6v2/ultrasound => dsp}/usfcdev.c | 0 .../qcom/qdsp6v2/ultrasound => dsp}/usfcdev.h | 0 .../asoc}/msm-dts-srs-tm-config.h | 2 +- .../registers.h => asoc/wcd934x_registers.h} | 0 include/{sound => dsp}/apr_audio-v2.h | 2 +- include/{sound => dsp}/audio_cal_utils.h | 4 +- include/{sound => dsp}/audio_calibration.h | 0 .../{linux/qdsp6v2 => dsp}/audio_notifier.h | 0 .../{sound => dsp}/msm-audio-effects-q6-v2.h | 0 include/dsp/msm_audio_ion.h | 45 + include/{sound => dsp}/q6adm-v2.h | 6 +- include/{sound => dsp}/q6afe-v2.h | 4 +- include/{sound => dsp}/q6asm-v2.h | 6 +- include/{sound => dsp}/q6audio-v2.h | 2 +- include/{sound => dsp}/q6core.h | 2 +- include/{sound => dsp}/q6lsm.h | 4 +- .../soc/msm/qdsp6v2 => include/dsp}/q6voice.h | 4 +- include/{linux/qdsp6v2 => dsp}/rtac.h | 3 +- include/{linux/qdsp6v2 => ipc}/apr.h | 0 include/{linux/qdsp6v2 => ipc}/apr_tal.h | 0 include/{linux/qdsp6v2 => ipc}/apr_us.h | 2 +- include/linux/avtimer_kernel.h | 24 - include/soc/internal.h | 1 + include/{linux/soundwire => soc}/soundwire.h | 0 include/{linux/soundwire => soc}/swr-wcd.h | 0 include/sound/apr_audio.h | 1931 ----------------- ipc/Makefile | 2 + {drivers/soc/qcom/qdsp6v2 => ipc}/apr.c | 11 +- .../soc/qcom/qdsp6v2 => ipc}/apr_tal_glink.c | 2 +- {drivers/soc/qcom/qdsp6v2 => ipc}/apr_v2.c | 7 +- {drivers/soc/qcom/qdsp6v2 => ipc}/apr_v3.c | 7 +- soc/Makefile | 6 + soc/core.h | 1 + {drivers/pinctrl/qcom => soc}/pinctrl-lpi.c | 6 +- soc/pinctrl-utils.h | 1 + {drivers/pinctrl/qcom => soc}/pinctrl-wcd.c | 6 +- {drivers/base/regmap => soc}/regmap-swr.c | 4 +- {drivers/soundwire => soc}/soundwire.c | 2 +- {drivers/soundwire => soc}/swr-wcd-ctrl.c | 4 +- {drivers/soundwire => soc}/swr-wcd-ctrl.h | 2 +- {drivers/soundwire => soc}/swrm_registers.h | 0 sound/Makefile | 4 - sound/soc/codecs/msm_sdw/Kconfig | 6 - sound/soc/codecs/sdm660_cdc/Kconfig | 5 - sound/soc/msm/Kconfig | 283 --- sound/soc/msm/qdsp6v2/Makefile | 19 - 269 files changed, 424 insertions(+), 2707 deletions(-) rename {sound/soc/msm => asoc}/Makefile (61%) rename {sound/soc => asoc}/codecs/Makefile (80%) rename {sound/soc => asoc}/codecs/audio-ext-clk-up.c (99%) rename {sound/soc => asoc}/codecs/audio-ext-clk-up.h (100%) rename {sound/soc => asoc}/codecs/audio-ext-clk.c (100%) rename {include/linux/mfd/wcd9xxx => asoc/codecs}/core.h (100%) rename {include/sound => asoc/codecs}/cpe_cmi.h (100%) rename {include/sound => asoc/codecs}/cpe_core.h (100%) rename {include/sound => asoc/codecs}/cpe_err.h (100%) rename {drivers/mfd => asoc/codecs}/msm-cdc-pinctrl.c (99%) rename {include/linux/mfd => asoc/codecs}/msm-cdc-pinctrl.h (100%) rename {drivers/mfd => asoc/codecs}/msm-cdc-supply.c (99%) rename {include/linux/mfd => asoc/codecs}/msm-cdc-supply.h (100%) rename {sound/soc => asoc}/codecs/msm_hdmi_codec_rx.c (100%) rename {sound/soc => asoc}/codecs/msm_sdw/Makefile (100%) rename {sound/soc => asoc}/codecs/msm_sdw/msm-sdw-tables.c (100%) rename {sound/soc => asoc}/codecs/msm_sdw/msm_sdw.h (100%) rename {sound/soc => asoc}/codecs/msm_sdw/msm_sdw_cdc.c (100%) rename {sound/soc => asoc}/codecs/msm_sdw/msm_sdw_cdc_utils.c (100%) rename {sound/soc => asoc}/codecs/msm_sdw/msm_sdw_registers.h (100%) rename {sound/soc => asoc}/codecs/msm_sdw/msm_sdw_regmap.c (100%) rename {sound/soc => asoc}/codecs/msm_stub.c (100%) rename {include/linux/mfd/wcd9xxx => asoc/codecs}/pdata.h (99%) rename {sound/soc => asoc}/codecs/sdm660_cdc/Makefile (100%) rename {sound/soc => asoc}/codecs/sdm660_cdc/msm-analog-cdc.c (99%) rename {sound/soc => asoc}/codecs/sdm660_cdc/msm-analog-cdc.h (99%) rename {sound/soc => asoc}/codecs/sdm660_cdc/msm-cdc-common.h (100%) rename {sound/soc => asoc}/codecs/sdm660_cdc/msm-digital-cdc.c (99%) rename {sound/soc => asoc}/codecs/sdm660_cdc/msm-digital-cdc.h (100%) rename {sound/soc => asoc}/codecs/sdm660_cdc/sdm660-cdc-irq.c (100%) rename {sound/soc => asoc}/codecs/sdm660_cdc/sdm660-cdc-irq.h (100%) rename {sound/soc => asoc}/codecs/sdm660_cdc/sdm660-cdc-registers.h (100%) rename {sound/soc => asoc}/codecs/sdm660_cdc/sdm660-regmap.c (100%) rename {sound/soc => asoc}/codecs/wcd-dsp-mgr.c (100%) rename {sound/soc => asoc}/codecs/wcd-dsp-utils.c (100%) rename {sound/soc => asoc}/codecs/wcd-dsp-utils.h (100%) rename {sound/soc => asoc}/codecs/wcd-mbhc-adc.c (100%) rename {sound/soc => asoc}/codecs/wcd-mbhc-adc.h (100%) rename {sound/soc => asoc}/codecs/wcd-mbhc-legacy.c (100%) rename {sound/soc => asoc}/codecs/wcd-mbhc-legacy.h (100%) rename {sound/soc => asoc}/codecs/wcd-mbhc-v2-api.h (100%) rename {sound/soc => asoc}/codecs/wcd-mbhc-v2.c (99%) rename {sound/soc => asoc}/codecs/wcd-mbhc-v2.h (100%) rename {sound/soc => asoc}/codecs/wcd-spi-registers.h (100%) rename {sound/soc => asoc}/codecs/wcd-spi.c (100%) rename {drivers/mfd => asoc/codecs}/wcd9335-regmap.c (99%) rename {drivers/mfd => asoc/codecs}/wcd9335-tables.c (99%) rename {sound/soc => asoc}/codecs/wcd9335.c (99%) rename {sound/soc => asoc}/codecs/wcd9335.h (98%) rename include/linux/mfd/wcd9335/irq.h => asoc/codecs/wcd9335_irq.h (100%) rename include/linux/mfd/wcd9335/registers.h => asoc/codecs/wcd9335_registers.h (100%) rename {sound/soc => asoc}/codecs/wcd934x/Makefile (67%) rename {sound/soc => asoc}/codecs/wcd934x/wcd934x-dsd.c (99%) rename {sound/soc => asoc}/codecs/wcd934x/wcd934x-dsd.h (100%) rename {sound/soc => asoc}/codecs/wcd934x/wcd934x-dsp-cntl.c (99%) rename {sound/soc => asoc}/codecs/wcd934x/wcd934x-dsp-cntl.h (100%) rename {sound/soc => asoc}/codecs/wcd934x/wcd934x-mbhc.c (99%) rename {sound/soc => asoc}/codecs/wcd934x/wcd934x-mbhc.h (100%) rename {drivers/mfd => asoc/codecs/wcd934x}/wcd934x-regmap.c (99%) rename {sound/soc => asoc}/codecs/wcd934x/wcd934x-routing.h (100%) rename {drivers/mfd => asoc/codecs/wcd934x}/wcd934x-tables.c (99%) rename {sound/soc => asoc}/codecs/wcd934x/wcd934x.c (99%) rename {sound/soc => asoc}/codecs/wcd934x/wcd934x.h (98%) rename include/linux/mfd/wcd934x/irq.h => asoc/codecs/wcd934x/wcd934x_irq.h (100%) rename {sound/soc => asoc}/codecs/wcd9xxx-common-v2.c (99%) rename {sound/soc => asoc}/codecs/wcd9xxx-common-v2.h (100%) rename {drivers/mfd => asoc/codecs}/wcd9xxx-core-init.c (92%) rename {drivers/mfd => asoc/codecs}/wcd9xxx-core.c (99%) rename {drivers/mfd => asoc/codecs}/wcd9xxx-irq.c (99%) rename {include/linux/mfd/wcd9xxx => asoc/codecs}/wcd9xxx-irq.h (97%) rename {drivers/mfd => asoc/codecs}/wcd9xxx-regmap.h (97%) rename {sound/soc => asoc}/codecs/wcd9xxx-resmgr-v2.c (99%) rename {sound/soc => asoc}/codecs/wcd9xxx-resmgr-v2.h (98%) rename {drivers/mfd => asoc/codecs}/wcd9xxx-rst.c (97%) rename {drivers/mfd => asoc/codecs}/wcd9xxx-slimslave.c (99%) rename {include/linux/mfd/wcd9xxx => asoc/codecs}/wcd9xxx-slimslave.h (96%) rename {sound/soc => asoc}/codecs/wcd9xxx-soc-init.c (100%) rename {drivers/mfd => asoc/codecs}/wcd9xxx-utils.c (99%) rename {include/linux/mfd/wcd9xxx => asoc/codecs}/wcd9xxx-utils.h (95%) rename {sound/soc => asoc}/codecs/wcd_cmi_api.h (100%) rename {sound/soc => asoc}/codecs/wcd_cpe_core.c (99%) rename {sound/soc => asoc}/codecs/wcd_cpe_core.h (100%) rename {sound/soc => asoc}/codecs/wcd_cpe_services.c (99%) rename {sound/soc => asoc}/codecs/wcd_cpe_services.h (100%) rename {sound/soc => asoc}/codecs/wcdcal-hwdep.c (100%) rename {sound/soc => asoc}/codecs/wcdcal-hwdep.h (100%) rename {sound/soc => asoc}/codecs/wsa881x-analog.c (100%) rename {sound/soc => asoc}/codecs/wsa881x-analog.h (100%) rename {sound/soc => asoc}/codecs/wsa881x-irq.c (100%) rename {sound/soc => asoc}/codecs/wsa881x-irq.h (100%) rename {sound/soc => asoc}/codecs/wsa881x-registers-analog.h (100%) rename {sound/soc => asoc}/codecs/wsa881x-registers.h (100%) rename {sound/soc => asoc}/codecs/wsa881x-regmap-analog.c (100%) rename {sound/soc => asoc}/codecs/wsa881x-regmap.c (100%) rename {sound/soc => asoc}/codecs/wsa881x-tables-analog.c (100%) rename {sound/soc => asoc}/codecs/wsa881x-tables.c (100%) rename {sound/soc => asoc}/codecs/wsa881x-temp-sensor.c (100%) rename {sound/soc => asoc}/codecs/wsa881x-temp-sensor.h (100%) rename {sound/soc => asoc}/codecs/wsa881x.c (99%) rename {sound/soc => asoc}/codecs/wsa881x.h (100%) rename {sound/soc/msm => asoc}/device_event.h (100%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-audio-effects-q6-v2.c (99%) rename {sound/soc/msm => asoc}/msm-audio-pinctrl.c (100%) rename {sound/soc/msm => asoc}/msm-audio-pinctrl.h (100%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-compress-q6-v2.c (99%) rename {sound/soc/msm => asoc}/msm-cpe-lsm.c (99%) rename {sound/soc/msm => asoc}/msm-dai-fe.c (100%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-dai-q6-hdmi-v2.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-dai-q6-v2.c (99%) rename {include/sound => asoc}/msm-dai-q6-v2.h (100%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-dai-slim.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-dai-stub-v2.c (100%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-dolby-common.h (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-dolby-dap-config.h (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-ds2-dap-config.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-ds2-dap-config.h (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-dts-srs-tm-config.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-lsm-client.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-afe-v2.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-afe-v2.h (95%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-dtmf-v2.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-host-voice-v2.c (99%) rename {sound/soc/msm => asoc}/msm-pcm-hostless.c (100%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-loopback-v2.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-q6-noirq.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-q6-v2.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-q6-v2.h (98%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-routing-devdep.c (100%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-routing-devdep.h (100%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-routing-v2.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-routing-v2.h (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-voice-v2.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-voice-v2.h (97%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-pcm-voip-v2.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-qti-pp-config.c (99%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-qti-pp-config.h (99%) rename {include/sound => asoc}/msm-slim-dma.h (100%) rename {sound/soc/msm/qdsp6v2 => asoc}/msm-transcode-loopback-q6-v2.c (99%) rename {sound/soc/msm => asoc}/msm8996.c (100%) rename {sound/soc/msm => asoc}/msm8998.c (99%) rename {sound/soc/msm => asoc}/sdm660-common.c (99%) rename {sound/soc/msm => asoc}/sdm660-common.h (98%) rename {sound/soc/msm => asoc}/sdm660-ext-dai-links.c (99%) rename {sound/soc/msm => asoc}/sdm660-external.c (99%) rename {sound/soc/msm => asoc}/sdm660-external.h (100%) rename {sound/soc/msm => asoc}/sdm660-internal.c (99%) rename {sound/soc/msm => asoc}/sdm660-internal.h (100%) rename {sound/soc/msm => asoc}/sdm845.c (99%) delete mode 100644 drivers/Makefile delete mode 100644 drivers/base/Makefile delete mode 100644 drivers/base/regmap/Makefile delete mode 120000 drivers/base/regmap/internal.h delete mode 100644 drivers/mfd/Makefile delete mode 100644 drivers/misc/Makefile delete mode 100644 drivers/misc/qcom/Kconfig delete mode 100644 drivers/misc/qcom/Makefile delete mode 100644 drivers/misc/qcom/qdsp6v2/ultrasound/Makefile delete mode 100644 drivers/pinctrl/Makefile delete mode 120000 drivers/pinctrl/core.h delete mode 120000 drivers/pinctrl/pinctrl-utils.h delete mode 100644 drivers/pinctrl/qcom/Makefile delete mode 100644 drivers/soc/Makefile delete mode 100644 drivers/soc/qcom/Makefile delete mode 100644 drivers/soc/qcom/qdsp6v2/Makefile delete mode 100644 drivers/soundwire/Kconfig delete mode 100644 drivers/soundwire/Makefile create mode 100644 dsp/Makefile rename {drivers/soc/qcom/qdsp6v2 => dsp}/adsp-loader.c (99%) rename {sound/soc/msm/qdsp6v2 => dsp}/adsp_err.c (99%) rename include/linux/qdsp6v2/dsp_debug.h => dsp/adsp_err.h (54%) rename {sound/soc/msm/qdsp6v2 => dsp}/audio_cal_utils.c (99%) rename {sound/soc/msm/qdsp6v2 => dsp}/audio_calibration.c (99%) rename {drivers/soc/qcom/qdsp6v2 => dsp}/audio_notifier.c (99%) rename {drivers/soc/qcom/qdsp6v2 => dsp}/audio_pdr.c (99%) rename {include/linux/qdsp6v2 => dsp}/audio_pdr.h (100%) rename {sound/soc/msm/qdsp6v2 => dsp}/audio_slimslave.c (100%) rename {drivers/soc/qcom/qdsp6v2 => dsp}/audio_ssr.c (98%) rename {include/linux/qdsp6v2 => dsp}/audio_ssr.h (100%) rename {drivers/soc/qcom/qdsp6v2 => dsp}/cdsp-loader.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/Makefile (92%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/aac_in.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/amrnb_in.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/amrwb_in.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_aac.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_alac.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_amrnb.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_amrwb.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_amrwbplus.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_ape.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_evrc.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_g711alaw.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_g711mlaw.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_hwacc_effects.c (99%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_mp3.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_multi_aac.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_qcelp.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_utils.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_utils.h (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_utils_aio.c (99%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_utils_aio.h (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_wma.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/audio_wmapro.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/evrc_in.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/g711alaw_in.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/g711mlaw_in.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/q6audio_common.h (94%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/q6audio_v2.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/q6audio_v2_aio.c (100%) rename {drivers/misc/qcom/qdsp6v2 => dsp/codecs}/qcelp_in.c (100%) rename {drivers/soc/qcom/qdsp6v2 => dsp}/msm_audio_ion.c (99%) rename {sound/soc/msm/qdsp6v2 => dsp}/q6adm.c (99%) rename {sound/soc/msm/qdsp6v2 => dsp}/q6afe.c (99%) rename {sound/soc/msm/qdsp6v2 => dsp}/q6asm.c (99%) rename {sound/soc/msm/qdsp6v2 => dsp}/q6audio-v2.c (99%) rename {sound/soc/msm/qdsp6v2 => dsp}/q6core.c (99%) rename {sound/soc/msm/qdsp6v2 => dsp}/q6lsm.c (99%) rename {drivers/misc/qcom/qdsp6v2/ultrasound => dsp}/q6usm.c (99%) rename {drivers/misc/qcom/qdsp6v2/ultrasound => dsp}/q6usm.h (99%) rename {sound/soc/msm/qdsp6v2 => dsp}/q6voice.c (99%) rename {sound/soc/msm/qdsp6v2 => dsp}/rtac.c (99%) rename {drivers/misc/qcom/qdsp6v2/ultrasound => dsp}/usf.c (99%) rename {include/linux/qdsp6v2 => dsp}/usf.h (100%) rename {drivers/misc/qcom/qdsp6v2/ultrasound => dsp}/usfcdev.c (100%) rename {drivers/misc/qcom/qdsp6v2/ultrasound => dsp}/usfcdev.h (100%) rename {sound/soc/msm/qdsp6v2 => include/asoc}/msm-dts-srs-tm-config.h (97%) rename include/{linux/mfd/wcd934x/registers.h => asoc/wcd934x_registers.h} (100%) rename include/{sound => dsp}/apr_audio-v2.h (99%) rename include/{sound => dsp}/audio_cal_utils.h (97%) rename include/{sound => dsp}/audio_calibration.h (100%) rename include/{linux/qdsp6v2 => dsp}/audio_notifier.h (100%) rename include/{sound => dsp}/msm-audio-effects-q6-v2.h (100%) create mode 100644 include/dsp/msm_audio_ion.h rename include/{sound => dsp}/q6adm-v2.h (98%) rename include/{sound => dsp}/q6afe-v2.h (99%) rename include/{sound => dsp}/q6asm-v2.h (99%) rename include/{sound => dsp}/q6audio-v2.h (96%) rename include/{sound => dsp}/q6core.h (99%) rename include/{sound => dsp}/q6lsm.h (99%) rename {sound/soc/msm/qdsp6v2 => include/dsp}/q6voice.h (99%) rename include/{linux/qdsp6v2 => dsp}/rtac.h (98%) rename include/{linux/qdsp6v2 => ipc}/apr.h (100%) rename include/{linux/qdsp6v2 => ipc}/apr_tal.h (100%) rename include/{linux/qdsp6v2 => ipc}/apr_us.h (99%) delete mode 100644 include/linux/avtimer_kernel.h create mode 120000 include/soc/internal.h rename include/{linux/soundwire => soc}/soundwire.h (100%) rename include/{linux/soundwire => soc}/swr-wcd.h (100%) delete mode 100644 include/sound/apr_audio.h create mode 100644 ipc/Makefile rename {drivers/soc/qcom/qdsp6v2 => ipc}/apr.c (99%) rename {drivers/soc/qcom/qdsp6v2 => ipc}/apr_tal_glink.c (99%) rename {drivers/soc/qcom/qdsp6v2 => ipc}/apr_v2.c (91%) rename {drivers/soc/qcom/qdsp6v2 => ipc}/apr_v3.c (90%) create mode 100644 soc/Makefile create mode 120000 soc/core.h rename {drivers/pinctrl/qcom => soc}/pinctrl-lpi.c (99%) create mode 120000 soc/pinctrl-utils.h rename {drivers/pinctrl/qcom => soc}/pinctrl-wcd.c (99%) rename {drivers/base/regmap => soc}/regmap-swr.c (98%) rename {drivers/soundwire => soc}/soundwire.c (99%) rename {drivers/soundwire => soc}/swr-wcd-ctrl.c (99%) rename {drivers/soundwire => soc}/swr-wcd-ctrl.h (98%) rename {drivers/soundwire => soc}/swrm_registers.h (100%) delete mode 100644 sound/Makefile delete mode 100644 sound/soc/codecs/msm_sdw/Kconfig delete mode 100644 sound/soc/codecs/sdm660_cdc/Kconfig delete mode 100644 sound/soc/msm/Kconfig delete mode 100644 sound/soc/msm/qdsp6v2/Makefile diff --git a/Makefile b/Makefile index b8428deb5c05..05b04c637301 100644 --- a/Makefile +++ b/Makefile @@ -19,5 +19,7 @@ LINUXINCLUDE += \ -include $(srctree)/techpack/audio/config/sdm845autoconf.h endif -obj-y += drivers/ -obj-y += sound/ +obj-y += asoc/ +obj-y += dsp/ +obj-y += ipc/ +obj-y += soc/ diff --git a/sound/soc/msm/Makefile b/asoc/Makefile similarity index 61% rename from sound/soc/msm/Makefile rename to asoc/Makefile index caf884322d9e..67cb9f3aac6b 100644 --- a/sound/soc/msm/Makefile +++ b/asoc/Makefile @@ -3,7 +3,6 @@ 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 @@ -37,3 +36,20 @@ obj-$(CONFIG_SND_SOC_EXT_CODEC) += snd-soc-ext-codec.o # for SDM845 sound card driver snd-soc-sdm845-objs := sdm845.o obj-$(CONFIG_SND_SOC_MACHINE_SDM845) += snd-soc-sdm845.o + +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-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 msm-transcode-loopback-q6-v2.o msm-pcm-q6-noirq.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_DOLBY_DAP) += msm-dolby-dap-config.o +obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o +obj-$(CONFIG_DOLBY_LICENSE) += 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 += codecs/ diff --git a/sound/soc/codecs/Makefile b/asoc/codecs/Makefile similarity index 80% rename from sound/soc/codecs/Makefile rename to asoc/codecs/Makefile index 53256649cd4e..04218e8103e1 100644 --- a/sound/soc/codecs/Makefile +++ b/asoc/codecs/Makefile @@ -1,4 +1,3 @@ -snd-soc-wcd934x-objs := wcd934x.o snd-soc-wcd9xxx-v2-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o wcdcal-hwdep.o wcd9xxx-soc-init.o snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o snd-soc-wsa881x-objs := wsa881x.o wsa881x-tables.o wsa881x-regmap.o wsa881x-temp-sensor.o @@ -24,3 +23,10 @@ obj-$(CONFIG_SND_SOC_WCD_SPI) += snd-soc-wcd-spi.o snd-soc-msm-stub-objs := msm_stub.o obj-$(CONFIG_SND_SOC_MSM_STUB) += snd-soc-msm-stub.o + +wcd-core-objs := wcd9xxx-rst.o wcd9xxx-core-init.o \ + wcd9xxx-core.o wcd9xxx-irq.o \ + wcd9xxx-slimslave.o wcd9xxx-utils.o \ + wcd9335-regmap.o wcd9335-tables.o \ + msm-cdc-pinctrl.o msm-cdc-supply.o +obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd-core.o diff --git a/sound/soc/codecs/audio-ext-clk-up.c b/asoc/codecs/audio-ext-clk-up.c similarity index 99% rename from sound/soc/codecs/audio-ext-clk-up.c rename to asoc/codecs/audio-ext-clk-up.c index 31c063d4b93e..50a8ed6fc014 100644 --- a/sound/soc/codecs/audio-ext-clk-up.c +++ b/asoc/codecs/audio-ext-clk-up.c @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "audio-ext-clk-up.h" enum audio_clk_mux { diff --git a/sound/soc/codecs/audio-ext-clk-up.h b/asoc/codecs/audio-ext-clk-up.h similarity index 100% rename from sound/soc/codecs/audio-ext-clk-up.h rename to asoc/codecs/audio-ext-clk-up.h diff --git a/sound/soc/codecs/audio-ext-clk.c b/asoc/codecs/audio-ext-clk.c similarity index 100% rename from sound/soc/codecs/audio-ext-clk.c rename to asoc/codecs/audio-ext-clk.c diff --git a/include/linux/mfd/wcd9xxx/core.h b/asoc/codecs/core.h similarity index 100% rename from include/linux/mfd/wcd9xxx/core.h rename to asoc/codecs/core.h diff --git a/include/sound/cpe_cmi.h b/asoc/codecs/cpe_cmi.h similarity index 100% rename from include/sound/cpe_cmi.h rename to asoc/codecs/cpe_cmi.h diff --git a/include/sound/cpe_core.h b/asoc/codecs/cpe_core.h similarity index 100% rename from include/sound/cpe_core.h rename to asoc/codecs/cpe_core.h diff --git a/include/sound/cpe_err.h b/asoc/codecs/cpe_err.h similarity index 100% rename from include/sound/cpe_err.h rename to asoc/codecs/cpe_err.h diff --git a/drivers/mfd/msm-cdc-pinctrl.c b/asoc/codecs/msm-cdc-pinctrl.c similarity index 99% rename from drivers/mfd/msm-cdc-pinctrl.c rename to asoc/codecs/msm-cdc-pinctrl.c index 859a75f93bb5..c4d0ec19931d 100644 --- a/drivers/mfd/msm-cdc-pinctrl.c +++ b/asoc/codecs/msm-cdc-pinctrl.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include "msm-cdc-pinctrl.h" struct msm_cdc_pinctrl_info { struct pinctrl *pinctrl; diff --git a/include/linux/mfd/msm-cdc-pinctrl.h b/asoc/codecs/msm-cdc-pinctrl.h similarity index 100% rename from include/linux/mfd/msm-cdc-pinctrl.h rename to asoc/codecs/msm-cdc-pinctrl.h diff --git a/drivers/mfd/msm-cdc-supply.c b/asoc/codecs/msm-cdc-supply.c similarity index 99% rename from drivers/mfd/msm-cdc-supply.c rename to asoc/codecs/msm-cdc-supply.c index 9c7ebf78a00e..e36baa82ac98 100644 --- a/drivers/mfd/msm-cdc-supply.c +++ b/asoc/codecs/msm-cdc-supply.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include "msm-cdc-supply.h" #include #define CODEC_DT_MAX_PROP_SIZE 40 diff --git a/include/linux/mfd/msm-cdc-supply.h b/asoc/codecs/msm-cdc-supply.h similarity index 100% rename from include/linux/mfd/msm-cdc-supply.h rename to asoc/codecs/msm-cdc-supply.h diff --git a/sound/soc/codecs/msm_hdmi_codec_rx.c b/asoc/codecs/msm_hdmi_codec_rx.c similarity index 100% rename from sound/soc/codecs/msm_hdmi_codec_rx.c rename to asoc/codecs/msm_hdmi_codec_rx.c diff --git a/sound/soc/codecs/msm_sdw/Makefile b/asoc/codecs/msm_sdw/Makefile similarity index 100% rename from sound/soc/codecs/msm_sdw/Makefile rename to asoc/codecs/msm_sdw/Makefile diff --git a/sound/soc/codecs/msm_sdw/msm-sdw-tables.c b/asoc/codecs/msm_sdw/msm-sdw-tables.c similarity index 100% rename from sound/soc/codecs/msm_sdw/msm-sdw-tables.c rename to asoc/codecs/msm_sdw/msm-sdw-tables.c diff --git a/sound/soc/codecs/msm_sdw/msm_sdw.h b/asoc/codecs/msm_sdw/msm_sdw.h similarity index 100% rename from sound/soc/codecs/msm_sdw/msm_sdw.h rename to asoc/codecs/msm_sdw/msm_sdw.h diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_cdc.c b/asoc/codecs/msm_sdw/msm_sdw_cdc.c similarity index 100% rename from sound/soc/codecs/msm_sdw/msm_sdw_cdc.c rename to asoc/codecs/msm_sdw/msm_sdw_cdc.c diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_cdc_utils.c b/asoc/codecs/msm_sdw/msm_sdw_cdc_utils.c similarity index 100% rename from sound/soc/codecs/msm_sdw/msm_sdw_cdc_utils.c rename to asoc/codecs/msm_sdw/msm_sdw_cdc_utils.c diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_registers.h b/asoc/codecs/msm_sdw/msm_sdw_registers.h similarity index 100% rename from sound/soc/codecs/msm_sdw/msm_sdw_registers.h rename to asoc/codecs/msm_sdw/msm_sdw_registers.h diff --git a/sound/soc/codecs/msm_sdw/msm_sdw_regmap.c b/asoc/codecs/msm_sdw/msm_sdw_regmap.c similarity index 100% rename from sound/soc/codecs/msm_sdw/msm_sdw_regmap.c rename to asoc/codecs/msm_sdw/msm_sdw_regmap.c diff --git a/sound/soc/codecs/msm_stub.c b/asoc/codecs/msm_stub.c similarity index 100% rename from sound/soc/codecs/msm_stub.c rename to asoc/codecs/msm_stub.c diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/asoc/codecs/pdata.h similarity index 99% rename from include/linux/mfd/wcd9xxx/pdata.h rename to asoc/codecs/pdata.h index f188e850b800..fa16b3d1c8f5 100644 --- a/include/linux/mfd/wcd9xxx/pdata.h +++ b/asoc/codecs/pdata.h @@ -15,7 +15,7 @@ #define __MFD_WCD9XXX_PDATA_H__ #include -#include +#include "msm-cdc-supply.h" #define MICBIAS_EXT_BYP_CAP 0x00 #define MICBIAS_NO_EXT_BYP_CAP 0x01 diff --git a/sound/soc/codecs/sdm660_cdc/Makefile b/asoc/codecs/sdm660_cdc/Makefile similarity index 100% rename from sound/soc/codecs/sdm660_cdc/Makefile rename to asoc/codecs/sdm660_cdc/Makefile diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c similarity index 99% rename from sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c rename to asoc/codecs/sdm660_cdc/msm-analog-cdc.c index f126d35dee81..ddef508cb682 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -16,22 +16,22 @@ #include #include #include -#include #include #include -#include -#include #include #include #include #include #include -#include +#include +#include +#include +#include #include "msm-analog-cdc.h" +#include "msm-cdc-common.h" #include "sdm660-cdc-irq.h" #include "sdm660-cdc-registers.h" -#include "msm-cdc-common.h" -#include "../../msm/sdm660-common.h" +#include "../../sdm660-common.h" #include "../wcd-mbhc-v2-api.h" #define DRV_NAME "pmic_analog_codec" diff --git a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h b/asoc/codecs/sdm660_cdc/msm-analog-cdc.h similarity index 99% rename from sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h rename to asoc/codecs/sdm660_cdc/msm-analog-cdc.h index 9563565f36d2..fffdf3156255 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-analog-cdc.h +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include "../wcd-mbhc-v2.h" #include "../wcdcal-hwdep.h" #include "sdm660-cdc-registers.h" diff --git a/sound/soc/codecs/sdm660_cdc/msm-cdc-common.h b/asoc/codecs/sdm660_cdc/msm-cdc-common.h similarity index 100% rename from sound/soc/codecs/sdm660_cdc/msm-cdc-common.h rename to asoc/codecs/sdm660_cdc/msm-cdc-common.h diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c similarity index 99% rename from sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c rename to asoc/codecs/sdm660_cdc/msm-digital-cdc.c index 68a1d8d47b39..f59b653323c3 100644 --- a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -17,20 +17,20 @@ #include #include #include -#include #include #include -#include #include #include #include #include #include +#include +#include +#include #include "sdm660-cdc-registers.h" #include "msm-digital-cdc.h" #include "msm-cdc-common.h" -#include "../../msm/sdm660-common.h" -#include "../../../../drivers/base/regmap/internal.h" +#include "../../sdm660-common.h" #define DRV_NAME "msm_digital_codec" #define MCLK_RATE_9P6MHZ 9600000 diff --git a/sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h b/asoc/codecs/sdm660_cdc/msm-digital-cdc.h similarity index 100% rename from sound/soc/codecs/sdm660_cdc/msm-digital-cdc.h rename to asoc/codecs/sdm660_cdc/msm-digital-cdc.h diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.c b/asoc/codecs/sdm660_cdc/sdm660-cdc-irq.c similarity index 100% rename from sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.c rename to asoc/codecs/sdm660_cdc/sdm660-cdc-irq.c diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.h b/asoc/codecs/sdm660_cdc/sdm660-cdc-irq.h similarity index 100% rename from sound/soc/codecs/sdm660_cdc/sdm660-cdc-irq.h rename to asoc/codecs/sdm660_cdc/sdm660-cdc-irq.h diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-cdc-registers.h b/asoc/codecs/sdm660_cdc/sdm660-cdc-registers.h similarity index 100% rename from sound/soc/codecs/sdm660_cdc/sdm660-cdc-registers.h rename to asoc/codecs/sdm660_cdc/sdm660-cdc-registers.h diff --git a/sound/soc/codecs/sdm660_cdc/sdm660-regmap.c b/asoc/codecs/sdm660_cdc/sdm660-regmap.c similarity index 100% rename from sound/soc/codecs/sdm660_cdc/sdm660-regmap.c rename to asoc/codecs/sdm660_cdc/sdm660-regmap.c diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/asoc/codecs/wcd-dsp-mgr.c similarity index 100% rename from sound/soc/codecs/wcd-dsp-mgr.c rename to asoc/codecs/wcd-dsp-mgr.c diff --git a/sound/soc/codecs/wcd-dsp-utils.c b/asoc/codecs/wcd-dsp-utils.c similarity index 100% rename from sound/soc/codecs/wcd-dsp-utils.c rename to asoc/codecs/wcd-dsp-utils.c diff --git a/sound/soc/codecs/wcd-dsp-utils.h b/asoc/codecs/wcd-dsp-utils.h similarity index 100% rename from sound/soc/codecs/wcd-dsp-utils.h rename to asoc/codecs/wcd-dsp-utils.h diff --git a/sound/soc/codecs/wcd-mbhc-adc.c b/asoc/codecs/wcd-mbhc-adc.c similarity index 100% rename from sound/soc/codecs/wcd-mbhc-adc.c rename to asoc/codecs/wcd-mbhc-adc.c diff --git a/sound/soc/codecs/wcd-mbhc-adc.h b/asoc/codecs/wcd-mbhc-adc.h similarity index 100% rename from sound/soc/codecs/wcd-mbhc-adc.h rename to asoc/codecs/wcd-mbhc-adc.h diff --git a/sound/soc/codecs/wcd-mbhc-legacy.c b/asoc/codecs/wcd-mbhc-legacy.c similarity index 100% rename from sound/soc/codecs/wcd-mbhc-legacy.c rename to asoc/codecs/wcd-mbhc-legacy.c diff --git a/sound/soc/codecs/wcd-mbhc-legacy.h b/asoc/codecs/wcd-mbhc-legacy.h similarity index 100% rename from sound/soc/codecs/wcd-mbhc-legacy.h rename to asoc/codecs/wcd-mbhc-legacy.h diff --git a/sound/soc/codecs/wcd-mbhc-v2-api.h b/asoc/codecs/wcd-mbhc-v2-api.h similarity index 100% rename from sound/soc/codecs/wcd-mbhc-v2-api.h rename to asoc/codecs/wcd-mbhc-v2-api.h diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c similarity index 99% rename from sound/soc/codecs/wcd-mbhc-v2.c rename to asoc/codecs/wcd-mbhc-v2.c index eb67de92871d..6b3dd8626da5 100644 --- a/sound/soc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -25,9 +25,9 @@ #include #include #include -#include #include #include +#include "msm-cdc-pinctrl.h" #include "wcdcal-hwdep.h" #include "wcd-mbhc-legacy.h" #include "wcd-mbhc-adc.h" diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/asoc/codecs/wcd-mbhc-v2.h similarity index 100% rename from sound/soc/codecs/wcd-mbhc-v2.h rename to asoc/codecs/wcd-mbhc-v2.h diff --git a/sound/soc/codecs/wcd-spi-registers.h b/asoc/codecs/wcd-spi-registers.h similarity index 100% rename from sound/soc/codecs/wcd-spi-registers.h rename to asoc/codecs/wcd-spi-registers.h diff --git a/sound/soc/codecs/wcd-spi.c b/asoc/codecs/wcd-spi.c similarity index 100% rename from sound/soc/codecs/wcd-spi.c rename to asoc/codecs/wcd-spi.c diff --git a/drivers/mfd/wcd9335-regmap.c b/asoc/codecs/wcd9335-regmap.c similarity index 99% rename from drivers/mfd/wcd9335-regmap.c rename to asoc/codecs/wcd9335-regmap.c index 1762374418c0..ffb79b79da1b 100644 --- a/drivers/mfd/wcd9335-regmap.c +++ b/asoc/codecs/wcd9335-regmap.c @@ -11,11 +11,11 @@ * GNU General Public License for more details. */ -#include -#include #include #include +#include "core.h" #include "wcd9xxx-regmap.h" +#include "wcd9335_registers.h" static const struct reg_sequence wcd9335_1_x_defaults[] = { { WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x00 }, diff --git a/drivers/mfd/wcd9335-tables.c b/asoc/codecs/wcd9335-tables.c similarity index 99% rename from drivers/mfd/wcd9335-tables.c rename to asoc/codecs/wcd9335-tables.c index f5c32efe76ef..7df2778f3498 100644 --- a/drivers/mfd/wcd9335-tables.c +++ b/asoc/codecs/wcd9335-tables.c @@ -12,7 +12,7 @@ */ #include -#include +#include "wcd9335_registers.h" #define WCD9335_REG(reg) ((reg) & 0xFF) diff --git a/sound/soc/codecs/wcd9335.c b/asoc/codecs/wcd9335.c similarity index 99% rename from sound/soc/codecs/wcd9335.c rename to asoc/codecs/wcd9335.c index dedeaea55675..26a5a9de362b 100644 --- a/sound/soc/codecs/wcd9335.c +++ b/asoc/codecs/wcd9335.c @@ -22,29 +22,29 @@ #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 "core.h" +#include "pdata.h" #include "wcd9335.h" #include "wcd-mbhc-v2.h" #include "wcd9xxx-common-v2.h" #include "wcd9xxx-resmgr-v2.h" +#include "wcd9xxx-irq.h" +#include "wcd9335_registers.h" +#include "wcd9335_irq.h" #include "wcd_cpe_core.h" #include "wcdcal-hwdep.h" #include "wcd-mbhc-v2-api.h" diff --git a/sound/soc/codecs/wcd9335.h b/asoc/codecs/wcd9335.h similarity index 98% rename from sound/soc/codecs/wcd9335.h rename to asoc/codecs/wcd9335.h index c76461edecf1..48826e668d2e 100644 --- a/sound/soc/codecs/wcd9335.h +++ b/asoc/codecs/wcd9335.h @@ -15,8 +15,8 @@ #include #include -#include -#include +#include +#include "wcd9xxx-slimslave.h" #include "wcd-mbhc-v2.h" #define TASHA_REG_VAL(reg, val) {reg, 0, val} diff --git a/include/linux/mfd/wcd9335/irq.h b/asoc/codecs/wcd9335_irq.h similarity index 100% rename from include/linux/mfd/wcd9335/irq.h rename to asoc/codecs/wcd9335_irq.h diff --git a/include/linux/mfd/wcd9335/registers.h b/asoc/codecs/wcd9335_registers.h similarity index 100% rename from include/linux/mfd/wcd9335/registers.h rename to asoc/codecs/wcd9335_registers.h diff --git a/sound/soc/codecs/wcd934x/Makefile b/asoc/codecs/wcd934x/Makefile similarity index 67% rename from sound/soc/codecs/wcd934x/Makefile rename to asoc/codecs/wcd934x/Makefile index 12781f6d4556..c4db59b0e9d7 100644 --- a/sound/soc/codecs/wcd934x/Makefile +++ b/asoc/codecs/wcd934x/Makefile @@ -2,5 +2,6 @@ # Makefile for wcd934x codec driver. # snd-soc-wcd934x-objs := wcd934x.o wcd934x-dsp-cntl.o \ - wcd934x-mbhc.o wcd934x-dsd.o + wcd934x-mbhc.o wcd934x-dsd.o \ + wcd934x-regmap.o wcd934x-tables.o obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/asoc/codecs/wcd934x/wcd934x-dsd.c similarity index 99% rename from sound/soc/codecs/wcd934x/wcd934x-dsd.c rename to asoc/codecs/wcd934x/wcd934x-dsd.c index 3e23e3749bda..eea18c3d951b 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsd.c +++ b/asoc/codecs/wcd934x/wcd934x-dsd.c @@ -12,9 +12,9 @@ #include #include -#include #include #include +#include #include "wcd934x-dsd.h" #define DSD_VOLUME_MAX_0dB 0 diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.h b/asoc/codecs/wcd934x/wcd934x-dsd.h similarity index 100% rename from sound/soc/codecs/wcd934x/wcd934x-dsd.h rename to asoc/codecs/wcd934x/wcd934x-dsd.h diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c similarity index 99% rename from sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c rename to asoc/codecs/wcd934x/wcd934x-dsp-cntl.c index 8da042531fd8..d82748b2689e 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -15,13 +15,13 @@ #include #include #include -#include -#include -#include #include #include +#include #include "wcd934x.h" #include "wcd934x-dsp-cntl.h" +#include "../wcd9xxx-irq.h" +#include "../core.h" #define WCD_CNTL_DIR_NAME_LEN_MAX 32 #define WCD_CPE_FLL_MAX_RETRIES 5 diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h similarity index 100% rename from sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h rename to asoc/codecs/wcd934x/wcd934x-dsp-cntl.h diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.c b/asoc/codecs/wcd934x/wcd934x-mbhc.c similarity index 99% rename from sound/soc/codecs/wcd934x/wcd934x-mbhc.c rename to asoc/codecs/wcd934x/wcd934x-mbhc.c index ea19caa14f8e..9595ba8e5c9c 100644 --- a/sound/soc/codecs/wcd934x/wcd934x-mbhc.c +++ b/asoc/codecs/wcd934x/wcd934x-mbhc.c @@ -20,18 +20,18 @@ #include #include #include -#include -#include #include -#include -#include -#include #include #include #include #include #include "wcd934x.h" #include "wcd934x-mbhc.h" +#include +#include "wcd934x_irq.h" +#include "../core.h" +#include "../pdata.h" +#include "../wcd9xxx-irq.h" #include "../wcdcal-hwdep.h" #include "../wcd-mbhc-v2-api.h" diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.h b/asoc/codecs/wcd934x/wcd934x-mbhc.h similarity index 100% rename from sound/soc/codecs/wcd934x/wcd934x-mbhc.h rename to asoc/codecs/wcd934x/wcd934x-mbhc.h diff --git a/drivers/mfd/wcd934x-regmap.c b/asoc/codecs/wcd934x/wcd934x-regmap.c similarity index 99% rename from drivers/mfd/wcd934x-regmap.c rename to asoc/codecs/wcd934x/wcd934x-regmap.c index 27249eeec013..7f648fe1c938 100644 --- a/drivers/mfd/wcd934x-regmap.c +++ b/asoc/codecs/wcd934x/wcd934x-regmap.c @@ -11,11 +11,11 @@ * GNU General Public License for more details. */ -#include -#include #include #include -#include "wcd9xxx-regmap.h" +#include +#include "../core.h" +#include "../wcd9xxx-regmap.h" static const struct reg_sequence wcd934x_1_1_defaults[] = { diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/asoc/codecs/wcd934x/wcd934x-routing.h similarity index 100% rename from sound/soc/codecs/wcd934x/wcd934x-routing.h rename to asoc/codecs/wcd934x/wcd934x-routing.h diff --git a/drivers/mfd/wcd934x-tables.c b/asoc/codecs/wcd934x/wcd934x-tables.c similarity index 99% rename from drivers/mfd/wcd934x-tables.c rename to asoc/codecs/wcd934x/wcd934x-tables.c index db963d08b66e..4e6c2fb2a1cc 100644 --- a/drivers/mfd/wcd934x-tables.c +++ b/asoc/codecs/wcd934x/wcd934x-tables.c @@ -12,7 +12,7 @@ */ #include -#include +#include #define WCD934X_REG(reg) ((reg) & 0xFF) diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c similarity index 99% rename from sound/soc/codecs/wcd934x/wcd934x.c rename to asoc/codecs/wcd934x/wcd934x.c index 3079cca65e38..1997e044fad7 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -27,24 +27,24 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include -#include +#include +#include #include #include #include #include #include #include +#include #include "wcd934x.h" #include "wcd934x-mbhc.h" #include "wcd934x-routing.h" #include "wcd934x-dsp-cntl.h" +#include "wcd934x_irq.h" +#include "../core.h" +#include "../pdata.h" +#include "../wcd9xxx-irq.h" #include "../wcd9xxx-common-v2.h" #include "../wcd9xxx-resmgr-v2.h" #include "../wcdcal-hwdep.h" diff --git a/sound/soc/codecs/wcd934x/wcd934x.h b/asoc/codecs/wcd934x/wcd934x.h similarity index 98% rename from sound/soc/codecs/wcd934x/wcd934x.h rename to asoc/codecs/wcd934x/wcd934x.h index 27c21f103533..ccd48ff2099f 100644 --- a/sound/soc/codecs/wcd934x/wcd934x.h +++ b/asoc/codecs/wcd934x/wcd934x.h @@ -13,9 +13,9 @@ #ifndef WCD934X_H #define WCD934X_H -#include -#include +#include #include "wcd934x-dsp-cntl.h" +#include "../wcd9xxx-slimslave.h" #include "../wcd9xxx-common-v2.h" #include "../wcd-mbhc-v2.h" diff --git a/include/linux/mfd/wcd934x/irq.h b/asoc/codecs/wcd934x/wcd934x_irq.h similarity index 100% rename from include/linux/mfd/wcd934x/irq.h rename to asoc/codecs/wcd934x/wcd934x_irq.h diff --git a/sound/soc/codecs/wcd9xxx-common-v2.c b/asoc/codecs/wcd9xxx-common-v2.c similarity index 99% rename from sound/soc/codecs/wcd9xxx-common-v2.c rename to asoc/codecs/wcd9xxx-common-v2.c index 62166579342a..478c0c6d72e6 100644 --- a/sound/soc/codecs/wcd9xxx-common-v2.c +++ b/asoc/codecs/wcd9xxx-common-v2.c @@ -16,8 +16,8 @@ #include #include #include -#include #include +#include "core.h" #include "wcd9xxx-common-v2.h" #define WCD_USLEEP_RANGE 50 diff --git a/sound/soc/codecs/wcd9xxx-common-v2.h b/asoc/codecs/wcd9xxx-common-v2.h similarity index 100% rename from sound/soc/codecs/wcd9xxx-common-v2.h rename to asoc/codecs/wcd9xxx-common-v2.h diff --git a/drivers/mfd/wcd9xxx-core-init.c b/asoc/codecs/wcd9xxx-core-init.c similarity index 92% rename from drivers/mfd/wcd9xxx-core-init.c rename to asoc/codecs/wcd9xxx-core-init.c index 7f933990682d..e575e0d3472e 100644 --- a/drivers/mfd/wcd9xxx-core-init.c +++ b/asoc/codecs/wcd9xxx-core-init.c @@ -11,9 +11,9 @@ */ #include -#include -#include -#include +#include "msm-cdc-pinctrl.h" +#include "wcd9xxx-irq.h" +#include "core.h" #define NUM_DRIVERS_REG_RET 3 diff --git a/drivers/mfd/wcd9xxx-core.c b/asoc/codecs/wcd9xxx-core.c similarity index 99% rename from drivers/mfd/wcd9xxx-core.c rename to asoc/codecs/wcd9xxx-core.c index 232c290bfb55..2ab5e894864e 100644 --- a/drivers/mfd/wcd9xxx-core.c +++ b/asoc/codecs/wcd9xxx-core.c @@ -16,21 +16,21 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include #include #include #include #include +#include #include +#include "core.h" +#include "pdata.h" +#include "msm-cdc-pinctrl.h" +#include "msm-cdc-supply.h" +#include "wcd9xxx-irq.h" +#include "wcd9xxx-utils.h" #include "wcd9xxx-regmap.h" +#include "wcd9xxx-slimslave.h" #define WCD9XXX_REGISTER_START_OFFSET 0x800 #define WCD9XXX_SLIM_RW_MAX_TRIES 3 diff --git a/drivers/mfd/wcd9xxx-irq.c b/asoc/codecs/wcd9xxx-irq.c similarity index 99% rename from drivers/mfd/wcd9xxx-irq.c rename to asoc/codecs/wcd9xxx-irq.c index 092f44632e1b..65100c983f06 100644 --- a/drivers/mfd/wcd9xxx-irq.c +++ b/asoc/codecs/wcd9xxx-irq.c @@ -16,9 +16,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -29,6 +26,9 @@ #include #include #include +#include +#include "core.h" +#include "wcd9xxx-irq.h" #define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE)) #define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-irq.h b/asoc/codecs/wcd9xxx-irq.h similarity index 97% rename from include/linux/mfd/wcd9xxx/wcd9xxx-irq.h rename to asoc/codecs/wcd9xxx-irq.h index 99ce60383cc2..dfe0b929a84b 100644 --- a/include/linux/mfd/wcd9xxx/wcd9xxx-irq.h +++ b/asoc/codecs/wcd9xxx-irq.h @@ -11,7 +11,7 @@ */ #include -#include +#include "core.h" #ifndef __MFD_WCD9XXX_IRQ_H #define __MFD_WCD9XXX_IRQ_H diff --git a/drivers/mfd/wcd9xxx-regmap.h b/asoc/codecs/wcd9xxx-regmap.h similarity index 97% rename from drivers/mfd/wcd9xxx-regmap.h rename to asoc/codecs/wcd9xxx-regmap.h index f44e8b1cf532..b7604ffb38d0 100644 --- a/drivers/mfd/wcd9xxx-regmap.h +++ b/asoc/codecs/wcd9xxx-regmap.h @@ -15,7 +15,7 @@ #define _WCD9XXX_REGMAP_ #include -#include +#include "core.h" typedef int (*regmap_patch_fptr)(struct regmap *, int); diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.c b/asoc/codecs/wcd9xxx-resmgr-v2.c similarity index 99% rename from sound/soc/codecs/wcd9xxx-resmgr-v2.c rename to asoc/codecs/wcd9xxx-resmgr-v2.c index feef0a48af79..1b7610f08835 100644 --- a/sound/soc/codecs/wcd9xxx-resmgr-v2.c +++ b/asoc/codecs/wcd9xxx-resmgr-v2.c @@ -14,11 +14,11 @@ #include #include #include -#include -#include -#include #include #include "wcd9xxx-resmgr-v2.h" +#include "core.h" +#include "wcd9335_registers.h" +#include #define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 5000 #define WCD93XX_ANA_BIAS 0x0601 diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.h b/asoc/codecs/wcd9xxx-resmgr-v2.h similarity index 98% rename from sound/soc/codecs/wcd9xxx-resmgr-v2.h rename to asoc/codecs/wcd9xxx-resmgr-v2.h index e831ba61e9c2..e9d3531e262e 100644 --- a/sound/soc/codecs/wcd9xxx-resmgr-v2.h +++ b/asoc/codecs/wcd9xxx-resmgr-v2.h @@ -13,8 +13,8 @@ #ifndef __WCD9XXX_COMMON_V2_H__ #define __WCD9XXX_COMMON_V2_H__ -#include #include +#include "core.h" enum wcd_clock_type { WCD_CLK_OFF, diff --git a/drivers/mfd/wcd9xxx-rst.c b/asoc/codecs/wcd9xxx-rst.c similarity index 97% rename from drivers/mfd/wcd9xxx-rst.c rename to asoc/codecs/wcd9xxx-rst.c index c8e0b348254a..3df95d537fe8 100644 --- a/drivers/mfd/wcd9xxx-rst.c +++ b/asoc/codecs/wcd9xxx-rst.c @@ -15,13 +15,13 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include +#include "core.h" +#include "pdata.h" +#include "wcd9xxx-utils.h" +#include "wcd9335_registers.h" +#include "wcd9335_irq.h" +#include +#include "wcd934x/wcd934x_irq.h" /* wcd9335 interrupt table */ static const struct intr_data wcd9335_intr_table[] = { diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/asoc/codecs/wcd9xxx-slimslave.c similarity index 99% rename from drivers/mfd/wcd9xxx-slimslave.c rename to asoc/codecs/wcd9xxx-slimslave.c index a99ad5a2f9c8..66a83320d03b 100644 --- a/drivers/mfd/wcd9xxx-slimslave.c +++ b/asoc/codecs/wcd9xxx-slimslave.c @@ -11,8 +11,8 @@ */ #include #include -#include #include +#include "wcd9xxx-slimslave.h" struct wcd9xxx_slim_sch { u16 rx_port_ch_reg_base; diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h b/asoc/codecs/wcd9xxx-slimslave.h similarity index 96% rename from include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h rename to asoc/codecs/wcd9xxx-slimslave.h index 96fdb00a2e03..5132e43efad0 100644 --- a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h +++ b/asoc/codecs/wcd9xxx-slimslave.h @@ -10,11 +10,11 @@ * GNU General Public License for more details. */ -#ifndef __WCD9310_SLIMSLAVE_H_ -#define __WCD9310_SLIMSLAVE_H_ +#ifndef __WCD9XXX_SLIMSLAVE_H_ +#define __WCD9XXX_SLIMSLAVE_H_ #include -#include +#include "core.h" /* @@ -116,4 +116,4 @@ int wcd9xxx_rx_vport_validation(u32 port_id, int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id, struct wcd9xxx_codec_dai_data *codec_dai, u32 num_codec_dais); -#endif /* __WCD9310_SLIMSLAVE_H_ */ +#endif /* __WCD9XXX_SLIMSLAVE_H_ */ diff --git a/sound/soc/codecs/wcd9xxx-soc-init.c b/asoc/codecs/wcd9xxx-soc-init.c similarity index 100% rename from sound/soc/codecs/wcd9xxx-soc-init.c rename to asoc/codecs/wcd9xxx-soc-init.c diff --git a/drivers/mfd/wcd9xxx-utils.c b/asoc/codecs/wcd9xxx-utils.c similarity index 99% rename from drivers/mfd/wcd9xxx-utils.c rename to asoc/codecs/wcd9xxx-utils.c index 8d3d4ad4bbb5..a1ea93843f28 100644 --- a/drivers/mfd/wcd9xxx-utils.c +++ b/asoc/codecs/wcd9xxx-utils.c @@ -20,12 +20,12 @@ #include #include #include -#include -#include -#include -#include -#include -#include +#include "core.h" +#include "msm-cdc-supply.h" +#include "msm-cdc-pinctrl.h" +#include "pdata.h" +#include "wcd9xxx-irq.h" +#include "wcd9xxx-utils.h" #define REG_BYTES 2 #define VAL_BYTES 1 diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-utils.h b/asoc/codecs/wcd9xxx-utils.h similarity index 95% rename from include/linux/mfd/wcd9xxx/wcd9xxx-utils.h rename to asoc/codecs/wcd9xxx-utils.h index 7a13dd19e8c0..a7ec56c2da0a 100644 --- a/include/linux/mfd/wcd9xxx/wcd9xxx-utils.h +++ b/asoc/codecs/wcd9xxx-utils.h @@ -16,8 +16,8 @@ #include #include #include -#include -#include +#include "pdata.h" +#include "core.h" struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev); int wcd9xxx_bringup(struct device *dev); diff --git a/sound/soc/codecs/wcd_cmi_api.h b/asoc/codecs/wcd_cmi_api.h similarity index 100% rename from sound/soc/codecs/wcd_cmi_api.h rename to asoc/codecs/wcd_cmi_api.h diff --git a/sound/soc/codecs/wcd_cpe_core.c b/asoc/codecs/wcd_cpe_core.c similarity index 99% rename from sound/soc/codecs/wcd_cpe_core.c rename to asoc/codecs/wcd_cpe_core.c index f2a20d51d0e2..7748ace12686 100644 --- a/sound/soc/codecs/wcd_cpe_core.c +++ b/asoc/codecs/wcd_cpe_core.c @@ -23,16 +23,16 @@ #include #include #include -#include #include -#include #include -#include -#include -#include +#include +#include "core.h" +#include "cpe_core.h" +#include "cpe_err.h" #include "wcd_cpe_core.h" #include "wcd_cpe_services.h" #include "wcd_cmi_api.h" +#include "wcd9xxx-irq.h" #define CMI_CMD_TIMEOUT (10 * HZ) #define WCD_CPE_LSM_MAX_SESSIONS 2 diff --git a/sound/soc/codecs/wcd_cpe_core.h b/asoc/codecs/wcd_cpe_core.h similarity index 100% rename from sound/soc/codecs/wcd_cpe_core.h rename to asoc/codecs/wcd_cpe_core.h diff --git a/sound/soc/codecs/wcd_cpe_services.c b/asoc/codecs/wcd_cpe_services.c similarity index 99% rename from sound/soc/codecs/wcd_cpe_services.c rename to asoc/codecs/wcd_cpe_services.c index ad8962b6f1fe..7a0e70311407 100644 --- a/sound/soc/codecs/wcd_cpe_services.c +++ b/asoc/codecs/wcd_cpe_services.c @@ -17,10 +17,11 @@ #include #include #include -#include -#include #include +#include #include +#include "core.h" +#include "cpe_cmi.h" #include "wcd_cpe_services.h" #include "wcd_cmi_api.h" diff --git a/sound/soc/codecs/wcd_cpe_services.h b/asoc/codecs/wcd_cpe_services.h similarity index 100% rename from sound/soc/codecs/wcd_cpe_services.h rename to asoc/codecs/wcd_cpe_services.h diff --git a/sound/soc/codecs/wcdcal-hwdep.c b/asoc/codecs/wcdcal-hwdep.c similarity index 100% rename from sound/soc/codecs/wcdcal-hwdep.c rename to asoc/codecs/wcdcal-hwdep.c diff --git a/sound/soc/codecs/wcdcal-hwdep.h b/asoc/codecs/wcdcal-hwdep.h similarity index 100% rename from sound/soc/codecs/wcdcal-hwdep.h rename to asoc/codecs/wcdcal-hwdep.h diff --git a/sound/soc/codecs/wsa881x-analog.c b/asoc/codecs/wsa881x-analog.c similarity index 100% rename from sound/soc/codecs/wsa881x-analog.c rename to asoc/codecs/wsa881x-analog.c diff --git a/sound/soc/codecs/wsa881x-analog.h b/asoc/codecs/wsa881x-analog.h similarity index 100% rename from sound/soc/codecs/wsa881x-analog.h rename to asoc/codecs/wsa881x-analog.h diff --git a/sound/soc/codecs/wsa881x-irq.c b/asoc/codecs/wsa881x-irq.c similarity index 100% rename from sound/soc/codecs/wsa881x-irq.c rename to asoc/codecs/wsa881x-irq.c diff --git a/sound/soc/codecs/wsa881x-irq.h b/asoc/codecs/wsa881x-irq.h similarity index 100% rename from sound/soc/codecs/wsa881x-irq.h rename to asoc/codecs/wsa881x-irq.h diff --git a/sound/soc/codecs/wsa881x-registers-analog.h b/asoc/codecs/wsa881x-registers-analog.h similarity index 100% rename from sound/soc/codecs/wsa881x-registers-analog.h rename to asoc/codecs/wsa881x-registers-analog.h diff --git a/sound/soc/codecs/wsa881x-registers.h b/asoc/codecs/wsa881x-registers.h similarity index 100% rename from sound/soc/codecs/wsa881x-registers.h rename to asoc/codecs/wsa881x-registers.h diff --git a/sound/soc/codecs/wsa881x-regmap-analog.c b/asoc/codecs/wsa881x-regmap-analog.c similarity index 100% rename from sound/soc/codecs/wsa881x-regmap-analog.c rename to asoc/codecs/wsa881x-regmap-analog.c diff --git a/sound/soc/codecs/wsa881x-regmap.c b/asoc/codecs/wsa881x-regmap.c similarity index 100% rename from sound/soc/codecs/wsa881x-regmap.c rename to asoc/codecs/wsa881x-regmap.c diff --git a/sound/soc/codecs/wsa881x-tables-analog.c b/asoc/codecs/wsa881x-tables-analog.c similarity index 100% rename from sound/soc/codecs/wsa881x-tables-analog.c rename to asoc/codecs/wsa881x-tables-analog.c diff --git a/sound/soc/codecs/wsa881x-tables.c b/asoc/codecs/wsa881x-tables.c similarity index 100% rename from sound/soc/codecs/wsa881x-tables.c rename to asoc/codecs/wsa881x-tables.c diff --git a/sound/soc/codecs/wsa881x-temp-sensor.c b/asoc/codecs/wsa881x-temp-sensor.c similarity index 100% rename from sound/soc/codecs/wsa881x-temp-sensor.c rename to asoc/codecs/wsa881x-temp-sensor.c diff --git a/sound/soc/codecs/wsa881x-temp-sensor.h b/asoc/codecs/wsa881x-temp-sensor.h similarity index 100% rename from sound/soc/codecs/wsa881x-temp-sensor.h rename to asoc/codecs/wsa881x-temp-sensor.h diff --git a/sound/soc/codecs/wsa881x.c b/asoc/codecs/wsa881x.c similarity index 99% rename from sound/soc/codecs/wsa881x.c rename to asoc/codecs/wsa881x.c index 77aea1049ca5..456f68c81954 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/asoc/codecs/wsa881x.c @@ -26,13 +26,13 @@ #include #include #include -#include -#include +#include #include #include #include #include #include +#include "msm-cdc-pinctrl.h" #include "wsa881x.h" #include "wsa881x-temp-sensor.h" diff --git a/sound/soc/codecs/wsa881x.h b/asoc/codecs/wsa881x.h similarity index 100% rename from sound/soc/codecs/wsa881x.h rename to asoc/codecs/wsa881x.h diff --git a/sound/soc/msm/device_event.h b/asoc/device_event.h similarity index 100% rename from sound/soc/msm/device_event.h rename to asoc/device_event.h diff --git a/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c b/asoc/msm-audio-effects-q6-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c rename to asoc/msm-audio-effects-q6-v2.c index 9f082226ca35..962d64e94ba3 100644 --- a/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c +++ b/asoc/msm-audio-effects-q6-v2.c @@ -11,11 +11,11 @@ */ #include -#include -#include #include -#include #include +#include +#include +#include #define MAX_ENABLE_CMD_SIZE 32 diff --git a/sound/soc/msm/msm-audio-pinctrl.c b/asoc/msm-audio-pinctrl.c similarity index 100% rename from sound/soc/msm/msm-audio-pinctrl.c rename to asoc/msm-audio-pinctrl.c diff --git a/sound/soc/msm/msm-audio-pinctrl.h b/asoc/msm-audio-pinctrl.h similarity index 100% rename from sound/soc/msm/msm-audio-pinctrl.h rename to asoc/msm-audio-pinctrl.h diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c rename to asoc/msm-compress-q6-v2.c index c88526564d79..89efb904ae3f 100644 --- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -26,23 +26,22 @@ #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-qti-pp-config.h" diff --git a/sound/soc/msm/msm-cpe-lsm.c b/asoc/msm-cpe-lsm.c similarity index 99% rename from sound/soc/msm/msm-cpe-lsm.c rename to asoc/msm-cpe-lsm.c index 7b65dda227b9..87297ec05104 100644 --- a/sound/soc/msm/msm-cpe-lsm.c +++ b/asoc/msm-cpe-lsm.c @@ -23,10 +23,10 @@ #include #include #include -#include #include #include -#include +#include "msm-slim-dma.h" +#include "codecs/cpe_core.h" #define SAMPLE_RATE_48KHZ 48000 #define SAMPLE_RATE_16KHZ 16000 diff --git a/sound/soc/msm/msm-dai-fe.c b/asoc/msm-dai-fe.c similarity index 100% rename from sound/soc/msm/msm-dai-fe.c rename to asoc/msm-dai-fe.c diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/asoc/msm-dai-q6-hdmi-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c rename to asoc/msm-dai-q6-hdmi-v2.c index deb179898b6a..9c8d20a9f2a7 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c +++ b/asoc/msm-dai-q6-hdmi-v2.c @@ -20,10 +20,10 @@ #include #include #include -#include -#include -#include #include +#include +#include +#include "msm-dai-q6-v2.h" #define HDMI_RX_CA_MAX 0x32 diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c rename to asoc/msm-dai-q6-v2.c index c8b01c679788..583934ddf6f1 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -22,10 +21,11 @@ #include #include #include -#include -#include -#include #include +#include +#include +#include "msm-dai-q6-v2.h" +#include "codecs/core.h" #define MSM_DAI_PRI_AUXPCM_DT_DEV_ID 1 #define MSM_DAI_SEC_AUXPCM_DT_DEV_ID 2 diff --git a/include/sound/msm-dai-q6-v2.h b/asoc/msm-dai-q6-v2.h similarity index 100% rename from include/sound/msm-dai-q6-v2.h rename to asoc/msm-dai-q6-v2.h diff --git a/sound/soc/msm/qdsp6v2/msm-dai-slim.c b/asoc/msm-dai-slim.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-dai-slim.c rename to asoc/msm-dai-slim.c index 8115feef846e..0e7c7b1c4883 100644 --- a/sound/soc/msm/qdsp6v2/msm-dai-slim.c +++ b/asoc/msm-dai-slim.c @@ -19,7 +19,7 @@ #include #include #include -#include +#include "msm-slim-dma.h" #define SLIM_DEV_NAME "msm-dai-slim" diff --git a/sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c b/asoc/msm-dai-stub-v2.c similarity index 100% rename from sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c rename to asoc/msm-dai-stub-v2.c diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-common.h b/asoc/msm-dolby-common.h similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-dolby-common.h rename to asoc/msm-dolby-common.h index b43ff15e7649..885d283ef03d 100644 --- a/sound/soc/msm/qdsp6v2/msm-dolby-common.h +++ b/asoc/msm-dolby-common.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h b/asoc/msm-dolby-dap-config.h similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h rename to asoc/msm-dolby-dap-config.h index 6204cb15857d..b2b713907130 100644 --- a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h +++ b/asoc/msm-dolby-dap-config.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c b/asoc/msm-ds2-dap-config.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c rename to asoc/msm-ds2-dap-config.c index 5c9393f7e407..7381e5d8c362 100644 --- a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c +++ b/asoc/msm-ds2-dap-config.c @@ -15,11 +15,11 @@ #include #include #include -#include +#include +#include #include "msm-ds2-dap-config.h" #include "msm-pcm-routing-v2.h" -#include #if defined(CONFIG_DOLBY_DS2) || defined(CONFIG_DOLBY_LICENSE) diff --git a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h b/asoc/msm-ds2-dap-config.h similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h rename to asoc/msm-ds2-dap-config.h index 5804a64f5d48..c90bd8fb83a4 100644 --- a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h +++ b/asoc/msm-ds2-dap-config.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c b/asoc/msm-dts-srs-tm-config.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c rename to asoc/msm-dts-srs-tm-config.c index 437cd979359d..78174afb9b47 100644 --- a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c +++ b/asoc/msm-dts-srs-tm-config.c @@ -16,11 +16,11 @@ #include #include #include -#include -#include -#include #include -#include "msm-dts-srs-tm-config.h" +#include +#include +#include +#include #include "msm-pcm-routing-v2.h" static int srs_port_id[AFE_MAX_PORTS] = {-1}; diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/asoc/msm-lsm-client.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-lsm-client.c rename to asoc/msm-lsm-client.c index 3a6cbe6937c4..e96d111a61e4 100644 --- a/sound/soc/msm/qdsp6v2/msm-lsm-client.c +++ b/asoc/msm-lsm-client.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -28,9 +27,10 @@ #include #include #include -#include #include #include +#include +#include #include "msm-pcm-routing-v2.h" #define CAPTURE_MIN_NUM_PERIODS 2 diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c b/asoc/msm-pcm-afe-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c rename to asoc/msm-pcm-afe-v2.c index ab9b3107665f..102fad8b3278 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c +++ b/asoc/msm-pcm-afe-v2.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -28,8 +27,9 @@ #include #include #include -#include #include +#include +#include #include "msm-pcm-afe-v2.h" #define MIN_PLAYBACK_PERIOD_SIZE (128 * 2) diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h b/asoc/msm-pcm-afe-v2.h similarity index 95% rename from sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h rename to asoc/msm-pcm-afe-v2.h index 84a4c3c6c275..926b626de37d 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h +++ b/asoc/msm-pcm-afe-v2.h @@ -11,8 +11,8 @@ */ #ifndef _MSM_PCM_AFE_H #define _MSM_PCM_AFE_H -#include -#include +#include +#include struct pcm_afe_info { diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c b/asoc/msm-pcm-dtmf-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c rename to asoc/msm-pcm-dtmf-v2.c index f4e03fe2e4cf..c387c7d0aab4 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c +++ b/asoc/msm-pcm-dtmf-v2.c @@ -20,11 +20,11 @@ #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, diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c b/asoc/msm-pcm-host-voice-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c rename to asoc/msm-pcm-host-voice-v2.c index 2f60db993895..55b07bc75823 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c +++ b/asoc/msm-pcm-host-voice-v2.c @@ -25,8 +25,8 @@ #include #include #include -#include -#include "q6voice.h" +#include +#include #define HPCM_MAX_Q_LEN 2 #define HPCM_MIN_VOC_PKT_SIZE 320 diff --git a/sound/soc/msm/msm-pcm-hostless.c b/asoc/msm-pcm-hostless.c similarity index 100% rename from sound/soc/msm/msm-pcm-hostless.c rename to asoc/msm-pcm-hostless.c diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c b/asoc/msm-pcm-loopback-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c rename to asoc/msm-pcm-loopback-v2.c index 7ef1ca8f1ae1..a6ac8ca006fa 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c +++ b/asoc/msm-pcm-loopback-v2.c @@ -16,16 +16,16 @@ #include #include #include -#include #include #include -#include #include #include #include #include #include -#include +#include +#include +#include #include "msm-pcm-routing-v2.h" diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c b/asoc/msm-pcm-q6-noirq.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c rename to asoc/msm-pcm-q6-noirq.c index 75a2bffaa3ce..3e03437abcf6 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c +++ b/asoc/msm-pcm-q6-noirq.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -28,7 +27,6 @@ #include #include #include -#include #include #include @@ -36,6 +34,8 @@ #include #include #include +#include +#include #include "msm-pcm-q6-v2.h" #include "msm-pcm-routing-v2.h" diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c rename to asoc/msm-pcm-q6-v2.c index 74e99d376049..4910decf6d8b 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -25,16 +25,16 @@ #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" diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/asoc/msm-pcm-q6-v2.h similarity index 98% rename from sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h rename to asoc/msm-pcm-q6-v2.h index 3b3f0480d33c..0177b2d6ce79 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h +++ b/asoc/msm-pcm-q6-v2.h @@ -18,8 +18,8 @@ #ifndef _MSM_PCM_H #define _MSM_PCM_H -#include -#include +#include +#include diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c b/asoc/msm-pcm-routing-devdep.c similarity index 100% rename from sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c rename to asoc/msm-pcm-routing-devdep.c diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h b/asoc/msm-pcm-routing-devdep.h similarity index 100% rename from sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h rename to asoc/msm-pcm-routing-devdep.h diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c rename to asoc/msm-pcm-routing-v2.c index d67296f238f0..de2201ef2f80 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -25,25 +25,25 @@ #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" #ifndef CONFIG_DOLBY_DAP #undef DOLBY_ADM_COPP_TOPOLOGY_ID diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h rename to asoc/msm-pcm-routing-v2.h index 19e726001d25..4e823d3eba3c 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -11,7 +11,7 @@ */ #ifndef _MSM_PCM_ROUTING_H #define _MSM_PCM_ROUTING_H -#include +#include /* * These names are used by HAL to specify the BE. If any changes are diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/asoc/msm-pcm-voice-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c rename to asoc/msm-pcm-voice-v2.c index 654806e9795e..9972132000e9 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c +++ b/asoc/msm-pcm-voice-v2.c @@ -26,9 +26,9 @@ #include #include #include +#include #include "msm-pcm-voice-v2.h" -#include "q6voice.h" static struct msm_voice voice_info[VOICE_SESSION_INDEX_MAX]; diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h b/asoc/msm-pcm-voice-v2.h similarity index 97% rename from sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h rename to asoc/msm-pcm-voice-v2.h index e00cebc51e7e..60f5adcf5757 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h +++ b/asoc/msm-pcm-voice-v2.h @@ -11,7 +11,7 @@ */ #ifndef _MSM_PCM_VOICE_H #define _MSM_PCM_VOICE_H -#include +#include enum { VOICE_SESSION_INDEX, diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/asoc/msm-pcm-voip-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c rename to asoc/msm-pcm-voip-v2.c index 02225f0dcef4..86dcacacbaa3 100644 --- a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c +++ b/asoc/msm-pcm-voip-v2.c @@ -26,10 +26,10 @@ #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 diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c b/asoc/msm-qti-pp-config.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-qti-pp-config.c rename to asoc/msm-qti-pp-config.c index a885e1e3aa86..2064056ab350 100644 --- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c +++ b/asoc/msm-qti-pp-config.c @@ -15,12 +15,12 @@ #include #include #include -#include -#include -#include #include -#include #include +#include +#include +#include +#include #include "msm-qti-pp-config.h" #include "msm-pcm-routing-v2.h" diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h b/asoc/msm-qti-pp-config.h similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-qti-pp-config.h rename to asoc/msm-qti-pp-config.h index 01a06a4965ef..14e9b10d0061 100644 --- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h +++ b/asoc/msm-qti-pp-config.h @@ -1,6 +1,5 @@ /* * 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. diff --git a/include/sound/msm-slim-dma.h b/asoc/msm-slim-dma.h similarity index 100% rename from include/sound/msm-slim-dma.h rename to asoc/msm-slim-dma.h diff --git a/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c b/asoc/msm-transcode-loopback-q6-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c rename to asoc/msm-transcode-loopback-q6-v2.c index b1bb27248714..eaef498c49b7 100644 --- a/sound/soc/msm/qdsp6v2/msm-transcode-loopback-q6-v2.c +++ b/asoc/msm-transcode-loopback-q6-v2.c @@ -19,22 +19,21 @@ #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-qti-pp-config.h" diff --git a/sound/soc/msm/msm8996.c b/asoc/msm8996.c similarity index 100% rename from sound/soc/msm/msm8996.c rename to asoc/msm8996.c diff --git a/sound/soc/msm/msm8998.c b/asoc/msm8998.c similarity index 99% rename from sound/soc/msm/msm8998.c rename to asoc/msm8998.c index 222c65a3182d..6c5393d021d7 100644 --- a/sound/soc/msm/msm8998.c +++ b/asoc/msm8998.c @@ -23,23 +23,23 @@ #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" +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include "codecs/msm-cdc-pinctrl.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" diff --git a/sound/soc/msm/sdm660-common.c b/asoc/sdm660-common.c similarity index 99% rename from sound/soc/msm/sdm660-common.c rename to asoc/sdm660-common.c index b34b04b83044..16ecfaf9e8c7 100644 --- a/sound/soc/msm/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -15,15 +15,15 @@ #include #include #include -#include #include -#include -#include "qdsp6v2/msm-pcm-routing-v2.h" +#include +#include "msm-pcm-routing-v2.h" #include "sdm660-common.h" #include "sdm660-internal.h" #include "sdm660-external.h" -#include "../codecs/sdm660_cdc/msm-analog-cdc.h" -#include "../codecs/wsa881x.h" +#include "codecs/msm-cdc-pinctrl.h" +#include "codecs/sdm660_cdc/msm-analog-cdc.h" +#include "codecs/wsa881x.h" #define DRV_NAME "sdm660-asoc-snd" diff --git a/sound/soc/msm/sdm660-common.h b/asoc/sdm660-common.h similarity index 98% rename from sound/soc/msm/sdm660-common.h rename to asoc/sdm660-common.h index bca8cd788a39..682dcd5d055e 100644 --- a/sound/soc/msm/sdm660-common.h +++ b/asoc/sdm660-common.h @@ -14,8 +14,8 @@ #define __MSM_COMMON #include -#include -#include "../codecs/wcd-mbhc-v2.h" +#include +#include "codecs/wcd-mbhc-v2.h" #define SAMPLING_RATE_8KHZ 8000 #define SAMPLING_RATE_11P025KHZ 11025 diff --git a/sound/soc/msm/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c similarity index 99% rename from sound/soc/msm/sdm660-ext-dai-links.c rename to asoc/sdm660-ext-dai-links.c index 77d3875d0a06..cde28706a75e 100644 --- a/sound/soc/msm/sdm660-ext-dai-links.c +++ b/asoc/sdm660-ext-dai-links.c @@ -10,7 +10,6 @@ * GNU General Public License for more details. */ -#include #include #include #include @@ -18,9 +17,10 @@ #include #include #include "qdsp6v2/msm-pcm-routing-v2.h" -#include "../codecs/wcd9335.h" #include "sdm660-common.h" #include "sdm660-external.h" +#include "codecs/core.h" +#include "codecs/wcd9335.h" #define DEV_NAME_STR_LEN 32 #define __CHIPSET__ "SDM660 " diff --git a/sound/soc/msm/sdm660-external.c b/asoc/sdm660-external.c similarity index 99% rename from sound/soc/msm/sdm660-external.c rename to asoc/sdm660-external.c index 84d1c2ea9b18..9085282964c0 100644 --- a/sound/soc/msm/sdm660-external.c +++ b/asoc/sdm660-external.c @@ -17,15 +17,15 @@ #include #include #include -#include -#include -#include "qdsp6v2/msm-pcm-routing-v2.h" +#include +#include +#include "msm-pcm-routing-v2.h" #include "msm-audio-pinctrl.h" #include "sdm660-common.h" #include "sdm660-external.h" -#include "../codecs/wcd9335.h" -#include "../codecs/wcd934x/wcd934x.h" -#include "../codecs/wcd934x/wcd934x-mbhc.h" +#include "codecs/wcd9335.h" +#include "codecs/wcd934x/wcd934x.h" +#include "codecs/wcd934x/wcd934x-mbhc.h" #define SDM660_SPK_ON 1 #define SDM660_SPK_OFF 0 diff --git a/sound/soc/msm/sdm660-external.h b/asoc/sdm660-external.h similarity index 100% rename from sound/soc/msm/sdm660-external.h rename to asoc/sdm660-external.h diff --git a/sound/soc/msm/sdm660-internal.c b/asoc/sdm660-internal.c similarity index 99% rename from sound/soc/msm/sdm660-internal.c rename to asoc/sdm660-internal.c index a57d6f611942..0afe82896cfb 100644 --- a/sound/soc/msm/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -13,13 +13,13 @@ #include #include #include -#include #include -#include "qdsp6v2/msm-pcm-routing-v2.h" +#include "msm-pcm-routing-v2.h" #include "sdm660-common.h" -#include "../codecs/sdm660_cdc/msm-digital-cdc.h" -#include "../codecs/sdm660_cdc/msm-analog-cdc.h" -#include "../codecs/msm_sdw/msm_sdw.h" +#include "codecs/msm-cdc-pinctrl.h" +#include "codecs/sdm660_cdc/msm-digital-cdc.h" +#include "codecs/sdm660_cdc/msm-analog-cdc.h" +#include "codecs/msm_sdw/msm_sdw.h" #define __CHIPSET__ "SDM660 " #define MSM_DAILINK_NAME(name) (__CHIPSET__#name) diff --git a/sound/soc/msm/sdm660-internal.h b/asoc/sdm660-internal.h similarity index 100% rename from sound/soc/msm/sdm660-internal.h rename to asoc/sdm660-internal.h diff --git a/sound/soc/msm/sdm845.c b/asoc/sdm845.c similarity index 99% rename from sound/soc/msm/sdm845.c rename to asoc/sdm845.c index 3be194cdc48d..9d6ff120e671 100644 --- a/sound/soc/msm/sdm845.c +++ b/asoc/sdm845.c @@ -21,22 +21,22 @@ #include #include #include -#include #include #include #include #include #include -#include -#include #include #include #include -#include -#include "qdsp6v2/msm-pcm-routing-v2.h" -#include "../codecs/wcd934x/wcd934x.h" -#include "../codecs/wcd934x/wcd934x-mbhc.h" -#include "../codecs/wsa881x.h" +#include +#include +#include +#include "msm-pcm-routing-v2.h" +#include "codecs/msm-cdc-pinctrl.h" +#include "codecs/wcd934x/wcd934x.h" +#include "codecs/wcd934x/wcd934x-mbhc.h" +#include "codecs/wsa881x.h" #define DRV_NAME "sdm845-asoc-snd" diff --git a/drivers/Makefile b/drivers/Makefile deleted file mode 100644 index b48069c04d47..000000000000 --- a/drivers/Makefile +++ /dev/null @@ -1,7 +0,0 @@ - -obj-y += mfd/ -obj-y += misc/ -obj-y += soc/ -obj-y += soundwire/ -obj-y += base/ -obj-y += pinctrl/ diff --git a/drivers/base/Makefile b/drivers/base/Makefile deleted file mode 100644 index 76e50f9816bf..000000000000 --- a/drivers/base/Makefile +++ /dev/null @@ -1,2 +0,0 @@ - -obj-y += regmap/ diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile deleted file mode 100644 index a23e35ed8459..000000000000 --- a/drivers/base/regmap/Makefile +++ /dev/null @@ -1,2 +0,0 @@ - -obj-$(CONFIG_REGMAP_SWR) += regmap-swr.o diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h deleted file mode 120000 index e163b7862be6..000000000000 --- a/drivers/base/regmap/internal.h +++ /dev/null @@ -1 +0,0 @@ -../../../../../drivers/base/regmap/internal.h \ No newline at end of file diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile deleted file mode 100644 index 745691af1673..000000000000 --- a/drivers/mfd/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -wcd-core-objs := wcd9xxx-rst.o wcd9xxx-core-init.o \ - wcd9xxx-core.o wcd9xxx-irq.o \ - wcd9xxx-slimslave.o wcd9xxx-utils.o \ - wcd934x-regmap.o wcd934x-tables.o \ - wcd9335-regmap.o wcd9335-tables.o \ - msm-cdc-pinctrl.o msm-cdc-supply.o -obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd-core.o - diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile deleted file mode 100644 index f00d59de9db2..000000000000 --- a/drivers/misc/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for misc devices that really don't fit anywhere else. -# - -obj-y += qcom/ diff --git a/drivers/misc/qcom/Kconfig b/drivers/misc/qcom/Kconfig deleted file mode 100644 index e8a79605f361..000000000000 --- a/drivers/misc/qcom/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -config MSM_QDSP6V2_CODECS - bool "Audio QDSP6V2 APR support" - 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 deleted file mode 100644 index 120bdddcbc84..000000000000 --- a/drivers/misc/qcom/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-y += qdsp6v2/ diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile b/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile deleted file mode 100644 index 41f614aa4eb3..000000000000 --- a/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -ccflags-y := -I$(src)/.. -obj-$(CONFIG_MSM_ULTRASOUND) += usf.o usfcdev.o q6usm.o diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile deleted file mode 100644 index 0e0c511e18d9..000000000000 --- a/drivers/pinctrl/Makefile +++ /dev/null @@ -1,2 +0,0 @@ - -obj-y += qcom/ diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h deleted file mode 120000 index 2b68c81aa5d3..000000000000 --- a/drivers/pinctrl/core.h +++ /dev/null @@ -1 +0,0 @@ -../../../../drivers/pinctrl/core.h \ No newline at end of file diff --git a/drivers/pinctrl/pinctrl-utils.h b/drivers/pinctrl/pinctrl-utils.h deleted file mode 120000 index 3303afdf9f46..000000000000 --- a/drivers/pinctrl/pinctrl-utils.h +++ /dev/null @@ -1 +0,0 @@ -../../../../drivers/pinctrl/pinctrl-utils.h \ No newline at end of file diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile deleted file mode 100644 index cd1b749ae5ee..000000000000 --- a/drivers/pinctrl/qcom/Makefile +++ /dev/null @@ -1,3 +0,0 @@ - -obj-$(CONFIG_PINCTRL_WCD) += pinctrl-wcd.o -obj-$(CONFIG_PINCTRL_LPI) += pinctrl-lpi.o diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile deleted file mode 100644 index ae8ffae4cbc3..000000000000 --- a/drivers/soc/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for the Linux Kernel SOC specific device drivers. -# - -obj-y += qcom/ diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile deleted file mode 100644 index 33e8a12f7890..000000000000 --- a/drivers/soc/qcom/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-y += qdsp6v2/ diff --git a/drivers/soc/qcom/qdsp6v2/Makefile b/drivers/soc/qcom/qdsp6v2/Makefile deleted file mode 100644 index 9fdd63a45857..000000000000 --- a/drivers/soc/qcom/qdsp6v2/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -obj-$(CONFIG_MSM_QDSP6_APRV2_GLINK) += apr.o apr_v2.o apr_tal_glink.o -obj-$(CONFIG_MSM_QDSP6_APRV3_GLINK) += apr.o apr_v3.o apr_tal_glink.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 -obj-$(CONFIG_MSM_CDSP_LOADER) += cdsp-loader.o diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig deleted file mode 100644 index e1ca532c22f5..000000000000 --- a/drivers/soundwire/Kconfig +++ /dev/null @@ -1,17 +0,0 @@ -# -# SOUNDWIRE driver configuration -# -menuconfig SOUNDWIRE - bool "Soundwire support" - help - Soundwire is a two wire interface for audio to connect - simple peripheral components in mobile devices. - -if SOUNDWIRE -config SOUNDWIRE_WCD_CTRL - depends on WCD9XXX_CODEC_CORE - tristate "QTI WCD CODEC Soundwire controller" - default n - help - Select driver for QTI's Soundwire Master Component. -endif diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile deleted file mode 100644 index 53acff15c4c4..000000000000 --- a/drivers/soundwire/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for kernel soundwire framework. -# -obj-$(CONFIG_SOUNDWIRE) += soundwire.o -obj-$(CONFIG_SOUNDWIRE_WCD_CTRL) += swr-wcd-ctrl.o diff --git a/dsp/Makefile b/dsp/Makefile new file mode 100644 index 000000000000..a31aeb22a20b --- /dev/null +++ b/dsp/Makefile @@ -0,0 +1,10 @@ +obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += 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 adsp_err.o 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 +obj-$(CONFIG_MSM_CDSP_LOADER) += cdsp-loader.o +obj-$(CONFIG_MSM_ULTRASOUND) += usf.o usfcdev.o q6usm.o +obj-$(CONFIG_MSM_QDSP6V2_CODECS) += codecs/ diff --git a/drivers/soc/qcom/qdsp6v2/adsp-loader.c b/dsp/adsp-loader.c similarity index 99% rename from drivers/soc/qcom/qdsp6v2/adsp-loader.c rename to dsp/adsp-loader.c index d90267e01505..07eeea4e4c8b 100644 --- a/drivers/soc/qcom/qdsp6v2/adsp-loader.c +++ b/dsp/adsp-loader.c @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/sound/soc/msm/qdsp6v2/adsp_err.c b/dsp/adsp_err.c similarity index 99% rename from sound/soc/msm/qdsp6v2/adsp_err.c rename to dsp/adsp_err.c index d17bd6ab58be..319efc37cfe1 100644 --- a/sound/soc/msm/qdsp6v2/adsp_err.c +++ b/dsp/adsp_err.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include /* ERROR STRING */ diff --git a/include/linux/qdsp6v2/dsp_debug.h b/dsp/adsp_err.h similarity index 54% rename from include/linux/qdsp6v2/dsp_debug.h rename to dsp/adsp_err.h index bc1cd9ec8743..43be91d6ba8f 100644 --- a/include/linux/qdsp6v2/dsp_debug.h +++ b/dsp/adsp_err.h @@ -1,4 +1,5 @@ -/* Copyright (c) 2010, The Linux Foundation. All rights reserved. +/* + * 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 @@ -6,17 +7,15 @@ * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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); +#ifndef __ADSP_ERR__ +#define __ADSP_ERR__ + +int adsp_err_get_lnx_err_code(u32 adsp_error); -#define DSP_STATE_CRASHED 0x0 -#define DSP_STATE_CRASH_DUMP_DONE 0x1 +char *adsp_err_get_err_str(u32 adsp_error); #endif diff --git a/sound/soc/msm/qdsp6v2/audio_cal_utils.c b/dsp/audio_cal_utils.c similarity index 99% rename from sound/soc/msm/qdsp6v2/audio_cal_utils.c rename to dsp/audio_cal_utils.c index 7e69a7fe28f5..9a9a96aade7d 100644 --- a/sound/soc/msm/qdsp6v2/audio_cal_utils.c +++ b/dsp/audio_cal_utils.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include static int unmap_memory(struct cal_type_data *cal_type, struct cal_block_data *cal_block); diff --git a/sound/soc/msm/qdsp6v2/audio_calibration.c b/dsp/audio_calibration.c similarity index 99% rename from sound/soc/msm/qdsp6v2/audio_calibration.c rename to dsp/audio_calibration.c index d709b091a799..59fd464fe061 100644 --- a/sound/soc/msm/qdsp6v2/audio_calibration.c +++ b/dsp/audio_calibration.c @@ -17,9 +17,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include struct audio_cal_client_info { struct list_head list; diff --git a/drivers/soc/qcom/qdsp6v2/audio_notifier.c b/dsp/audio_notifier.c similarity index 99% rename from drivers/soc/qcom/qdsp6v2/audio_notifier.c rename to dsp/audio_notifier.c index 2320fea6e891..bcb5f9d98378 100644 --- a/drivers/soc/qcom/qdsp6v2/audio_notifier.c +++ b/dsp/audio_notifier.c @@ -12,12 +12,12 @@ #include #include -#include -#include -#include #include #include #include +#include +#include "audio_ssr.h" +#include "audio_pdr.h" /* Audio states internal to notifier. Client */ /* used states defined in audio_notifier.h */ diff --git a/drivers/soc/qcom/qdsp6v2/audio_pdr.c b/dsp/audio_pdr.c similarity index 99% rename from drivers/soc/qcom/qdsp6v2/audio_pdr.c rename to dsp/audio_pdr.c index 449d19a80b3e..0edcf0233efb 100644 --- a/drivers/soc/qcom/qdsp6v2/audio_pdr.c +++ b/dsp/audio_pdr.c @@ -12,9 +12,9 @@ #include #include -#include #include #include +#include "audio_pdr.h" static struct pd_qmi_client_data audio_pdr_services[AUDIO_PDR_DOMAIN_MAX] = { { /* AUDIO_PDR_DOMAIN_ADSP */ diff --git a/include/linux/qdsp6v2/audio_pdr.h b/dsp/audio_pdr.h similarity index 100% rename from include/linux/qdsp6v2/audio_pdr.h rename to dsp/audio_pdr.h diff --git a/sound/soc/msm/qdsp6v2/audio_slimslave.c b/dsp/audio_slimslave.c similarity index 100% rename from sound/soc/msm/qdsp6v2/audio_slimslave.c rename to dsp/audio_slimslave.c diff --git a/drivers/soc/qcom/qdsp6v2/audio_ssr.c b/dsp/audio_ssr.c similarity index 98% rename from drivers/soc/qcom/qdsp6v2/audio_ssr.c rename to dsp/audio_ssr.c index a66fb2a63fae..d3880c912cfd 100644 --- a/drivers/soc/qcom/qdsp6v2/audio_ssr.c +++ b/dsp/audio_ssr.c @@ -11,10 +11,10 @@ */ #include -#include #include #include #include +#include "audio_ssr.h" #define SCM_Q6_NMI_CMD 0x1 diff --git a/include/linux/qdsp6v2/audio_ssr.h b/dsp/audio_ssr.h similarity index 100% rename from include/linux/qdsp6v2/audio_ssr.h rename to dsp/audio_ssr.h diff --git a/drivers/soc/qcom/qdsp6v2/cdsp-loader.c b/dsp/cdsp-loader.c similarity index 100% rename from drivers/soc/qcom/qdsp6v2/cdsp-loader.c rename to dsp/cdsp-loader.c diff --git a/drivers/misc/qcom/qdsp6v2/Makefile b/dsp/codecs/Makefile similarity index 92% rename from drivers/misc/qcom/qdsp6v2/Makefile rename to dsp/codecs/Makefile index 90a123adbb7f..f9052190b273 100644 --- a/drivers/misc/qcom/qdsp6v2/Makefile +++ b/dsp/codecs/Makefile @@ -3,4 +3,3 @@ obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio 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/dsp/codecs/aac_in.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/aac_in.c rename to dsp/codecs/aac_in.c diff --git a/drivers/misc/qcom/qdsp6v2/amrnb_in.c b/dsp/codecs/amrnb_in.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/amrnb_in.c rename to dsp/codecs/amrnb_in.c diff --git a/drivers/misc/qcom/qdsp6v2/amrwb_in.c b/dsp/codecs/amrwb_in.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/amrwb_in.c rename to dsp/codecs/amrwb_in.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_aac.c b/dsp/codecs/audio_aac.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_aac.c rename to dsp/codecs/audio_aac.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_alac.c b/dsp/codecs/audio_alac.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_alac.c rename to dsp/codecs/audio_alac.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrnb.c b/dsp/codecs/audio_amrnb.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_amrnb.c rename to dsp/codecs/audio_amrnb.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwb.c b/dsp/codecs/audio_amrwb.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_amrwb.c rename to dsp/codecs/audio_amrwb.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c b/dsp/codecs/audio_amrwbplus.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c rename to dsp/codecs/audio_amrwbplus.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_ape.c b/dsp/codecs/audio_ape.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_ape.c rename to dsp/codecs/audio_ape.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_evrc.c b/dsp/codecs/audio_evrc.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_evrc.c rename to dsp/codecs/audio_evrc.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_g711alaw.c b/dsp/codecs/audio_g711alaw.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_g711alaw.c rename to dsp/codecs/audio_g711alaw.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_g711mlaw.c b/dsp/codecs/audio_g711mlaw.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_g711mlaw.c rename to dsp/codecs/audio_g711mlaw.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c b/dsp/codecs/audio_hwacc_effects.c similarity index 99% rename from drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c rename to dsp/codecs/audio_hwacc_effects.c index b8972e3d8b6e..9444aa7c6259 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c +++ b/dsp/codecs/audio_hwacc_effects.c @@ -14,8 +14,8 @@ #include #include #include "q6audio_common.h" +#include #include "audio_utils_aio.h" -#include #define MAX_CHANNELS_SUPPORTED 8 #define WAIT_TIMEDOUT_DURATION_SECS 1 diff --git a/drivers/misc/qcom/qdsp6v2/audio_mp3.c b/dsp/codecs/audio_mp3.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_mp3.c rename to dsp/codecs/audio_mp3.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c b/dsp/codecs/audio_multi_aac.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_multi_aac.c rename to dsp/codecs/audio_multi_aac.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_qcelp.c b/dsp/codecs/audio_qcelp.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_qcelp.c rename to dsp/codecs/audio_qcelp.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.c b/dsp/codecs/audio_utils.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_utils.c rename to dsp/codecs/audio_utils.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.h b/dsp/codecs/audio_utils.h similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_utils.h rename to dsp/codecs/audio_utils.h diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/dsp/codecs/audio_utils_aio.c similarity index 99% rename from drivers/misc/qcom/qdsp6v2/audio_utils_aio.c rename to dsp/codecs/audio_utils_aio.c index 80f6e57e4699..52dced4f3558 100644 --- a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c +++ b/dsp/codecs/audio_utils_aio.c @@ -24,8 +24,8 @@ #include #include #include -#include #include +#include #include "audio_utils_aio.h" #ifdef CONFIG_USE_DEV_CTRL_VOLUME #include diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h b/dsp/codecs/audio_utils_aio.h similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_utils_aio.h rename to dsp/codecs/audio_utils_aio.h diff --git a/drivers/misc/qcom/qdsp6v2/audio_wma.c b/dsp/codecs/audio_wma.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_wma.c rename to dsp/codecs/audio_wma.c diff --git a/drivers/misc/qcom/qdsp6v2/audio_wmapro.c b/dsp/codecs/audio_wmapro.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/audio_wmapro.c rename to dsp/codecs/audio_wmapro.c diff --git a/drivers/misc/qcom/qdsp6v2/evrc_in.c b/dsp/codecs/evrc_in.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/evrc_in.c rename to dsp/codecs/evrc_in.c diff --git a/drivers/misc/qcom/qdsp6v2/g711alaw_in.c b/dsp/codecs/g711alaw_in.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/g711alaw_in.c rename to dsp/codecs/g711alaw_in.c diff --git a/drivers/misc/qcom/qdsp6v2/g711mlaw_in.c b/dsp/codecs/g711mlaw_in.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/g711mlaw_in.c rename to dsp/codecs/g711mlaw_in.c diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_common.h b/dsp/codecs/q6audio_common.h similarity index 94% rename from drivers/misc/qcom/qdsp6v2/q6audio_common.h rename to dsp/codecs/q6audio_common.h index 49b88b793cc3..75f20e5ac2bd 100644 --- a/drivers/misc/qcom/qdsp6v2/q6audio_common.h +++ b/dsp/codecs/q6audio_common.h @@ -16,8 +16,8 @@ #ifndef __Q6_AUDIO_COMMON_H__ #define __Q6_AUDIO_COMMON_H__ -#include -#include +#include +#include void q6_audio_cb(uint32_t opcode, uint32_t token, diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2.c b/dsp/codecs/q6audio_v2.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/q6audio_v2.c rename to dsp/codecs/q6audio_v2.c diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c b/dsp/codecs/q6audio_v2_aio.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c rename to dsp/codecs/q6audio_v2_aio.c diff --git a/drivers/misc/qcom/qdsp6v2/qcelp_in.c b/dsp/codecs/qcelp_in.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/qcelp_in.c rename to dsp/codecs/qcelp_in.c diff --git a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c b/dsp/msm_audio_ion.c similarity index 99% rename from drivers/soc/qcom/qdsp6v2/msm_audio_ion.c rename to dsp/msm_audio_ion.c index 2f14c744cd89..24df1aa06714 100644 --- a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c +++ b/dsp/msm_audio_ion.c @@ -23,11 +23,11 @@ #include #include #include -#include +#include #include -#include #include #include +#include #define MSM_AUDIO_ION_PROBED (1 << 0) diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/dsp/q6adm.c similarity index 99% rename from sound/soc/msm/qdsp6v2/q6adm.c rename to dsp/q6adm.c index 15906054f2b8..19151139eeff 100644 --- a/sound/soc/msm/qdsp6v2/q6adm.c +++ b/dsp/q6adm.c @@ -17,15 +17,15 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include -#include "msm-dts-srs-tm-config.h" -#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" #define TIMEOUT_MS 1000 diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/dsp/q6afe.c similarity index 99% rename from sound/soc/msm/qdsp6v2/q6afe.c rename to dsp/q6afe.c index e1ce947cba48..78be555eb8eb 100644 --- a/sound/soc/msm/qdsp6v2/q6afe.c +++ b/dsp/q6afe.c @@ -18,15 +18,14 @@ #include #include #include -#include #include -#include -#include -#include -#include "msm-pcm-routing-v2.h" -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" #define WAKELOCK_TIMEOUT 5000 enum { diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/dsp/q6asm.c similarity index 99% rename from sound/soc/msm/qdsp6v2/q6asm.c rename to dsp/q6asm.c index e7e161849ff5..28b3f733bcd9 100644 --- a/sound/soc/msm/qdsp6v2/q6asm.c +++ b/dsp/q6asm.c @@ -22,25 +22,25 @@ #include #include #include -#include #include #include #include -#include #include #include #include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include "adsp_err.h" + #define TRUE 0x01 #define FALSE 0x00 #define SESSION_MAX 8 diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/dsp/q6audio-v2.c similarity index 99% rename from sound/soc/msm/qdsp6v2/q6audio-v2.c rename to dsp/q6audio-v2.c index ea78e6a6538e..6e31c366c798 100644 --- a/sound/soc/msm/qdsp6v2/q6audio-v2.c +++ b/dsp/q6audio-v2.c @@ -16,8 +16,8 @@ #include #include #include -#include -#include +#include +#include int q6audio_get_port_index(u16 port_id) { diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/dsp/q6core.c similarity index 99% rename from sound/soc/msm/qdsp6v2/q6core.c rename to dsp/q6core.c index 3aaaa35e535b..dd97a495221b 100644 --- a/sound/soc/msm/qdsp6v2/q6core.c +++ b/dsp/q6core.c @@ -18,9 +18,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #define TIMEOUT_MS 1000 /* diff --git a/sound/soc/msm/qdsp6v2/q6lsm.c b/dsp/q6lsm.c similarity index 99% rename from sound/soc/msm/qdsp6v2/q6lsm.c rename to dsp/q6lsm.c index 799d1be97ffb..adc541ae0f37 100644 --- a/sound/soc/msm/qdsp6v2/q6lsm.c +++ b/dsp/q6lsm.c @@ -23,16 +23,16 @@ #include #include #include -#include #include -#include -#include #include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" #define APR_TIMEOUT (5 * HZ) #define LSM_ALIGN_BOUNDARY 512 diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c b/dsp/q6usm.c similarity index 99% rename from drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c rename to dsp/q6usm.c index f20f335be3cb..5b822ae99358 100644 --- a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c +++ b/dsp/q6usm.c @@ -12,13 +12,12 @@ */ #include #include -#include #include #include #include -#include -#include -#include +#include +#include +#include #include "q6usm.h" #define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3 diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h b/dsp/q6usm.h similarity index 99% rename from drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h rename to dsp/q6usm.h index d45d1657c924..2e9d7c9bdb9f 100644 --- a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h +++ b/dsp/q6usm.h @@ -13,7 +13,7 @@ #ifndef __Q6_USM_H__ #define __Q6_USM_H__ -#include +#include #define Q6USM_EVENT_UNDEF 0 #define Q6USM_EVENT_READ_DONE 1 diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/dsp/q6voice.c similarity index 99% rename from sound/soc/msm/qdsp6v2/q6voice.c rename to dsp/q6voice.c index 15c9e130f4f4..cfcc822d5484 100644 --- a/sound/soc/msm/qdsp6v2/q6voice.c +++ b/dsp/q6voice.c @@ -16,17 +16,17 @@ #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 + +#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" #define TIMEOUT_MS 300 diff --git a/sound/soc/msm/qdsp6v2/rtac.c b/dsp/rtac.c similarity index 99% rename from sound/soc/msm/qdsp6v2/rtac.c rename to dsp/rtac.c index cd02501bac7b..456ac5773d64 100644 --- a/sound/soc/msm/qdsp6v2/rtac.c +++ b/dsp/rtac.c @@ -20,16 +20,15 @@ #include #include #include -#include -#include #include -#include -#include -#include -#include -#include "q6voice.h" -#include "msm-pcm-routing-v2.h" -#include +#include +#include +#include +#include +#include +#include +#include +#include "adsp_err.h" /* Max size of payload (buf size - apr header) */ diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c b/dsp/usf.c similarity index 99% rename from drivers/misc/qcom/qdsp6v2/ultrasound/usf.c rename to dsp/usf.c index 3da46b647002..dfa2097ddf67 100644 --- a/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c +++ b/dsp/usf.c @@ -24,9 +24,9 @@ #include #include #include -#include -#include +#include #include "q6usm.h" +#include "usf.h" #include "usfcdev.h" /* The driver version*/ diff --git a/include/linux/qdsp6v2/usf.h b/dsp/usf.h similarity index 100% rename from include/linux/qdsp6v2/usf.h rename to dsp/usf.h diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c b/dsp/usfcdev.c similarity index 100% rename from drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c rename to dsp/usfcdev.c diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h b/dsp/usfcdev.h similarity index 100% rename from drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h rename to dsp/usfcdev.h diff --git a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h b/include/asoc/msm-dts-srs-tm-config.h similarity index 97% rename from sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h rename to include/asoc/msm-dts-srs-tm-config.h index e3c75b3bd755..c00461b84bf0 100644 --- a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h +++ b/include/asoc/msm-dts-srs-tm-config.h @@ -1,6 +1,5 @@ /* * 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. @@ -15,6 +14,7 @@ #define _MSM_DTS_SRS_TM_CONFIG_H_ #include +#include struct param_outband; diff --git a/include/linux/mfd/wcd934x/registers.h b/include/asoc/wcd934x_registers.h similarity index 100% rename from include/linux/mfd/wcd934x/registers.h rename to include/asoc/wcd934x_registers.h diff --git a/include/sound/apr_audio-v2.h b/include/dsp/apr_audio-v2.h similarity index 99% rename from include/sound/apr_audio-v2.h rename to include/dsp/apr_audio-v2.h index 14f644579430..7fb2256bff8b 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -14,7 +14,7 @@ #ifndef _APR_AUDIO_V2_H_ #define _APR_AUDIO_V2_H_ -#include +#include #include /* size of header needed for passing data out of band */ diff --git a/include/sound/audio_cal_utils.h b/include/dsp/audio_cal_utils.h similarity index 97% rename from include/sound/audio_cal_utils.h rename to include/dsp/audio_cal_utils.h index b28b3bdf4e83..e12d8c1a9890 100644 --- a/include/sound/audio_cal_utils.h +++ b/include/dsp/audio_cal_utils.h @@ -14,9 +14,9 @@ #define _AUDIO_CAL_UTILS_H #include -#include #include -#include "audio_calibration.h" +#include +#include struct cal_data { size_t size; diff --git a/include/sound/audio_calibration.h b/include/dsp/audio_calibration.h similarity index 100% rename from include/sound/audio_calibration.h rename to include/dsp/audio_calibration.h diff --git a/include/linux/qdsp6v2/audio_notifier.h b/include/dsp/audio_notifier.h similarity index 100% rename from include/linux/qdsp6v2/audio_notifier.h rename to include/dsp/audio_notifier.h diff --git a/include/sound/msm-audio-effects-q6-v2.h b/include/dsp/msm-audio-effects-q6-v2.h similarity index 100% rename from include/sound/msm-audio-effects-q6-v2.h rename to include/dsp/msm-audio-effects-q6-v2.h diff --git a/include/dsp/msm_audio_ion.h b/include/dsp/msm_audio_ion.h new file mode 100644 index 000000000000..8a2fb6e34b99 --- /dev/null +++ b/include/dsp/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/sound/q6adm-v2.h b/include/dsp/q6adm-v2.h similarity index 98% rename from include/sound/q6adm-v2.h rename to include/dsp/q6adm-v2.h index e689e9357012..013d5229cf08 100644 --- a/include/sound/q6adm-v2.h +++ b/include/dsp/q6adm-v2.h @@ -18,9 +18,9 @@ #define ADM_PATH_NONLIVE_REC 0x3 #define ADM_PATH_COMPRESSED_RX 0x5 #define ADM_PATH_COMPRESSED_TX 0x6 -#include -#include -#include +#include +#include +#include #define MAX_MODULES_IN_TOPO 16 #define ADM_GET_TOPO_MODULE_LIST_LENGTH\ diff --git a/include/sound/q6afe-v2.h b/include/dsp/q6afe-v2.h similarity index 99% rename from include/sound/q6afe-v2.h rename to include/dsp/q6afe-v2.h index 836117568d37..7df993e61c1c 100644 --- a/include/sound/q6afe-v2.h +++ b/include/dsp/q6afe-v2.h @@ -11,8 +11,8 @@ */ #ifndef __Q6AFE_V2_H__ #define __Q6AFE_V2_H__ -#include -#include +#include +#include #define IN 0x000 #define OUT 0x001 diff --git a/include/sound/q6asm-v2.h b/include/dsp/q6asm-v2.h similarity index 99% rename from include/sound/q6asm-v2.h rename to include/dsp/q6asm-v2.h index 00b46a598280..157c243185b6 100644 --- a/include/sound/q6asm-v2.h +++ b/include/dsp/q6asm-v2.h @@ -12,9 +12,9 @@ #ifndef __Q6_ASM_V2_H__ #define __Q6_ASM_V2_H__ -#include -#include -#include +#include +#include +#include #include #include diff --git a/include/sound/q6audio-v2.h b/include/dsp/q6audio-v2.h similarity index 96% rename from include/sound/q6audio-v2.h rename to include/dsp/q6audio-v2.h index fd14f330d1d5..5df88fcbb6f0 100644 --- a/include/sound/q6audio-v2.h +++ b/include/dsp/q6audio-v2.h @@ -13,7 +13,7 @@ #ifndef _Q6_AUDIO_H_ #define _Q6_AUDIO_H_ -#include +#include enum { LEGACY_PCM_MODE = 0, diff --git a/include/sound/q6core.h b/include/dsp/q6core.h similarity index 99% rename from include/sound/q6core.h rename to include/dsp/q6core.h index 0b8309a10a66..c0327ae1f77e 100644 --- a/include/sound/q6core.h +++ b/include/dsp/q6core.h @@ -12,7 +12,7 @@ #ifndef __Q6CORE_H__ #define __Q6CORE_H__ -#include +#include diff --git a/include/sound/q6lsm.h b/include/dsp/q6lsm.h similarity index 99% rename from include/sound/q6lsm.h rename to include/dsp/q6lsm.h index 26106a8db0ce..efce3a6d2076 100644 --- a/include/sound/q6lsm.h +++ b/include/dsp/q6lsm.h @@ -15,9 +15,9 @@ #include #include -#include +#include #include -#include +#include #define MAX_NUM_CONFIDENCE 20 diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/include/dsp/q6voice.h similarity index 99% rename from sound/soc/msm/qdsp6v2/q6voice.h rename to include/dsp/q6voice.h index 74d80be57931..56326b174b38 100644 --- a/sound/soc/msm/qdsp6v2/q6voice.h +++ b/include/dsp/q6voice.h @@ -12,10 +12,10 @@ #ifndef __QDSP6VOICE_H__ #define __QDSP6VOICE_H__ -#include -#include #include #include +#include +#include #define MAX_VOC_PKT_SIZE 642 #define SESSION_NAME_LEN 20 diff --git a/include/linux/qdsp6v2/rtac.h b/include/dsp/rtac.h similarity index 98% rename from include/linux/qdsp6v2/rtac.h rename to include/dsp/rtac.h index b6f4e56db7e7..f8c5556c5455 100644 --- a/include/linux/qdsp6v2/rtac.h +++ b/include/dsp/rtac.h @@ -9,12 +9,13 @@ * but WITHOUT 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 +#include /* Voice Modes */ #define RTAC_CVP 0 diff --git a/include/linux/qdsp6v2/apr.h b/include/ipc/apr.h similarity index 100% rename from include/linux/qdsp6v2/apr.h rename to include/ipc/apr.h diff --git a/include/linux/qdsp6v2/apr_tal.h b/include/ipc/apr_tal.h similarity index 100% rename from include/linux/qdsp6v2/apr_tal.h rename to include/ipc/apr_tal.h diff --git a/include/linux/qdsp6v2/apr_us.h b/include/ipc/apr_us.h similarity index 99% rename from include/linux/qdsp6v2/apr_us.h rename to include/ipc/apr_us.h index 9a6804a4d634..6e2f63de9756 100644 --- a/include/linux/qdsp6v2/apr_us.h +++ b/include/ipc/apr_us.h @@ -13,7 +13,7 @@ #ifndef __APR_US_H__ #define __APR_US_H__ -#include +#include /* ======================================================================= */ /* Session Level commands */ diff --git a/include/linux/avtimer_kernel.h b/include/linux/avtimer_kernel.h deleted file mode 100644 index e5a12559f1ba..000000000000 --- a/include/linux/avtimer_kernel.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 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 _AVTIMER_H -#define _AVTIMER_H - -#include - -int avcs_core_open(void); -int avcs_core_disable_power_collapse(int disable);/* true or flase */ -int avcs_core_query_timer(uint64_t *avtimer_tick); - -#endif diff --git a/include/soc/internal.h b/include/soc/internal.h new file mode 120000 index 000000000000..2bc065526db0 --- /dev/null +++ b/include/soc/internal.h @@ -0,0 +1 @@ +../../../../drivers/base/regmap/internal.h \ No newline at end of file diff --git a/include/linux/soundwire/soundwire.h b/include/soc/soundwire.h similarity index 100% rename from include/linux/soundwire/soundwire.h rename to include/soc/soundwire.h diff --git a/include/linux/soundwire/swr-wcd.h b/include/soc/swr-wcd.h similarity index 100% rename from include/linux/soundwire/swr-wcd.h rename to include/soc/swr-wcd.h diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h deleted file mode 100644 index eb35645c759f..000000000000 --- a/include/sound/apr_audio.h +++ /dev/null @@ -1,1931 +0,0 @@ -/* - * - * 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/ipc/Makefile b/ipc/Makefile new file mode 100644 index 000000000000..fd840c94d178 --- /dev/null +++ b/ipc/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_MSM_QDSP6_APRV2_GLINK) += apr.o apr_v2.o apr_tal_glink.o +obj-$(CONFIG_MSM_QDSP6_APRV3_GLINK) += apr.o apr_v3.o apr_tal_glink.o diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/ipc/apr.c similarity index 99% rename from drivers/soc/qcom/qdsp6v2/apr.c rename to ipc/apr.c index e45f61eee58c..bec513ad270e 100644 --- a/drivers/soc/qcom/qdsp6v2/apr.c +++ b/ipc/apr.c @@ -26,14 +26,13 @@ #include #include #include +#include #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include #define APR_PKT_IPC_LOG_PAGE_CNT 2 diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c b/ipc/apr_tal_glink.c similarity index 99% rename from drivers/soc/qcom/qdsp6v2/apr_tal_glink.c rename to ipc/apr_tal_glink.c index fc7f63f1cae1..f99f9ed22e7a 100644 --- a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c +++ b/ipc/apr_tal_glink.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #define APR_MAXIMUM_NUM_OF_RETRIES 2 diff --git a/drivers/soc/qcom/qdsp6v2/apr_v2.c b/ipc/apr_v2.c similarity index 91% rename from drivers/soc/qcom/qdsp6v2/apr_v2.c rename to ipc/apr_v2.c index 4ddf39b1a097..d330571f3908 100644 --- a/drivers/soc/qcom/qdsp6v2/apr_v2.c +++ b/ipc/apr_v2.c @@ -15,10 +15,9 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include enum apr_subsys_state apr_get_subsys_state(void) { diff --git a/drivers/soc/qcom/qdsp6v2/apr_v3.c b/ipc/apr_v3.c similarity index 90% rename from drivers/soc/qcom/qdsp6v2/apr_v3.c rename to ipc/apr_v3.c index 2bfc518841c9..5a4ef53bb76f 100644 --- a/drivers/soc/qcom/qdsp6v2/apr_v3.c +++ b/ipc/apr_v3.c @@ -15,10 +15,9 @@ #include #include #include -#include -#include -#include -#include +#include +#include +#include #define DEST_ID APR_DEST_MODEM diff --git a/soc/Makefile b/soc/Makefile new file mode 100644 index 000000000000..c563e76ecdce --- /dev/null +++ b/soc/Makefile @@ -0,0 +1,6 @@ + +obj-$(CONFIG_REGMAP_SWR) += regmap-swr.o +obj-$(CONFIG_PINCTRL_WCD) += pinctrl-wcd.o +obj-$(CONFIG_PINCTRL_LPI) += pinctrl-lpi.o +obj-$(CONFIG_SOUNDWIRE) += soundwire.o +obj-$(CONFIG_SOUNDWIRE_WCD_CTRL) += swr-wcd-ctrl.o diff --git a/soc/core.h b/soc/core.h new file mode 120000 index 000000000000..8a2fa90df8cb --- /dev/null +++ b/soc/core.h @@ -0,0 +1 @@ +../../../drivers/pinctrl/core.h \ No newline at end of file diff --git a/drivers/pinctrl/qcom/pinctrl-lpi.c b/soc/pinctrl-lpi.c similarity index 99% rename from drivers/pinctrl/qcom/pinctrl-lpi.c rename to soc/pinctrl-lpi.c index 11f954e1eb6c..db5a9e5a4d02 100644 --- a/drivers/pinctrl/qcom/pinctrl-lpi.c +++ b/soc/pinctrl-lpi.c @@ -19,12 +19,12 @@ #include #include #include -#include #include #include +#include -#include "../core.h" -#include "../pinctrl-utils.h" +#include "core.h" +#include "pinctrl-utils.h" #define LPI_ADDRESS_SIZE 0xC000 diff --git a/soc/pinctrl-utils.h b/soc/pinctrl-utils.h new file mode 120000 index 000000000000..9aab5f24bd14 --- /dev/null +++ b/soc/pinctrl-utils.h @@ -0,0 +1 @@ +../../../drivers/pinctrl/pinctrl-utils.h \ No newline at end of file diff --git a/drivers/pinctrl/qcom/pinctrl-wcd.c b/soc/pinctrl-wcd.c similarity index 99% rename from drivers/pinctrl/qcom/pinctrl-wcd.c rename to soc/pinctrl-wcd.c index 2cc68a0c6743..d7695dbe77b8 100644 --- a/drivers/pinctrl/qcom/pinctrl-wcd.c +++ b/soc/pinctrl-wcd.c @@ -21,10 +21,10 @@ #include #include #include -#include +#include -#include "../core.h" -#include "../pinctrl-utils.h" +#include "core.h" +#include "pinctrl-utils.h" #define WCD_REG_DIR_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE #define WCD_REG_VAL_CTL WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA diff --git a/drivers/base/regmap/regmap-swr.c b/soc/regmap-swr.c similarity index 98% rename from drivers/base/regmap/regmap-swr.c rename to soc/regmap-swr.c index be1eb00ad876..ba092a2aaa70 100644 --- a/drivers/base/regmap/regmap-swr.c +++ b/soc/regmap-swr.c @@ -15,11 +15,11 @@ #include #include #include -#include #include #include +#include +#include -#include "internal.h" static int regmap_swr_gather_write(void *context, const void *reg, size_t reg_size, diff --git a/drivers/soundwire/soundwire.c b/soc/soundwire.c similarity index 99% rename from drivers/soundwire/soundwire.c rename to soc/soundwire.c index f0c7aa9ac01f..210de34e6666 100644 --- a/drivers/soundwire/soundwire.c +++ b/soc/soundwire.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include struct boardinfo { struct list_head list; diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/soc/swr-wcd-ctrl.c similarity index 99% rename from drivers/soundwire/swr-wcd-ctrl.c rename to soc/swr-wcd-ctrl.c index e338d582fa95..e7d7c797dd9e 100644 --- a/drivers/soundwire/swr-wcd-ctrl.c +++ b/soc/swr-wcd-ctrl.c @@ -17,8 +17,6 @@ #include #include #include -#include -#include #include #include #include @@ -26,6 +24,8 @@ #include #include #include +#include +#include #include "swrm_registers.h" #include "swr-wcd-ctrl.h" diff --git a/drivers/soundwire/swr-wcd-ctrl.h b/soc/swr-wcd-ctrl.h similarity index 98% rename from drivers/soundwire/swr-wcd-ctrl.h rename to soc/swr-wcd-ctrl.h index b7a3edac3e00..5a4f79b4ecf6 100644 --- a/drivers/soundwire/swr-wcd-ctrl.h +++ b/soc/swr-wcd-ctrl.h @@ -13,7 +13,7 @@ #ifndef _SWR_WCD_CTRL_H #define _SWR_WCD_CTRL_H #include -#include +#include #define SWR_MAX_ROW 0 /* Rows = 48 */ #define SWR_MAX_COL 7 /* Cols = 16 */ diff --git a/drivers/soundwire/swrm_registers.h b/soc/swrm_registers.h similarity index 100% rename from drivers/soundwire/swrm_registers.h rename to soc/swrm_registers.h diff --git a/sound/Makefile b/sound/Makefile deleted file mode 100644 index 71c713a41d38..000000000000 --- a/sound/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# MSM Machine Support - -obj-$(CONFIG_SND_SOC) += soc/msm/ -obj-$(CONFIG_SND_SOC) += soc/codecs/ diff --git a/sound/soc/codecs/msm_sdw/Kconfig b/sound/soc/codecs/msm_sdw/Kconfig deleted file mode 100644 index abd7c8c7dfb0..000000000000 --- a/sound/soc/codecs/msm_sdw/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ -config SND_SOC_MSM_SDW - tristate "MSM Internal soundwire codec" - help - MSM-based soundwire codec core driver - supported along with internal digital - codec core. diff --git a/sound/soc/codecs/sdm660_cdc/Kconfig b/sound/soc/codecs/sdm660_cdc/Kconfig deleted file mode 100644 index 2f36c393a65b..000000000000 --- a/sound/soc/codecs/sdm660_cdc/Kconfig +++ /dev/null @@ -1,5 +0,0 @@ - -config SND_SOC_SDM660_CDC - tristate "MSM Internal PMIC based codec" - select SND_SOC_WCD_MBHC - select SND_SOC_WCD_MBHC_LEGACY diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig deleted file mode 100644 index c557ae06e95f..000000000000 --- a/sound/soc/msm/Kconfig +++ /dev/null @@ -1,283 +0,0 @@ -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_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 DOLBY_LICENSE - bool "Enable Dolby LICENSE" - depends on SND_SOC_MSM_QDSP6V2_INTF - help - To add support for dolby DAP post processing, - and retain DAP set license functionality only. - This is required by Dolby GEF implementation which needs - nothing but dolby license validation functionality in driver. - -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 SDM660_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 MSM_CDC_PINCTRL - select SND_SOC_MSM_SDW - select SND_SOC_SDM660_CDC - select SND_SOC_MSM_HDMI_CODEC_RX - select QTI_PP - select DTS_SRS_TM - select DOLBY_LICENSE - select SND_HWDEP - select MSM_ULTRASOUND - select DTS_EAGLE - select SND_SOC_SDM660_COMMON - select SND_SOC_COMPRESS - select PINCTRL_LPI - 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 SDM660_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 SND_SOC_MSM_HDMI_CODEC_RX - select MFD_CORE - select QTI_PP - select DTS_SRS_TM - select DOLBY_LICENSE - select SND_SOC_CPE - select SND_SOC_WCD_CPE - select SND_HWDEP - select MSM_ULTRASOUND - select DTS_EAGLE - select SND_SOC_SDM660_COMMON - select SND_SOC_COMPRESS - select PINCTRL_LPI - 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_MACHINE_MSM8998 - tristate "SoC Machine driver for MSM8998 boards" - select SND_SOC_WSA881X - select SND_SOC_WCD9335 - select SND_SOC_WCD934X - select SND_SOC_CPE - - help - To enable the machine driver and the - corresponding DAI-links on MSM8998. - All platform specific audio modules are - enabled here. - -config SND_SOC_MSM8998 - tristate "Sound SoC drivers to interface with DSP" - 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_MSM_HDMI_CODEC_RX - select DTS_SRS_TM - select QTI_PP - select MSM_ULTRASOUND - select DOLBY_LICENSE - 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_660 - tristate "SoC Machine driver for SDM660 boards" - depends on ARCH_SDM660 - select SND_SOC_INT_CODEC - select SND_SOC_EXT_CODEC - help - To add support for SoC audio on SDM660. - 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_MACHINE_SDM845 - tristate "SoC Machine driver for SDM845 boards" - select SND_SOC_WSA881X - select SND_SOC_WCD934X - - help - To enable the machine driver and the - corresponding DAI-links on SDM845. - All platform specific audio modules are - enabled here. - -config SND_SOC_SDM845 - tristate "SoC Machine driver for SDM845 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 DTS_SRS_TM - select QTI_PP - select MSM_ULTRASOUND - select DOLBY_DS2 - select SND_HWDEP - select DTS_EAGLE - help - To add support for SoC audio on SDM845. - This enables sound soc drivers that interfaces - with DSP. This also enables the machine driver - and the corresponding DAI-links. - -endmenu diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile deleted file mode 100644 index da2b2c783a83..000000000000 --- a/sound/soc/msm/qdsp6v2/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -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-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 msm-transcode-loopback-q6-v2.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_DOLBY_DAP) += msm-dolby-dap-config.o -obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o -obj-$(CONFIG_DOLBY_LICENSE) += 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 From d1f3619befe5ca773d0f6056ddb6e5d2ab84f5c5 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Thu, 3 Aug 2017 18:54:01 +0530 Subject: [PATCH 007/276] audio-lnx: Propagate changes from kernel for wdsp-glink/avtimer Pick the WDSP-Glink and avtimer drivers from 4.9 kernel at below cutoff - (80be7d6a313 - "msm: ADSPRPC: Fix for NULL pointer dereference") This changes are done to migrate wdsp-glink/avtimer drivers to this new audio kernel techpack. Also remove the CDSP loader from techpack audio as it is non-audio related. Change-Id: I4f55429c5cbcd24920fdcb8eb2fe3aec02af320e Signed-off-by: Laxminath Kasam --- dsp/Makefile | 2 +- dsp/avtimer.c | 548 ++++++++++++++++++++ dsp/cdsp-loader.c | 280 ---------- ipc/Makefile | 1 + ipc/wcd-dsp-glink.c | 1201 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1751 insertions(+), 281 deletions(-) create mode 100644 dsp/avtimer.c delete mode 100644 dsp/cdsp-loader.c create mode 100644 ipc/wcd-dsp-glink.c diff --git a/dsp/Makefile b/dsp/Makefile index a31aeb22a20b..b609df228abe 100644 --- a/dsp/Makefile +++ b/dsp/Makefile @@ -5,6 +5,6 @@ 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 -obj-$(CONFIG_MSM_CDSP_LOADER) += cdsp-loader.o obj-$(CONFIG_MSM_ULTRASOUND) += usf.o usfcdev.o q6usm.o +obj-$(CONFIG_MSM_AVTIMER) += avtimer.o obj-$(CONFIG_MSM_QDSP6V2_CODECS) += codecs/ diff --git a/dsp/avtimer.c b/dsp/avtimer.c new file mode 100644 index 000000000000..63044db6e4b9 --- /dev/null +++ b/dsp/avtimer.c @@ -0,0 +1,548 @@ +/* + * 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 + +#define DEVICE_NAME "avtimer" +#define TIMEOUT_MS 1000 +#define CORE_CLIENT 1 +#define TEMP_PORT ((CORE_CLIENT << 8) | 0x0001) +#define SSR_WAKETIME 1000 +#define Q6_READY_RETRY 250 +#define Q6_READY_MAX_RETRIES 40 + +#define AVCS_CMD_REMOTE_AVTIMER_VOTE_REQUEST 0x00012914 +#define AVCS_CMD_RSP_REMOTE_AVTIMER_VOTE_REQUEST 0x00012915 +#define AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST 0x00012916 +#define AVTIMER_REG_CNT 2 + +struct adsp_avt_timer { + struct apr_hdr hdr; + union { + char client_name[8]; + u32 avtimer_handle; + }; +} __packed; + +static int major; + +struct avtimer_t { + struct apr_svc *core_handle_q; + struct cdev myc; + struct class *avtimer_class; + struct mutex avtimer_lock; + int avtimer_open_cnt; + struct delayed_work ssr_dwork; + wait_queue_head_t adsp_resp_wait; + int enable_timer_resp_received; + int timer_handle; + void __iomem *p_avtimer_msw; + void __iomem *p_avtimer_lsw; + uint32_t clk_div; + uint32_t clk_mult; + atomic_t adsp_ready; + int num_retries; +}; + +static struct avtimer_t avtimer; + +static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) +{ + uint32_t *payload1; + + if (!data) { + pr_err("%s: Invalid params\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) { + pr_err("%s: APR_BASIC_RSP_RESULT No Payload ", + __func__); + return 0; + } + + payload1 = data->payload; + switch (payload1[0]) { + case AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST: + pr_debug("%s: Cmd = TIMER RELEASE status[0x%x]\n", + __func__, payload1[1]); + break; + default: + pr_err("Invalid cmd rsp[0x%x][0x%x]\n", + payload1[0], payload1[1]); + break; + } + break; + } + + case RESET_EVENTS:{ + pr_debug("%s: Reset event received in AV timer\n", __func__); + apr_reset(avtimer.core_handle_q); + avtimer.core_handle_q = NULL; + avtimer.avtimer_open_cnt = 0; + atomic_set(&avtimer.adsp_ready, 0); + schedule_delayed_work(&avtimer.ssr_dwork, + msecs_to_jiffies(SSR_WAKETIME)); + break; + } + + case AVCS_CMD_RSP_REMOTE_AVTIMER_VOTE_REQUEST: + payload1 = data->payload; + pr_debug("%s: RSP_REMOTE_AVTIMER_VOTE_REQUEST handle %x\n", + __func__, payload1[0]); + avtimer.timer_handle = payload1[0]; + avtimer.enable_timer_resp_received = 1; + wake_up(&avtimer.adsp_resp_wait); + break; + default: + pr_err("%s: Message adspcore svc: %d\n", + __func__, data->opcode); + break; + } + + return 0; +} + +int avcs_core_open(void) +{ + if (!avtimer.core_handle_q) + avtimer.core_handle_q = apr_register("ADSP", "CORE", + aprv2_core_fn_q, TEMP_PORT, NULL); + pr_debug("%s: Open_q %p\n", __func__, avtimer.core_handle_q); + if (!avtimer.core_handle_q) { + pr_err("%s: Unable to register CORE\n", __func__); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(avcs_core_open); + +static int avcs_core_disable_avtimer(int timerhandle) +{ + int rc = -EINVAL; + struct adsp_avt_timer payload; + + if (!timerhandle) { + pr_err("%s: Invalid timer handle\n", __func__); + return -EINVAL; + } + memset(&payload, 0, sizeof(payload)); + rc = avcs_core_open(); + if (!rc && avtimer.core_handle_q) { + payload.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER); + payload.hdr.pkt_size = + sizeof(struct adsp_avt_timer); + payload.hdr.src_svc = avtimer.core_handle_q->id; + payload.hdr.src_domain = APR_DOMAIN_APPS; + payload.hdr.dest_domain = APR_DOMAIN_ADSP; + payload.hdr.dest_svc = APR_SVC_ADSP_CORE; + payload.hdr.src_port = TEMP_PORT; + payload.hdr.dest_port = TEMP_PORT; + payload.hdr.token = CORE_CLIENT; + payload.hdr.opcode = AVCS_CMD_REMOTE_AVTIMER_RELEASE_REQUEST; + payload.avtimer_handle = timerhandle; + pr_debug("%s: disable avtimer opcode %x handle %x\n", + __func__, payload.hdr.opcode, payload.avtimer_handle); + rc = apr_send_pkt(avtimer.core_handle_q, + (uint32_t *)&payload); + if (rc < 0) + pr_err("%s: Enable AVtimer failed op[0x%x]rc[%d]\n", + __func__, payload.hdr.opcode, rc); + else + rc = 0; + } + return rc; +} + +static int avcs_core_enable_avtimer(char *client_name) +{ + int rc = -EINVAL, ret = -EINVAL; + struct adsp_avt_timer payload; + + if (!client_name) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + memset(&payload, 0, sizeof(payload)); + rc = avcs_core_open(); + if (!rc && avtimer.core_handle_q) { + avtimer.enable_timer_resp_received = 0; + 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_avt_timer); + payload.hdr.src_svc = avtimer.core_handle_q->id; + payload.hdr.src_domain = APR_DOMAIN_APPS; + payload.hdr.dest_domain = APR_DOMAIN_ADSP; + payload.hdr.dest_svc = APR_SVC_ADSP_CORE; + payload.hdr.src_port = TEMP_PORT; + payload.hdr.dest_port = TEMP_PORT; + payload.hdr.token = CORE_CLIENT; + payload.hdr.opcode = AVCS_CMD_REMOTE_AVTIMER_VOTE_REQUEST; + strlcpy(payload.client_name, client_name, + sizeof(payload.client_name)); + pr_debug("%s: enable avtimer opcode %x client name %s\n", + __func__, payload.hdr.opcode, payload.client_name); + rc = apr_send_pkt(avtimer.core_handle_q, + (uint32_t *)&payload); + if (rc < 0) { + pr_err("%s: Enable AVtimer failed op[0x%x]rc[%d]\n", + __func__, payload.hdr.opcode, rc); + goto bail; + } else + rc = 0; + ret = wait_event_timeout(avtimer.adsp_resp_wait, + (avtimer.enable_timer_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout for Enable timer\n", + __func__); + rc = -ETIMEDOUT; + } + if (rc) + avtimer.timer_handle = 0; + } +bail: + return rc; +} + +int avcs_core_disable_power_collapse(int enable) +{ + int rc = 0; + + mutex_lock(&avtimer.avtimer_lock); + if (enable) { + if (avtimer.avtimer_open_cnt) { + avtimer.avtimer_open_cnt++; + pr_debug("%s: opened avtimer open count=%d\n", + __func__, avtimer.avtimer_open_cnt); + rc = 0; + goto done; + } + rc = avcs_core_enable_avtimer("timer"); + if (!rc) { + avtimer.avtimer_open_cnt++; + atomic_set(&avtimer.adsp_ready, 1); + } + } else { + if (avtimer.avtimer_open_cnt > 0) { + avtimer.avtimer_open_cnt--; + if (!avtimer.avtimer_open_cnt) { + rc = avcs_core_disable_avtimer( + avtimer.timer_handle); + avtimer.timer_handle = 0; + } + } + } +done: + mutex_unlock(&avtimer.avtimer_lock); + return rc; +} +EXPORT_SYMBOL(avcs_core_disable_power_collapse); + +static void reset_work(struct work_struct *work) +{ + if (q6core_is_adsp_ready()) { + avcs_core_disable_power_collapse(1); + avtimer.num_retries = Q6_READY_MAX_RETRIES; + return; + } + pr_debug("%s:Q6 not ready-retry after sometime\n", __func__); + if (--avtimer.num_retries > 0) { + schedule_delayed_work(&avtimer.ssr_dwork, + msecs_to_jiffies(Q6_READY_RETRY)); + } else { + pr_err("%s: Q6 failed responding after multiple retries\n", + __func__); + avtimer.num_retries = Q6_READY_MAX_RETRIES; + } +} + +int avcs_core_query_timer(uint64_t *avtimer_tick) +{ + uint32_t avtimer_msw = 0, avtimer_lsw = 0; + uint64_t avtimer_tick_temp; + + if (!atomic_read(&avtimer.adsp_ready)) { + pr_debug("%s:In SSR, return\n", __func__); + return -ENETRESET; + } + avtimer_lsw = ioread32(avtimer.p_avtimer_lsw); + avtimer_msw = ioread32(avtimer.p_avtimer_msw); + + avtimer_tick_temp = (uint64_t)((uint64_t)avtimer_msw << 32) + | avtimer_lsw; + *avtimer_tick = mul_u64_u32_div(avtimer_tick_temp, avtimer.clk_mult, + avtimer.clk_div); + pr_debug_ratelimited("%s:Avtimer: msw: %u, lsw: %u, tick: %llu\n", + __func__, + avtimer_msw, avtimer_lsw, *avtimer_tick); + return 0; +} +EXPORT_SYMBOL(avcs_core_query_timer); + +static int avtimer_open(struct inode *inode, struct file *file) +{ + return avcs_core_disable_power_collapse(1); +} + +static int avtimer_release(struct inode *inode, struct file *file) +{ + return avcs_core_disable_power_collapse(0); +} + +/* + * ioctl call provides GET_AVTIMER + */ +static long avtimer_ioctl(struct file *file, unsigned int ioctl_num, + unsigned long ioctl_param) +{ + switch (ioctl_num) { + case IOCTL_GET_AVTIMER_TICK: + { + uint64_t avtimer_tick = 0; + int rc; + + rc = avcs_core_query_timer(&avtimer_tick); + + if (rc) { + pr_err("%s: Error: Invalid AV Timer tick, rc = %d\n", + __func__, rc); + return rc; + } + + pr_debug_ratelimited("%s: AV Timer tick: time %llx\n", + __func__, avtimer_tick); + if (copy_to_user((void *) ioctl_param, &avtimer_tick, + sizeof(avtimer_tick))) { + pr_err("%s: copy_to_user failed\n", __func__); + return -EFAULT; + } + } + break; + + default: + pr_err("%s: invalid cmd\n", __func__); + return -EINVAL; + } + return 0; +} + +static const struct file_operations avtimer_fops = { + .unlocked_ioctl = avtimer_ioctl, + .compat_ioctl = avtimer_ioctl, + .open = avtimer_open, + .release = avtimer_release +}; + +static int dev_avtimer_probe(struct platform_device *pdev) +{ + int result = 0; + dev_t dev = MKDEV(major, 0); + struct device *device_handle; + struct resource *reg_lsb = NULL, *reg_msb = NULL; + uint32_t clk_div_val; + uint32_t clk_mult_val; + + if (!pdev) { + pr_err("%s: Invalid params\n", __func__); + return -EINVAL; + } + reg_lsb = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "avtimer_lsb_addr"); + if (!reg_lsb) { + dev_err(&pdev->dev, "%s: Looking up %s property", + "avtimer_lsb_addr", __func__); + return -EINVAL; + } + reg_msb = platform_get_resource_byname(pdev, + IORESOURCE_MEM, "avtimer_msb_addr"); + if (!reg_msb) { + dev_err(&pdev->dev, "%s: Looking up %s property", + "avtimer_msb_addr", __func__); + return -EINVAL; + } + INIT_DELAYED_WORK(&avtimer.ssr_dwork, reset_work); + + avtimer.p_avtimer_lsw = devm_ioremap_nocache(&pdev->dev, + reg_lsb->start, resource_size(reg_lsb)); + if (!avtimer.p_avtimer_lsw) { + dev_err(&pdev->dev, "%s: ioremap failed for lsb avtimer register", + __func__); + return -ENOMEM; + } + + avtimer.p_avtimer_msw = devm_ioremap_nocache(&pdev->dev, + reg_msb->start, resource_size(reg_msb)); + if (!avtimer.p_avtimer_msw) { + dev_err(&pdev->dev, "%s: ioremap failed for msb avtimer register", + __func__); + goto unmap; + } + avtimer.num_retries = Q6_READY_MAX_RETRIES; + /* get the device number */ + if (major) + result = register_chrdev_region(dev, 1, DEVICE_NAME); + else { + result = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME); + major = MAJOR(dev); + } + + if (result < 0) { + dev_err(&pdev->dev, "%s: Registering avtimer device failed\n", + __func__); + goto unmap; + } + + avtimer.avtimer_class = class_create(THIS_MODULE, "avtimer"); + if (IS_ERR(avtimer.avtimer_class)) { + result = PTR_ERR(avtimer.avtimer_class); + dev_err(&pdev->dev, "%s: Error creating avtimer class: %d\n", + __func__, result); + goto unregister_chrdev_region; + } + + cdev_init(&avtimer.myc, &avtimer_fops); + result = cdev_add(&avtimer.myc, dev, 1); + + if (result < 0) { + dev_err(&pdev->dev, "%s: Registering file operations failed\n", + __func__); + goto class_destroy; + } + + device_handle = device_create(avtimer.avtimer_class, + NULL, avtimer.myc.dev, NULL, "avtimer"); + if (IS_ERR(device_handle)) { + result = PTR_ERR(device_handle); + pr_err("%s: device_create failed: %d\n", __func__, result); + goto class_destroy; + } + init_waitqueue_head(&avtimer.adsp_resp_wait); + mutex_init(&avtimer.avtimer_lock); + avtimer.avtimer_open_cnt = 0; + + pr_debug("%s: Device create done for avtimer major=%d\n", + __func__, major); + + if (of_property_read_u32(pdev->dev.of_node, + "qcom,clk-div", &clk_div_val)) + avtimer.clk_div = 1; + else + avtimer.clk_div = clk_div_val; + + if (of_property_read_u32(pdev->dev.of_node, + "qcom,clk-mult", &clk_mult_val)) + avtimer.clk_mult = 1; + else + avtimer.clk_mult = clk_mult_val; + + pr_debug("%s: avtimer.clk_div = %d, avtimer.clk_mult = %d\n", + __func__, avtimer.clk_div, avtimer.clk_mult); + return 0; + +class_destroy: + class_destroy(avtimer.avtimer_class); +unregister_chrdev_region: + unregister_chrdev_region(MKDEV(major, 0), 1); +unmap: + if (avtimer.p_avtimer_lsw) + devm_iounmap(&pdev->dev, avtimer.p_avtimer_lsw); + if (avtimer.p_avtimer_msw) + devm_iounmap(&pdev->dev, avtimer.p_avtimer_msw); + avtimer.p_avtimer_lsw = NULL; + avtimer.p_avtimer_msw = NULL; + return result; + +} + +static int dev_avtimer_remove(struct platform_device *pdev) +{ + pr_debug("%s: dev_avtimer_remove\n", __func__); + + if (avtimer.p_avtimer_lsw) + devm_iounmap(&pdev->dev, avtimer.p_avtimer_lsw); + if (avtimer.p_avtimer_msw) + devm_iounmap(&pdev->dev, avtimer.p_avtimer_msw); + device_destroy(avtimer.avtimer_class, avtimer.myc.dev); + cdev_del(&avtimer.myc); + class_destroy(avtimer.avtimer_class); + unregister_chrdev_region(MKDEV(major, 0), 1); + + return 0; +} + +static const struct of_device_id avtimer_machine_of_match[] = { + { .compatible = "qcom,avtimer", }, + {}, +}; +static struct platform_driver dev_avtimer_driver = { + .probe = dev_avtimer_probe, + .remove = dev_avtimer_remove, + .driver = { + .name = "dev_avtimer", + .of_match_table = avtimer_machine_of_match, + }, +}; + +static int __init avtimer_init(void) +{ + s32 rc; + + rc = platform_driver_register(&dev_avtimer_driver); + if (rc < 0) { + pr_err("%s: platform_driver_register failed\n", __func__); + goto error_platform_driver; + } + pr_debug("%s: dev_avtimer_init : done\n", __func__); + + return 0; +error_platform_driver: + + pr_err("%s: encounterd error\n", __func__); + return rc; +} + +static void __exit avtimer_exit(void) +{ + pr_debug("%s: avtimer_exit\n", __func__); + platform_driver_unregister(&dev_avtimer_driver); +} + +module_init(avtimer_init); +module_exit(avtimer_exit); + +MODULE_DESCRIPTION("avtimer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/dsp/cdsp-loader.c b/dsp/cdsp-loader.c deleted file mode 100644 index 70977d36f0c9..000000000000 --- a/dsp/cdsp-loader.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 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 BOOT_CMD 1 -#define IMAGE_UNLOAD_CMD 0 - -#define CDSP_SUBSYS_DOWN 0 -#define CDSP_SUBSYS_LOADED 1 - -static ssize_t cdsp_boot_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, size_t count); - -struct cdsp_loader_private { - void *pil_h; - struct kobject *boot_cdsp_obj; - struct attribute_group *attr_group; -}; - -static struct kobj_attribute cdsp_boot_attribute = - __ATTR(boot, 0220, NULL, cdsp_boot_store); - -static struct attribute *attrs[] = { - &cdsp_boot_attribute.attr, - NULL, -}; - -static u32 cdsp_state = CDSP_SUBSYS_DOWN; -static struct platform_device *cdsp_private; -static struct work_struct cdsp_ldr_work; -static void cdsp_loader_unload(struct platform_device *pdev); - -static void cdsp_load_fw(struct work_struct *cdsp_ldr_work) -{ - struct platform_device *pdev = cdsp_private; - struct cdsp_loader_private *priv = NULL; - - int rc = 0; - 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_string(pdev->dev.of_node, - "qcom,proc-img-to-load", - &img_name); - if (rc) - goto fail; - - if (!strcmp(img_name, "cdsp")) { - /* cdsp_state always returns "0".*/ - if (cdsp_state == CDSP_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("cdsp"); - if (IS_ERR(priv->pil_h)) { - dev_err(&pdev->dev, "%s: pil get failed,\n", - __func__); - goto fail; - } - - /* Set the state of the CDSP.*/ - cdsp_state = CDSP_SUBSYS_LOADED; - } else if (cdsp_state == CDSP_SUBSYS_LOADED) { - dev_dbg(&pdev->dev, - "%s: CDSP state = %x\n", __func__, cdsp_state); - } - - dev_dbg(&pdev->dev, "%s: CDSP image is loaded\n", __func__); - return; - } - -fail: - dev_err(&pdev->dev, "%s: CDSP image loading failed\n", __func__); -} - -static void cdsp_loader_do(struct platform_device *pdev) -{ - schedule_work(&cdsp_ldr_work); -} - -static ssize_t cdsp_boot_store(struct kobject *kobj, - struct kobj_attribute *attr, - const char *buf, - size_t count) -{ - int boot = 0, ret = 0; - - ret = sscanf(buf, "%du", &boot); - - if (ret != 1) - pr_debug("%s: invalid arguments for cdsp_loader.\n", __func__); - - if (boot == BOOT_CMD) { - pr_debug("%s: going to call cdsp_loader_do\n", __func__); - cdsp_loader_do(cdsp_private); - } else if (boot == IMAGE_UNLOAD_CMD) { - pr_debug("%s: going to call cdsp_unloader\n", __func__); - cdsp_loader_unload(cdsp_private); - } - return count; -} - -static void cdsp_loader_unload(struct platform_device *pdev) -{ - struct cdsp_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 cdsp_loader_init_sysfs(struct platform_device *pdev) -{ - int ret = -EINVAL; - struct cdsp_loader_private *priv = NULL; - - cdsp_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_cdsp_obj = NULL; - priv->attr_group = devm_kzalloc(&pdev->dev, - sizeof(*(priv->attr_group)), - GFP_KERNEL); - if (!priv->attr_group) { - dev_err(&pdev->dev, "%s: malloc attr_group failed\n", - __func__); - ret = -ENOMEM; - goto error_return; - } - - priv->attr_group->attrs = attrs; - - priv->boot_cdsp_obj = kobject_create_and_add("boot_cdsp", kernel_kobj); - if (!priv->boot_cdsp_obj) { - dev_err(&pdev->dev, "%s: sysfs create and add failed\n", - __func__); - ret = -ENOMEM; - goto error_return; - } - - ret = sysfs_create_group(priv->boot_cdsp_obj, priv->attr_group); - if (ret) { - dev_err(&pdev->dev, "%s: sysfs create group failed %d\n", - __func__, ret); - goto error_return; - } - - cdsp_private = pdev; - - return 0; - -error_return: - - if (priv->boot_cdsp_obj) { - kobject_del(priv->boot_cdsp_obj); - priv->boot_cdsp_obj = NULL; - } - - return ret; -} - -static int cdsp_loader_remove(struct platform_device *pdev) -{ - struct cdsp_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_cdsp_obj) { - sysfs_remove_group(priv->boot_cdsp_obj, priv->attr_group); - kobject_del(priv->boot_cdsp_obj); - priv->boot_cdsp_obj = NULL; - } - - return 0; -} - -static int cdsp_loader_probe(struct platform_device *pdev) -{ - int ret = cdsp_loader_init_sysfs(pdev); - - if (ret != 0) { - dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__); - return ret; - } - - INIT_WORK(&cdsp_ldr_work, cdsp_load_fw); - - return 0; -} - -static const struct of_device_id cdsp_loader_dt_match[] = { - { .compatible = "qcom,cdsp-loader" }, - { } -}; -MODULE_DEVICE_TABLE(of, cdsp_loader_dt_match); - -static struct platform_driver cdsp_loader_driver = { - .driver = { - .name = "cdsp-loader", - .owner = THIS_MODULE, - .of_match_table = cdsp_loader_dt_match, - }, - .probe = cdsp_loader_probe, - .remove = cdsp_loader_remove, -}; - -static int __init cdsp_loader_init(void) -{ - return platform_driver_register(&cdsp_loader_driver); -} -module_init(cdsp_loader_init); - -static void __exit cdsp_loader_exit(void) -{ - platform_driver_unregister(&cdsp_loader_driver); -} -module_exit(cdsp_loader_exit); - -MODULE_DESCRIPTION("CDSP Loader module"); -MODULE_LICENSE("GPL v2"); diff --git a/ipc/Makefile b/ipc/Makefile index fd840c94d178..69389153b7c4 100644 --- a/ipc/Makefile +++ b/ipc/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_MSM_QDSP6_APRV2_GLINK) += apr.o apr_v2.o apr_tal_glink.o obj-$(CONFIG_MSM_QDSP6_APRV3_GLINK) += apr.o apr_v3.o apr_tal_glink.o +obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o diff --git a/ipc/wcd-dsp-glink.c b/ipc/wcd-dsp-glink.c new file mode 100644 index 000000000000..870b9f7455d3 --- /dev/null +++ b/ipc/wcd-dsp-glink.c @@ -0,0 +1,1201 @@ +/* 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 "sound/wcd-dsp-glink.h" + +#define WDSP_GLINK_DRIVER_NAME "wcd-dsp-glink" +#define WDSP_MAX_WRITE_SIZE (256 * 1024) +#define WDSP_MAX_READ_SIZE (4 * 1024) +#define WDSP_MAX_NO_OF_INTENTS (20) +#define WDSP_MAX_NO_OF_CHANNELS (10) +#define WDSP_WRITE_PKT_SIZE (sizeof(struct wdsp_write_pkt)) +#define WDSP_REG_PKT_SIZE (sizeof(struct wdsp_reg_pkt)) +#define WDSP_CMD_PKT_SIZE (sizeof(struct wdsp_cmd_pkt)) +#define WDSP_CH_CFG_SIZE (sizeof(struct wdsp_glink_ch_cfg)) + +#define MINOR_NUMBER_COUNT 1 +#define WDSP_EDGE "wdsp" +#define RESP_QUEUE_SIZE 3 +#define QOS_PKT_SIZE 1024 +#define TIMEOUT_MS 1000 + +struct wdsp_glink_dev { + struct class *cls; + struct device *dev; + struct cdev cdev; + dev_t dev_num; +}; + +struct wdsp_glink_rsp_que { + /* Size of valid data in buffer */ + u32 buf_size; + + /* Response buffer */ + u8 buf[WDSP_MAX_READ_SIZE]; +}; + +struct wdsp_glink_tx_buf { + struct work_struct tx_work; + struct work_struct free_tx_work; + + /* Glink channel information */ + struct wdsp_glink_ch *ch; + + /* Tx buffer to send to glink */ + u8 buf[0]; +}; + +struct wdsp_glink_ch { + struct wdsp_glink_priv *wpriv; + + /* Glink channel handle */ + void *handle; + + /* Channel states like connect, disconnect */ + int channel_state; + struct mutex mutex; + + /* To free up the channel memory */ + bool free_mem; + + /* Glink local channel open work */ + struct work_struct lcl_ch_open_wrk; + + /* Glink local channel close work */ + struct work_struct lcl_ch_cls_wrk; + + /* Wait for ch connect state before sending any command */ + wait_queue_head_t ch_connect_wait; + + /* + * Glink channel configuration. This has to be the last + * member of the strucuture as it has variable size + */ + struct wdsp_glink_ch_cfg ch_cfg; +}; + +struct wdsp_glink_state { + /* Glink link state information */ + enum glink_link_state link_state; + void *handle; +}; + +struct wdsp_glink_priv { + /* Respone buffer related */ + u8 rsp_cnt; + struct wdsp_glink_rsp_que rsp[RESP_QUEUE_SIZE]; + struct completion rsp_complete; + struct mutex rsp_mutex; + + /* Glink channel related */ + struct mutex glink_mutex; + struct wdsp_glink_state glink_state; + struct wdsp_glink_ch **ch; + u8 no_of_channels; + struct work_struct ch_open_cls_wrk; + struct workqueue_struct *work_queue; + + wait_queue_head_t link_state_wait; + + struct device *dev; +}; + +static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch); +static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch); + +/* + * wdsp_glink_free_tx_buf_work - Work function to free tx pkt + * work: Work structure + */ +static void wdsp_glink_free_tx_buf_work(struct work_struct *work) +{ + struct wdsp_glink_tx_buf *tx_buf; + + tx_buf = container_of(work, struct wdsp_glink_tx_buf, + free_tx_work); + vfree(tx_buf); +} + +/* + * wdsp_glink_free_tx_buf - Function to free tx buffer + * priv: Pointer to the channel + * pkt_priv: Pointer to the tx buffer + */ +static void wdsp_glink_free_tx_buf(const void *priv, const void *pkt_priv) +{ + struct wdsp_glink_tx_buf *tx_buf = (struct wdsp_glink_tx_buf *)pkt_priv; + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_ch *ch; + + if (!priv) { + pr_err("%s: Invalid priv\n", __func__); + return; + } + if (!tx_buf) { + pr_err("%s: Invalid tx_buf\n", __func__); + return; + } + + ch = (struct wdsp_glink_ch *)priv; + wpriv = ch->wpriv; + /* Work queue to free tx pkt */ + INIT_WORK(&tx_buf->free_tx_work, wdsp_glink_free_tx_buf_work); + queue_work(wpriv->work_queue, &tx_buf->free_tx_work); +} + +/* + * wdsp_glink_notify_rx - Glink notify rx callback for responses + * handle: Opaque Channel handle returned by GLink + * priv: Private pointer to the channel + * pkt_priv: Private pointer to the packet + * ptr: Pointer to the Rx data + * size: Size of the Rx data + */ +static void wdsp_glink_notify_rx(void *handle, const void *priv, + const void *pkt_priv, const void *ptr, + size_t size) +{ + u8 *rx_buf; + u8 rsp_cnt; + struct wdsp_glink_ch *ch; + struct wdsp_glink_priv *wpriv; + + if (!ptr || !priv) { + pr_err("%s: Invalid parameters\n", __func__); + return; + } + + ch = (struct wdsp_glink_ch *)priv; + wpriv = ch->wpriv; + rx_buf = (u8 *)ptr; + if (size > WDSP_MAX_READ_SIZE) { + dev_err(wpriv->dev, "%s: Size %zd is greater than allowed %d\n", + __func__, size, WDSP_MAX_READ_SIZE); + size = WDSP_MAX_READ_SIZE; + } + + mutex_lock(&wpriv->rsp_mutex); + rsp_cnt = wpriv->rsp_cnt; + if (rsp_cnt >= RESP_QUEUE_SIZE) { + dev_err(wpriv->dev, "%s: Resp Queue is Full\n", __func__); + rsp_cnt = 0; + } + dev_dbg(wpriv->dev, "%s: copy into buffer %d\n", __func__, rsp_cnt); + + memcpy(wpriv->rsp[rsp_cnt].buf, rx_buf, size); + wpriv->rsp[rsp_cnt].buf_size = size; + wpriv->rsp_cnt = ++rsp_cnt; + mutex_unlock(&wpriv->rsp_mutex); + + glink_rx_done(handle, ptr, true); + complete(&wpriv->rsp_complete); +} + +/* + * wdsp_glink_notify_tx_done - Glink notify tx done callback to + * free tx buffer + * handle: Opaque Channel handle returned by GLink + * priv: Private pointer to the channel + * pkt_priv: Private pointer to the packet + * ptr: Pointer to the Tx data + */ +static void wdsp_glink_notify_tx_done(void *handle, const void *priv, + const void *pkt_priv, const void *ptr) +{ + wdsp_glink_free_tx_buf(priv, pkt_priv); +} +/* + * wdsp_glink_notify_tx_abort - Glink notify tx abort callback to + * free tx buffer + * handle: Opaque Channel handle returned by GLink + * priv: Private pointer to the channel + * pkt_priv: Private pointer to the packet + */ +static void wdsp_glink_notify_tx_abort(void *handle, const void *priv, + const void *pkt_priv) +{ + wdsp_glink_free_tx_buf(priv, pkt_priv); +} + +/* + * wdsp_glink_notify_rx_intent_req - Glink notify rx intent request callback + * to queue buffer to receive from remote client + * handle: Opaque channel handle returned by GLink + * priv: Private pointer to the channel + * req_size: Size of intent to be queued + */ +static bool wdsp_glink_notify_rx_intent_req(void *handle, const void *priv, + size_t req_size) +{ + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_ch *ch; + int rc = 0; + bool ret = false; + + if (!priv) { + pr_err("%s: Invalid priv\n", __func__); + goto done; + } + if (req_size > WDSP_MAX_READ_SIZE) { + pr_err("%s: Invalid req_size %zd\n", __func__, req_size); + goto done; + } + + ch = (struct wdsp_glink_ch *)priv; + wpriv = ch->wpriv; + + dev_dbg(wpriv->dev, "%s: intent size %zd requested for ch name %s", + __func__, req_size, ch->ch_cfg.name); + + mutex_lock(&ch->mutex); + rc = glink_queue_rx_intent(ch->handle, ch, req_size); + if (rc < 0) { + dev_err(wpriv->dev, "%s: Failed to queue rx intent, rc = %d\n", + __func__, rc); + mutex_unlock(&ch->mutex); + goto done; + } + mutex_unlock(&ch->mutex); + ret = true; + +done: + return ret; +} + +/* + * wdsp_glink_lcl_ch_open_wrk - Work function to open channel again + * when local disconnect event happens + * work: Work structure + */ +static void wdsp_glink_lcl_ch_open_wrk(struct work_struct *work) +{ + struct wdsp_glink_ch *ch; + + ch = container_of(work, struct wdsp_glink_ch, + lcl_ch_open_wrk); + + wdsp_glink_open_ch(ch); +} + +/* + * wdsp_glink_lcl_ch_cls_wrk - Work function to close channel locally + * when remote disconnect event happens + * work: Work structure + */ +static void wdsp_glink_lcl_ch_cls_wrk(struct work_struct *work) +{ + struct wdsp_glink_ch *ch; + + ch = container_of(work, struct wdsp_glink_ch, + lcl_ch_cls_wrk); + + wdsp_glink_close_ch(ch); +} + +/* + * wdsp_glink_notify_state - Glink channel state information event callback + * handle: Opaque Channel handle returned by GLink + * priv: Private pointer to the channel + * event: channel state event + */ +static void wdsp_glink_notify_state(void *handle, const void *priv, + unsigned int event) +{ + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_ch *ch; + int i, ret = 0; + + if (!priv) { + pr_err("%s: Invalid priv\n", __func__); + return; + } + + ch = (struct wdsp_glink_ch *)priv; + wpriv = ch->wpriv; + + mutex_lock(&ch->mutex); + ch->channel_state = event; + if (event == GLINK_CONNECTED) { + dev_dbg(wpriv->dev, "%s: glink channel: %s connected\n", + __func__, ch->ch_cfg.name); + + for (i = 0; i < ch->ch_cfg.no_of_intents; i++) { + dev_dbg(wpriv->dev, "%s: intent_size = %d\n", __func__, + ch->ch_cfg.intents_size[i]); + ret = glink_queue_rx_intent(ch->handle, ch, + ch->ch_cfg.intents_size[i]); + if (ret < 0) + dev_warn(wpriv->dev, "%s: Failed to queue intent %d of size %d\n", + __func__, i, + ch->ch_cfg.intents_size[i]); + } + + ret = glink_qos_latency(ch->handle, ch->ch_cfg.latency_in_us, + QOS_PKT_SIZE); + if (ret < 0) + dev_warn(wpriv->dev, "%s: Failed to request qos %d for ch %s\n", + __func__, ch->ch_cfg.latency_in_us, + ch->ch_cfg.name); + + wake_up(&ch->ch_connect_wait); + mutex_unlock(&ch->mutex); + } else if (event == GLINK_LOCAL_DISCONNECTED) { + /* + * Don't use dev_dbg here as dev may not be valid if channel + * closed from driver close. + */ + pr_debug("%s: channel: %s disconnected locally\n", + __func__, ch->ch_cfg.name); + mutex_unlock(&ch->mutex); + + if (ch->free_mem) { + kfree(ch); + ch = NULL; + } + } else if (event == GLINK_REMOTE_DISCONNECTED) { + dev_dbg(wpriv->dev, "%s: remote channel: %s disconnected remotely\n", + __func__, ch->ch_cfg.name); + mutex_unlock(&ch->mutex); + /* + * If remote disconnect happens, local side also has + * to close the channel as per glink design in a + * separate work_queue. + */ + queue_work(wpriv->work_queue, &ch->lcl_ch_cls_wrk); + } +} + +/* + * wdsp_glink_close_ch - Internal function to close glink channel + * ch: Glink Channel structure. + */ +static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch) +{ + struct wdsp_glink_priv *wpriv = ch->wpriv; + int ret = 0; + + mutex_lock(&wpriv->glink_mutex); + if (ch->handle) { + ret = glink_close(ch->handle); + if (ret < 0) { + dev_err(wpriv->dev, "%s: glink_close is failed, ret = %d\n", + __func__, ret); + } else { + ch->handle = NULL; + dev_dbg(wpriv->dev, "%s: ch %s is closed\n", __func__, + ch->ch_cfg.name); + } + } else { + dev_dbg(wpriv->dev, "%s: ch %s is already closed\n", __func__, + ch->ch_cfg.name); + } + mutex_unlock(&wpriv->glink_mutex); + + + return ret; +} + +/* + * wdsp_glink_open_ch - Internal function to open glink channel + * ch: Glink Channel structure. + */ +static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch) +{ + struct wdsp_glink_priv *wpriv = ch->wpriv; + struct glink_open_config open_cfg; + int ret = 0; + + mutex_lock(&wpriv->glink_mutex); + if (!ch->handle) { + memset(&open_cfg, 0, sizeof(open_cfg)); + open_cfg.options = GLINK_OPT_INITIAL_XPORT; + open_cfg.edge = WDSP_EDGE; + open_cfg.notify_rx = wdsp_glink_notify_rx; + open_cfg.notify_tx_done = wdsp_glink_notify_tx_done; + open_cfg.notify_tx_abort = wdsp_glink_notify_tx_abort; + open_cfg.notify_state = wdsp_glink_notify_state; + open_cfg.notify_rx_intent_req = wdsp_glink_notify_rx_intent_req; + open_cfg.priv = ch; + open_cfg.name = ch->ch_cfg.name; + + dev_dbg(wpriv->dev, "%s: ch->ch_cfg.name = %s, latency_in_us = %d, intents = %d\n", + __func__, ch->ch_cfg.name, ch->ch_cfg.latency_in_us, + ch->ch_cfg.no_of_intents); + + ch->handle = glink_open(&open_cfg); + if (IS_ERR_OR_NULL(ch->handle)) { + dev_err(wpriv->dev, "%s: glink_open failed for ch %s\n", + __func__, ch->ch_cfg.name); + ch->handle = NULL; + ret = -EINVAL; + } + } else { + dev_err(wpriv->dev, "%s: ch %s is already opened\n", __func__, + ch->ch_cfg.name); + } + mutex_unlock(&wpriv->glink_mutex); + + return ret; +} + +/* + * wdsp_glink_close_all_ch - Internal function to close all glink channels + * wpriv: Wdsp_glink private structure + */ +static void wdsp_glink_close_all_ch(struct wdsp_glink_priv *wpriv) +{ + int i; + + for (i = 0; i < wpriv->no_of_channels; i++) + if (wpriv->ch && wpriv->ch[i]) + wdsp_glink_close_ch(wpriv->ch[i]); +} + +/* + * wdsp_glink_open_all_ch - Internal function to open all glink channels + * wpriv: Wdsp_glink private structure + */ +static int wdsp_glink_open_all_ch(struct wdsp_glink_priv *wpriv) +{ + int ret = 0, i, j; + + for (i = 0; i < wpriv->no_of_channels; i++) { + if (wpriv->ch && wpriv->ch[i]) { + ret = wdsp_glink_open_ch(wpriv->ch[i]); + if (ret < 0) + goto err_open; + } + } + goto done; + +err_open: + for (j = 0; j < i; j++) + if (wpriv->ch[i]) + wdsp_glink_close_ch(wpriv->ch[j]); + +done: + return ret; +} + +/* + * wdsp_glink_ch_open_wq - Work function to open glink channels + * work: Work structure + */ +static void wdsp_glink_ch_open_cls_wrk(struct work_struct *work) +{ + struct wdsp_glink_priv *wpriv; + + wpriv = container_of(work, struct wdsp_glink_priv, + ch_open_cls_wrk); + + if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) { + dev_info(wpriv->dev, "%s: GLINK_LINK_STATE_DOWN\n", + __func__); + + wdsp_glink_close_all_ch(wpriv); + } else if (wpriv->glink_state.link_state == GLINK_LINK_STATE_UP) { + dev_info(wpriv->dev, "%s: GLINK_LINK_STATE_UP\n", + __func__); + + wdsp_glink_open_all_ch(wpriv); + } +} + +/* + * wdsp_glink_link_state_cb - Glink link state callback to inform + * about link states + * cb_info: Glink link state callback information structure + * priv: Private structure of link state passed while register + */ +static void wdsp_glink_link_state_cb(struct glink_link_state_cb_info *cb_info, + void *priv) +{ + struct wdsp_glink_priv *wpriv; + + if (!cb_info || !priv) { + pr_err("%s: Invalid parameters\n", __func__); + return; + } + + wpriv = (struct wdsp_glink_priv *)priv; + + mutex_lock(&wpriv->glink_mutex); + wpriv->glink_state.link_state = cb_info->link_state; + wake_up(&wpriv->link_state_wait); + mutex_unlock(&wpriv->glink_mutex); + + queue_work(wpriv->work_queue, &wpriv->ch_open_cls_wrk); +} + +/* + * wdsp_glink_ch_info_init- Internal function to allocate channel memory + * and register with glink + * wpriv: Wdsp_glink private structure. + * pkt: Glink registration packet contains glink channel information. + * pkt_size: Size of the pkt. + */ +static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, + struct wdsp_reg_pkt *pkt, size_t pkt_size) +{ + int ret = 0, i, j; + struct glink_link_info link_info; + struct wdsp_glink_ch_cfg *ch_cfg; + struct wdsp_glink_ch **ch; + u8 no_of_channels; + u8 *payload; + u32 ch_size, ch_cfg_size; + size_t size = WDSP_WRITE_PKT_SIZE + WDSP_REG_PKT_SIZE; + + mutex_lock(&wpriv->glink_mutex); + if (wpriv->ch) { + dev_err(wpriv->dev, "%s: glink ch memory is already allocated\n", + __func__); + ret = -EINVAL; + goto done; + } + payload = (u8 *)pkt->payload; + no_of_channels = pkt->no_of_channels; + + if (no_of_channels > WDSP_MAX_NO_OF_CHANNELS) { + dev_err(wpriv->dev, "%s: no_of_channels: %d but max allowed are %d\n", + __func__, no_of_channels, WDSP_MAX_NO_OF_CHANNELS); + ret = -EINVAL; + goto done; + } + ch = kcalloc(no_of_channels, sizeof(struct wdsp_glink_ch *), + GFP_ATOMIC); + if (!ch) { + ret = -ENOMEM; + goto done; + } + wpriv->ch = ch; + wpriv->no_of_channels = no_of_channels; + + for (i = 0; i < no_of_channels; i++) { + ch_cfg = (struct wdsp_glink_ch_cfg *)payload; + + size += WDSP_CH_CFG_SIZE; + if (size > pkt_size) { + dev_err(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", + __func__, size, pkt_size); + ret = -EINVAL; + goto err_ch_mem; + } + if (ch_cfg->no_of_intents > WDSP_MAX_NO_OF_INTENTS) { + dev_err(wpriv->dev, "%s: Invalid no_of_intents = %d\n", + __func__, ch_cfg->no_of_intents); + ret = -EINVAL; + goto err_ch_mem; + } + size += (sizeof(u32) * ch_cfg->no_of_intents); + if (size > pkt_size) { + dev_err(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", + __func__, size, pkt_size); + ret = -EINVAL; + goto err_ch_mem; + } + + ch_cfg_size = sizeof(struct wdsp_glink_ch_cfg) + + (sizeof(u32) * ch_cfg->no_of_intents); + ch_size = sizeof(struct wdsp_glink_ch) + + (sizeof(u32) * ch_cfg->no_of_intents); + + dev_dbg(wpriv->dev, "%s: channels: %d ch_cfg_size: %d, size: %zd, pkt_size: %zd", + __func__, no_of_channels, ch_cfg_size, size, pkt_size); + + ch[i] = kzalloc(ch_size, GFP_KERNEL); + if (!ch[i]) { + ret = -ENOMEM; + goto err_ch_mem; + } + ch[i]->channel_state = GLINK_LOCAL_DISCONNECTED; + memcpy(&ch[i]->ch_cfg, payload, ch_cfg_size); + payload += ch_cfg_size; + + mutex_init(&ch[i]->mutex); + ch[i]->wpriv = wpriv; + INIT_WORK(&ch[i]->lcl_ch_open_wrk, wdsp_glink_lcl_ch_open_wrk); + INIT_WORK(&ch[i]->lcl_ch_cls_wrk, wdsp_glink_lcl_ch_cls_wrk); + init_waitqueue_head(&ch[i]->ch_connect_wait); + } + + INIT_WORK(&wpriv->ch_open_cls_wrk, wdsp_glink_ch_open_cls_wrk); + + /* Register glink link_state notification */ + link_info.glink_link_state_notif_cb = wdsp_glink_link_state_cb; + link_info.transport = NULL; + link_info.edge = WDSP_EDGE; + + wpriv->glink_state.link_state = GLINK_LINK_STATE_DOWN; + wpriv->glink_state.handle = glink_register_link_state_cb(&link_info, + wpriv); + if (!wpriv->glink_state.handle) { + dev_err(wpriv->dev, "%s: Unable to register wdsp link state\n", + __func__); + ret = -EINVAL; + goto err_ch_mem; + } + goto done; + +err_ch_mem: + for (j = 0; j < i; j++) { + mutex_destroy(&ch[j]->mutex); + kfree(wpriv->ch[j]); + wpriv->ch[j] = NULL; + } + kfree(wpriv->ch); + wpriv->ch = NULL; + wpriv->no_of_channels = 0; + +done: + mutex_unlock(&wpriv->glink_mutex); + return ret; +} + +/* + * wdsp_glink_tx_buf_work - Work queue function to send tx buffer to glink + * work: Work structure + */ +static void wdsp_glink_tx_buf_work(struct work_struct *work) +{ + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_ch *ch; + struct wdsp_glink_tx_buf *tx_buf; + struct wdsp_write_pkt *wpkt; + struct wdsp_cmd_pkt *cpkt; + int ret = 0; + + tx_buf = container_of(work, struct wdsp_glink_tx_buf, + tx_work); + ch = tx_buf->ch; + wpriv = ch->wpriv; + wpkt = (struct wdsp_write_pkt *)tx_buf->buf; + cpkt = (struct wdsp_cmd_pkt *)wpkt->payload; + dev_dbg(wpriv->dev, "%s: ch name = %s, payload size = %d\n", + __func__, cpkt->ch_name, cpkt->payload_size); + + mutex_lock(&tx_buf->ch->mutex); + if (ch->channel_state == GLINK_CONNECTED) { + mutex_unlock(&tx_buf->ch->mutex); + ret = glink_tx(ch->handle, tx_buf, + cpkt->payload, cpkt->payload_size, + GLINK_TX_REQ_INTENT); + if (ret < 0) { + dev_err(wpriv->dev, "%s: glink tx failed, ret = %d\n", + __func__, ret); + /* + * If glink_tx() is failed then free tx_buf here as + * there won't be any tx_done notification to + * free the buffer. + */ + vfree(tx_buf); + } + } else { + mutex_unlock(&tx_buf->ch->mutex); + dev_err(wpriv->dev, "%s: channel %s is not in connected state\n", + __func__, ch->ch_cfg.name); + /* + * Free tx_buf here as there won't be any tx_done + * notification in this case also. + */ + vfree(tx_buf); + } +} + +/* + * wdsp_glink_read - Read API to send the data to userspace + * file: Pointer to the file structure + * buf: Pointer to the userspace buffer + * count: Number bytes to read from the file + * ppos: Pointer to the position into the file + */ +static ssize_t wdsp_glink_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int ret = 0, ret1 = 0; + struct wdsp_glink_rsp_que *rsp; + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + ret = -EINVAL; + goto done; + } + + if (count > WDSP_MAX_READ_SIZE) { + dev_info(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n", + __func__, count); + count = WDSP_MAX_READ_SIZE; + } + /* + * Complete signal has given from glink rx notification callback + * or from flush API. Also use interruptible wait_for_completion API + * to allow the system to go in suspend. + */ + ret = wait_for_completion_interruptible(&wpriv->rsp_complete); + if (ret) + goto done; + + mutex_lock(&wpriv->rsp_mutex); + if (wpriv->rsp_cnt) { + wpriv->rsp_cnt--; + dev_dbg(wpriv->dev, "%s: read from buffer %d\n", + __func__, wpriv->rsp_cnt); + + rsp = &wpriv->rsp[wpriv->rsp_cnt]; + if (count < rsp->buf_size) { + ret1 = copy_to_user(buf, &rsp->buf, count); + /* Return the number of bytes copied */ + ret = count; + } else { + ret1 = copy_to_user(buf, &rsp->buf, rsp->buf_size); + /* Return the number of bytes copied */ + ret = rsp->buf_size; + } + + if (ret1) { + mutex_unlock(&wpriv->rsp_mutex); + dev_err(wpriv->dev, "%s: copy_to_user failed %d\n", + __func__, ret); + ret = -EFAULT; + goto done; + } + } else { + /* + * This will execute only if flush API is called or + * something wrong with ref_cnt + */ + dev_dbg(wpriv->dev, "%s: resp count = %d\n", __func__, + wpriv->rsp_cnt); + ret = -EINVAL; + } + mutex_unlock(&wpriv->rsp_mutex); + +done: + return ret; +} + +/* + * wdsp_glink_write - Write API to receive the data from userspace + * file: Pointer to the file structure + * buf: Pointer to the userspace buffer + * count: Number bytes to read from the file + * ppos: Pointer to the position into the file + */ +static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int ret = 0, i, tx_buf_size; + struct wdsp_write_pkt *wpkt; + struct wdsp_cmd_pkt *cpkt; + struct wdsp_glink_tx_buf *tx_buf; + struct wdsp_glink_priv *wpriv; + size_t pkt_max_size; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + ret = -EINVAL; + goto done; + } + + if ((count < WDSP_WRITE_PKT_SIZE) || + (count > WDSP_MAX_WRITE_SIZE)) { + dev_err(wpriv->dev, "%s: Invalid count = %zd\n", + __func__, count); + ret = -EINVAL; + goto done; + } + + dev_dbg(wpriv->dev, "%s: count = %zd\n", __func__, count); + + tx_buf_size = count + sizeof(struct wdsp_glink_tx_buf); + tx_buf = vzalloc(tx_buf_size); + if (!tx_buf) { + ret = -ENOMEM; + goto done; + } + + ret = copy_from_user(tx_buf->buf, buf, count); + if (ret) { + dev_err(wpriv->dev, "%s: copy_from_user failed %d\n", + __func__, ret); + ret = -EFAULT; + goto free_buf; + } + + wpkt = (struct wdsp_write_pkt *)tx_buf->buf; + switch (wpkt->pkt_type) { + case WDSP_REG_PKT: + if (count < (WDSP_WRITE_PKT_SIZE + WDSP_REG_PKT_SIZE + + WDSP_CH_CFG_SIZE)) { + dev_err(wpriv->dev, "%s: Invalid reg pkt size = %zd\n", + __func__, count); + ret = -EINVAL; + goto free_buf; + } + ret = wdsp_glink_ch_info_init(wpriv, + (struct wdsp_reg_pkt *)wpkt->payload, + count); + if (ret < 0) + dev_err(wpriv->dev, "%s: glink register failed, ret = %d\n", + __func__, ret); + vfree(tx_buf); + break; + case WDSP_READY_PKT: + ret = wait_event_timeout(wpriv->link_state_wait, + (wpriv->glink_state.link_state == + GLINK_LINK_STATE_UP), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + dev_err(wpriv->dev, "%s: Link state wait timeout\n", + __func__); + ret = -ETIMEDOUT; + goto free_buf; + } + ret = 0; + vfree(tx_buf); + break; + case WDSP_CMD_PKT: + if (count <= (WDSP_WRITE_PKT_SIZE + WDSP_CMD_PKT_SIZE)) { + dev_err(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n", + __func__, count); + ret = -EINVAL; + goto free_buf; + } + mutex_lock(&wpriv->glink_mutex); + if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) { + mutex_unlock(&wpriv->glink_mutex); + dev_err(wpriv->dev, "%s: Link state is Down\n", + __func__); + + ret = -ENETRESET; + goto free_buf; + } + mutex_unlock(&wpriv->glink_mutex); + cpkt = (struct wdsp_cmd_pkt *)wpkt->payload; + pkt_max_size = sizeof(struct wdsp_write_pkt) + + sizeof(struct wdsp_cmd_pkt) + + cpkt->payload_size; + if (count < pkt_max_size) { + dev_err(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n", + __func__, count, pkt_max_size); + ret = -EINVAL; + goto free_buf; + } + dev_dbg(wpriv->dev, "%s: requested ch_name: %s, pkt_size: %zd\n", + __func__, cpkt->ch_name, pkt_max_size); + for (i = 0; i < wpriv->no_of_channels; i++) { + if (wpriv->ch && wpriv->ch[i] && + (!strcmp(cpkt->ch_name, + wpriv->ch[i]->ch_cfg.name))) { + tx_buf->ch = wpriv->ch[i]; + break; + } + } + if (!tx_buf->ch) { + dev_err(wpriv->dev, "%s: Failed to get glink channel\n", + __func__); + ret = -EINVAL; + goto free_buf; + } + + ret = wait_event_timeout(tx_buf->ch->ch_connect_wait, + (tx_buf->ch->channel_state == + GLINK_CONNECTED), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + dev_err(wpriv->dev, "%s: glink channel %s is not in connected state %d\n", + __func__, tx_buf->ch->ch_cfg.name, + tx_buf->ch->channel_state); + ret = -ETIMEDOUT; + goto free_buf; + } + ret = 0; + + INIT_WORK(&tx_buf->tx_work, wdsp_glink_tx_buf_work); + queue_work(wpriv->work_queue, &tx_buf->tx_work); + break; + default: + dev_err(wpriv->dev, "%s: Invalid packet type\n", __func__); + ret = -EINVAL; + vfree(tx_buf); + break; + } + goto done; + +free_buf: + vfree(tx_buf); + +done: + return ret; +} + +/* + * wdsp_glink_open - Open API to initialize private data + * inode: Pointer to the inode structure + * file: Pointer to the file structure + */ +static int wdsp_glink_open(struct inode *inode, struct file *file) +{ + int ret = 0; + struct wdsp_glink_priv *wpriv; + struct wdsp_glink_dev *wdev; + + if (!inode->i_cdev) { + pr_err("%s: cdev is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + wdev = container_of(inode->i_cdev, struct wdsp_glink_dev, cdev); + + wpriv = kzalloc(sizeof(struct wdsp_glink_priv), GFP_KERNEL); + if (!wpriv) { + ret = -ENOMEM; + goto done; + } + wpriv->dev = wdev->dev; + wpriv->work_queue = create_singlethread_workqueue("wdsp_glink_wq"); + if (!wpriv->work_queue) { + dev_err(wpriv->dev, "%s: Error creating wdsp_glink_wq\n", + __func__); + ret = -EINVAL; + goto err_wq; + } + + init_completion(&wpriv->rsp_complete); + init_waitqueue_head(&wpriv->link_state_wait); + mutex_init(&wpriv->rsp_mutex); + mutex_init(&wpriv->glink_mutex); + file->private_data = wpriv; + + goto done; + +err_wq: + kfree(wpriv); + +done: + return ret; +} + +/* + * wdsp_glink_flush - Flush API to unblock read. + * file: Pointer to the file structure + * id: Lock owner ID + */ +static int wdsp_glink_flush(struct file *file, fl_owner_t id) +{ + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + return -EINVAL; + } + + complete(&wpriv->rsp_complete); + + return 0; +} + +/* + * wdsp_glink_release - Release API to clean up resources. + * Whenever a file structure is shared across multiple threads, + * release won't be invoked until all copies are closed + * (file->f_count.counter should be 0). If we need to flush pending + * data when any copy is closed, you should implement the flush method. + * + * inode: Pointer to the inode structure + * file: Pointer to the file structure + */ +static int wdsp_glink_release(struct inode *inode, struct file *file) +{ + int i, ret = 0; + struct wdsp_glink_priv *wpriv; + + wpriv = (struct wdsp_glink_priv *)file->private_data; + if (!wpriv) { + pr_err("%s: Invalid private data\n", __func__); + ret = -EINVAL; + goto done; + } + + if (wpriv->glink_state.handle) + glink_unregister_link_state_cb(wpriv->glink_state.handle); + + flush_workqueue(wpriv->work_queue); + destroy_workqueue(wpriv->work_queue); + + /* + * Clean up glink channel memory in channel state + * callback only if close channels are called from here. + */ + if (wpriv->ch) { + for (i = 0; i < wpriv->no_of_channels; i++) { + if (wpriv->ch[i]) { + wpriv->ch[i]->free_mem = true; + /* + * Channel handle NULL means channel is already + * closed. Free the channel memory here itself. + */ + if (!wpriv->ch[i]->handle) { + kfree(wpriv->ch[i]); + wpriv->ch[i] = NULL; + } else { + wdsp_glink_close_ch(wpriv->ch[i]); + } + } + } + + kfree(wpriv->ch); + wpriv->ch = NULL; + } + + mutex_destroy(&wpriv->glink_mutex); + mutex_destroy(&wpriv->rsp_mutex); + kfree(wpriv); + file->private_data = NULL; + +done: + return ret; +} + +static const struct file_operations wdsp_glink_fops = { + .owner = THIS_MODULE, + .open = wdsp_glink_open, + .read = wdsp_glink_read, + .write = wdsp_glink_write, + .flush = wdsp_glink_flush, + .release = wdsp_glink_release, +}; + +/* + * wdsp_glink_probe - Driver probe to expose char device + * pdev: Pointer to device tree data. + */ +static int wdsp_glink_probe(struct platform_device *pdev) +{ + int ret; + struct wdsp_glink_dev *wdev; + + wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL); + if (!wdev) { + ret = -ENOMEM; + goto done; + } + + ret = alloc_chrdev_region(&wdev->dev_num, 0, MINOR_NUMBER_COUNT, + WDSP_GLINK_DRIVER_NAME); + if (ret < 0) { + dev_err(&pdev->dev, "%s: Failed to alloc char dev, err = %d\n", + __func__, ret); + goto err_chrdev; + } + + wdev->cls = class_create(THIS_MODULE, WDSP_GLINK_DRIVER_NAME); + if (IS_ERR(wdev->cls)) { + ret = PTR_ERR(wdev->cls); + dev_err(&pdev->dev, "%s: Failed to create class, err = %d\n", + __func__, ret); + goto err_class; + } + + wdev->dev = device_create(wdev->cls, NULL, wdev->dev_num, + NULL, WDSP_GLINK_DRIVER_NAME); + if (IS_ERR(wdev->dev)) { + ret = PTR_ERR(wdev->dev); + dev_err(&pdev->dev, "%s: Failed to create device, err = %d\n", + __func__, ret); + goto err_dev_create; + } + + cdev_init(&wdev->cdev, &wdsp_glink_fops); + ret = cdev_add(&wdev->cdev, wdev->dev_num, MINOR_NUMBER_COUNT); + if (ret < 0) { + dev_err(&pdev->dev, "%s: Failed to register char dev, err = %d\n", + __func__, ret); + goto err_cdev_add; + } + platform_set_drvdata(pdev, wdev); + goto done; + +err_cdev_add: + device_destroy(wdev->cls, wdev->dev_num); + +err_dev_create: + class_destroy(wdev->cls); + +err_class: + unregister_chrdev_region(0, MINOR_NUMBER_COUNT); + +err_chrdev: + devm_kfree(&pdev->dev, wdev); + +done: + return ret; +} + +/* + * wdsp_glink_remove - Driver remove to handle cleanup + * pdev: Pointer to device tree data. + */ +static int wdsp_glink_remove(struct platform_device *pdev) +{ + struct wdsp_glink_dev *wdev = platform_get_drvdata(pdev); + + if (wdev) { + cdev_del(&wdev->cdev); + device_destroy(wdev->cls, wdev->dev_num); + class_destroy(wdev->cls); + unregister_chrdev_region(0, MINOR_NUMBER_COUNT); + devm_kfree(&pdev->dev, wdev); + } else { + dev_err(&pdev->dev, "%s: Invalid device data\n", __func__); + } + + return 0; +} + +static const struct of_device_id wdsp_glink_of_match[] = { + {.compatible = "qcom,wcd-dsp-glink"}, + { } +}; +MODULE_DEVICE_TABLE(of, wdsp_glink_of_match); + +static struct platform_driver wdsp_glink_driver = { + .probe = wdsp_glink_probe, + .remove = wdsp_glink_remove, + .driver = { + .name = WDSP_GLINK_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = wdsp_glink_of_match, + }, +}; + +module_platform_driver(wdsp_glink_driver); + +MODULE_DESCRIPTION("SoC WCD_DSP GLINK Driver"); +MODULE_LICENSE("GPL v2"); From 71ca58e195db0752381aa16ea07352de7a37fd08 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Thu, 3 Aug 2017 18:58:09 +0530 Subject: [PATCH 008/276] autoconf: Enable config for wdsp-glink/avtimer Enable the configs for WCD DSP Glink and avtimer drivers. Remove the CDSP Loader config as it is not audio related. Change-Id: Ic6101e106c1e1d91bece6f53d1572507570b6b6f Signed-off-by: Laxminath Kasam --- config/sdm845auto.conf | 3 ++- config/sdm845autoconf.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/config/sdm845auto.conf b/config/sdm845auto.conf index f9a7748a3fde..b6d4231ff275 100644 --- a/config/sdm845auto.conf +++ b/config/sdm845auto.conf @@ -20,7 +20,6 @@ CONFIG_REGMAP_SWR=y CONFIG_MSM_QDSP6_SSR=y CONFIG_MSM_QDSP6_PDR=y CONFIG_MSM_QDSP6_NOTIFIER=y -CONFIG_MSM_CDSP_LOADER=y CONFIG_SND_SOC_MSM_HOSTLESS_PCM=y CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y CONFIG_SND_SOC_SDM845=y @@ -39,3 +38,5 @@ CONFIG_DOLBY_LICENSE=y CONFIG_DTS_SRS_TM=y CONFIG_SND_SOC_MACHINE_SDM845=y CONFIG_SND_SOC_MSM_STUB=y +CONFIG_WCD_DSP_GLINK=y +CONFIG_MSM_AVTIMER=y diff --git a/config/sdm845autoconf.h b/config/sdm845autoconf.h index 6c3bb330656d..7e9de815db80 100644 --- a/config/sdm845autoconf.h +++ b/config/sdm845autoconf.h @@ -34,7 +34,6 @@ #define CONFIG_MSM_QDSP6_SSR 1 #define CONFIG_MSM_QDSP6_PDR 1 #define CONFIG_MSM_QDSP6_NOTIFIER 1 -#define CONFIG_MSM_CDSP_LOADER 1 #define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 #define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 #define CONFIG_SND_SOC_SDM845 1 @@ -53,3 +52,5 @@ #define CONFIG_DTS_SRS_TM 1 #define CONFIG_WCD9XXX_CODEC_CORE 1 #define CONFIG_SND_SOC_MSM_STUB 1 +#define CONFIG_WCD_DSP_GLINK 1 +#define CONFIG_MSM_AVTIMER 1 From 34504581a0a6ed64b2f342867432f9628b48f0a9 Mon Sep 17 00:00:00 2001 From: Asish Bhattacharya Date: Tue, 8 Aug 2017 12:55:01 +0530 Subject: [PATCH 009/276] audio-lnx: Add snapshot of audio drivers from below commit. (1996ec83a45: "ARM: dts: msm: Reduce CNOC bus voting for USB slave on SDM845") This change is to migrate latest snapshot of kernel drivers. Change-Id: I84669db544773d3ce9d2d5dff0ab6943bd7d8ff9 Signed-off-by: Asish Bhattacharya --- asoc/codecs/sdm660_cdc/msm-digital-cdc.c | 5 +- asoc/codecs/wcd9335.c | 50 ++--- asoc/codecs/wcd934x/wcd934x.c | 40 ++-- asoc/msm-dai-fe.c | 15 +- asoc/msm-pcm-routing-v2.c | 228 ++++++++++++++++++----- asoc/msm8996.c | 7 +- asoc/msm8998.c | 7 +- asoc/sdm660-ext-dai-links.c | 6 +- asoc/sdm660-internal.c | 6 +- asoc/sdm845.c | 96 +++++++++- dsp/q6adm.c | 16 +- 11 files changed, 345 insertions(+), 131 deletions(-) diff --git a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c index f59b653323c3..3df917f717b5 100644 --- a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -9,7 +9,6 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ - #include #include #include @@ -122,9 +121,7 @@ static void disable_digital_callback(void *flag) static int msm_dig_cdc_put_dec_enum(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 *w = wlist->widgets[0]; + struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int dec_mux, decimator; diff --git a/asoc/codecs/wcd9335.c b/asoc/codecs/wcd9335.c index 26a5a9de362b..2f877160bf66 100644 --- a/asoc/codecs/wcd9335.c +++ b/asoc/codecs/wcd9335.c @@ -2261,9 +2261,8 @@ static const struct snd_kcontrol_new hph_type_detect_controls[] = { static int tasha_vi_feed_mixer_get(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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); @@ -2275,9 +2274,8 @@ static int tasha_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, static int tasha_vi_feed_mixer_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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); struct wcd9xxx *core = tasha_p->wcd9xxx; @@ -2328,9 +2326,8 @@ static int tasha_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, static int slim_tx_mixer_get(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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); @@ -2341,9 +2338,8 @@ static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol, static int slim_tx_mixer_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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); @@ -2436,9 +2432,8 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, static int slim_rx_mux_get(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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); @@ -2454,9 +2449,8 @@ static const char *const slim_rx_mux_text[] = { static int slim_rx_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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); @@ -8505,9 +8499,8 @@ static const struct snd_kcontrol_new tasha_snd_controls[] = { static int tasha_put_dec_enum(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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val; @@ -8565,9 +8558,8 @@ static int tasha_put_dec_enum(struct snd_kcontrol *kcontrol, static int tasha_int_dem_inp_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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val; @@ -8899,9 +8891,8 @@ static int tasha_codec_configure_cpe_input(struct snd_soc_dapm_widget *w, static int tasha_codec_aif4_mixer_switch_get(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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); @@ -8918,9 +8909,8 @@ static int tasha_codec_aif4_mixer_switch_get(struct snd_kcontrol *kcontrol, static int tasha_codec_aif4_mixer_switch_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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_update *update = NULL; struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec); diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 1997e044fad7..ce0141f9f43b 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -1110,9 +1110,8 @@ static int tavil_put_clkmode(struct snd_kcontrol *kcontrol, static int tavil_vi_feed_mixer_get(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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); @@ -1124,9 +1123,8 @@ static int tavil_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, static int tavil_vi_feed_mixer_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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); @@ -1176,9 +1174,8 @@ static int tavil_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, static int slim_tx_mixer_get(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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); @@ -1189,9 +1186,8 @@ static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol, static int slim_tx_mixer_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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); @@ -1271,9 +1267,8 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, static int slim_rx_mux_get(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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); @@ -1285,9 +1280,8 @@ static int slim_rx_mux_get(struct snd_kcontrol *kcontrol, static int slim_rx_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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent); @@ -5929,9 +5923,8 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = { static int tavil_dec_enum_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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val; @@ -5989,9 +5982,8 @@ static int tavil_dec_enum_put(struct snd_kcontrol *kcontrol, static int tavil_int_dem_inp_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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; unsigned int val; diff --git a/asoc/msm-dai-fe.c b/asoc/msm-dai-fe.c index c319ccf43feb..30a4d59f2ba4 100644 --- a/asoc/msm-dai-fe.c +++ b/asoc/msm-dai-fe.c @@ -2496,8 +2496,21 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .rate_min = 8000, .rate_max = 384000, }, + .capture = { + .stream_name = "MultiMedia16 Capture", + .aif_name = "MM_UL16", + .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, - .compress_new = snd_soc_new_compress, .name = "MultiMedia16", .probe = fe_dai_probe, }, diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index de2201ef2f80..aa9b3abf6a48 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -1768,9 +1768,8 @@ static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol, 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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_dapm_update *update = NULL; @@ -1810,9 +1809,8 @@ static int msm_routing_get_listen_mixer(struct snd_kcontrol *kcontrol, static int msm_routing_put_listen_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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_dapm_update *update = NULL; @@ -1928,9 +1926,8 @@ static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol, 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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_dapm_update *update = NULL; @@ -1972,9 +1969,8 @@ static int msm_routing_get_voice_stub_mixer(struct snd_kcontrol *kcontrol, 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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; struct snd_soc_dapm_update *update = NULL; @@ -2075,9 +2071,8 @@ static int msm_routing_get_switch_mixer(struct snd_kcontrol *kcontrol, 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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_update *update = NULL; pr_debug("%s: FM Switch enable %ld\n", __func__, @@ -2104,9 +2099,8 @@ static int msm_routing_get_hfp_switch_mixer(struct snd_kcontrol *kcontrol, 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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_update *update = NULL; pr_debug("%s: HFP Switch enable %ld\n", __func__, @@ -2133,9 +2127,8 @@ static int msm_routing_get_int0_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, static int msm_routing_put_int0_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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_update *update = NULL; pr_debug("%s: INT0 MI2S Switch enable %ld\n", __func__, @@ -2162,9 +2155,8 @@ static int msm_routing_get_int4_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, static int msm_routing_put_int4_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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_update *update = NULL; pr_debug("%s: INT4 MI2S Switch enable %ld\n", __func__, @@ -2191,9 +2183,8 @@ static int msm_routing_get_usb_switch_mixer(struct snd_kcontrol *kcontrol, static int msm_routing_put_usb_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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_update *update = NULL; pr_debug("%s: USB Switch enable %ld\n", __func__, @@ -2220,9 +2211,8 @@ static int msm_routing_get_pri_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, 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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_update *update = NULL; pr_debug("%s: PRI MI2S Switch enable %ld\n", __func__, @@ -2249,9 +2239,8 @@ static int msm_routing_get_sec_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, 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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_update *update = NULL; pr_debug("%s: SEC MI2S Switch enable %ld\n", __func__, @@ -2280,9 +2269,8 @@ 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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_update *update = NULL; pr_debug("%s: TERT MI2S Switch enable %ld\n", __func__, @@ -2311,9 +2299,8 @@ 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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_update *update = NULL; pr_debug("%s: QUAT MI2S Switch enable %ld\n", __func__, @@ -2340,9 +2327,8 @@ static int msm_routing_get_fm_pcmrx_switch_mixer(struct snd_kcontrol *kcontrol, 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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_dapm_update *update = NULL; pr_debug("%s: FM Switch enable %ld\n", __func__, @@ -3490,9 +3476,8 @@ 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]; + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; struct snd_soc_dapm_update *update = NULL; @@ -3655,6 +3640,11 @@ static const struct snd_kcontrol_new ext_ec_ref_mux_ul9 = 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_ul16 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL16 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], @@ -3684,9 +3674,8 @@ static int msm_routing_ext_ec_get(struct snd_kcontrol *kcontrol, 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]; + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); int mux = ucontrol->value.enumerated.item[0]; struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; int ret = 1; @@ -7103,6 +7092,114 @@ static const struct snd_kcontrol_new mmul8_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new mmul16_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("PRI_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("SEC_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 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_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + static const struct snd_kcontrol_new mmul9_mixer_controls[] = { SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, @@ -11392,6 +11489,7 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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_UL16", "MultiMedia16 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), @@ -12127,6 +12225,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia9 Mixer", SND_SOC_NOPM, 0, 0, mmul9_mixer_controls, ARRAY_SIZE(mmul9_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia16 Mixer", SND_SOC_NOPM, 0, 0, + mmul16_mixer_controls, ARRAY_SIZE(mmul16_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, @@ -12457,6 +12557,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { &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_UL16 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul16), 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, @@ -12708,6 +12810,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia8 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia3 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia16 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"}, @@ -13276,6 +13379,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"}, {"MultiMedia3 Mixer", "MI2S_TX", "MI2S_TX"}, {"MultiMedia5 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia16 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"}, @@ -13294,12 +13398,15 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia16 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"}, + {"MultiMedia16 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"}, + {"MultiMedia16 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, {"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia2 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"}, {"MultiMedia2 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, @@ -13314,9 +13421,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia6 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, {"MultiMedia3 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, {"MultiMedia5 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, + {"MultiMedia16 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"}, + {"MultiMedia16 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"}, @@ -13478,6 +13587,24 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia6 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, {"MultiMedia8 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia16 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, + {"MultiMedia16 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, + {"MultiMedia16 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, + {"MultiMedia16 Mixer", "PRI_TDM_TX_3", "PRI_TDM_TX_3"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_0", "SEC_TDM_TX_0"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_1", "SEC_TDM_TX_1"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_2", "SEC_TDM_TX_2"}, + {"MultiMedia16 Mixer", "SEC_TDM_TX_3", "SEC_TDM_TX_3"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia16 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia16 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia16 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"}, @@ -13560,8 +13687,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia16 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"}, + {"MultiMedia16 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"}, @@ -13577,6 +13706,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia19 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia8 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia16 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MM_UL1", NULL, "MultiMedia1 Mixer"}, {"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MM_UL2", NULL, "MultiMedia2 Mixer"}, @@ -13586,6 +13716,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MM_UL6", NULL, "MultiMedia6 Mixer"}, {"MM_UL8", NULL, "MultiMedia8 Mixer"}, {"MM_UL9", NULL, "MultiMedia9 Mixer"}, + {"MM_UL16", NULL, "MultiMedia16 Mixer"}, {"MM_UL17", NULL, "MultiMedia17 Mixer"}, {"MM_UL18", NULL, "MultiMedia18 Mixer"}, {"MM_UL19", NULL, "MultiMedia19 Mixer"}, @@ -13996,6 +14127,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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_UL16", NULL, "AUDIO_REF_EC_UL16 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"}, diff --git a/asoc/msm8996.c b/asoc/msm8996.c index 45c5479884a2..08900372a2c2 100644 --- a/asoc/msm8996.c +++ b/asoc/msm8996.c @@ -2915,12 +2915,13 @@ static struct snd_soc_dai_link msm8996_common_dai_links[] = { .id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, { - .name = "MSM8996 Compress9", - .stream_name = "Compress9", + .name = "MSM8996 ULL NOIRQ_2", + .stream_name = "MM_NOIRQ_2", .cpu_dai_name = "MultiMedia16", - .platform_name = "msm-compress-dsp", + .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", diff --git a/asoc/msm8998.c b/asoc/msm8998.c index 6c5393d021d7..b2fc005ebbb4 100644 --- a/asoc/msm8998.c +++ b/asoc/msm8998.c @@ -5312,12 +5312,13 @@ static struct snd_soc_dai_link msm_common_dai_links[] = { .id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, { - .name = MSM_DAILINK_NAME(Compress9), - .stream_name = "Compress9", + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", .cpu_dai_name = "MultiMedia16", - .platform_name = "msm-compress-dsp", + .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", diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c index cde28706a75e..1e89976b4ab3 100644 --- a/asoc/sdm660-ext-dai-links.c +++ b/asoc/sdm660-ext-dai-links.c @@ -1270,10 +1270,10 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, {/* hw:x,33 */ - .name = MSM_DAILINK_NAME(Compress9), - .stream_name = "Compress9", + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", .cpu_dai_name = "MultiMedia16", - .platform_name = "msm-compress-dsp", + .platform_name = "msm-pcm-dsp-noirq", .dynamic = 1, .dpcm_capture = 1, .dpcm_playback = 1, diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c index 0afe82896cfb..fc2378d4e8d2 100644 --- a/asoc/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -2194,10 +2194,10 @@ static struct snd_soc_dai_link msm_int_dai[] = { .id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, {/* hw:x,33 */ - .name = MSM_DAILINK_NAME(Compress9), - .stream_name = "Compress9", + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", .cpu_dai_name = "MultiMedia16", - .platform_name = "msm-compress-dsp", + .platform_name = "msm-pcm-dsp-noirq", .dynamic = 1, .dpcm_capture = 1, .dpcm_playback = 1, diff --git a/asoc/sdm845.c b/asoc/sdm845.c index 9d6ff120e671..e38def3c9e95 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -134,6 +134,13 @@ struct mi2s_conf { u32 msm_is_mi2s_master; }; +static u32 mi2s_ebit_clk[MI2S_MAX] = { + Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT +}; + struct auxpcm_conf { struct mutex lock; u32 ref_cnt; @@ -434,6 +441,7 @@ static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; static const char *const hifi_text[] = {"Off", "On"}; +static const char *const qos_text[] = {"Disable", "Enable"}; 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); @@ -498,9 +506,11 @@ static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_rx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_tx_format, bit_format_text); static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text); +static SOC_ENUM_SINGLE_EXT_DECL(qos_vote, qos_text); static struct platform_device *spdev; static int msm_hifi_control; +static int qos_vote_status; static bool is_initial_boot; static bool codec_reg_done; @@ -2628,6 +2638,72 @@ static int msm_hifi_put(struct snd_kcontrol *kcontrol, return 0; } +static s32 msm_qos_value(struct snd_pcm_runtime *runtime) +{ + s32 usecs; + + if (!runtime->rate) + return -EINVAL; + + /* take 75% of period time as the deadline */ + usecs = (750000 / runtime->rate) * runtime->period_size; + usecs += ((750000 % runtime->rate) * runtime->period_size) / + runtime->rate; + + return usecs; +} + +static int msm_qos_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = qos_vote_status; + + return 0; +} + +static int msm_qos_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + const char *be_name = MSM_DAILINK_NAME(LowLatency); + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm_substream *substream; + s32 usecs; + + rtd = snd_soc_get_pcm_runtime(card, be_name); + if (!rtd) { + pr_err("%s: fail to get pcm runtime for %s\n", + __func__, be_name); + return -EINVAL; + } + + substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s: substream is null\n", __func__); + return -EINVAL; + } + + qos_vote_status = ucontrol->value.enumerated.item[0]; + if (qos_vote_status) { + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + if (!substream->runtime) { + pr_err("%s: runtime is null\n", __func__); + return -EINVAL; + } + usecs = msm_qos_value(substream->runtime); + if (usecs >= 0) + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, usecs); + } else { + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + } + + 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), @@ -2857,6 +2933,8 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { msm_aux_pcm_tx_format_get, msm_aux_pcm_tx_format_put), SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get, msm_hifi_put), + SOC_ENUM_EXT("MultiMedia5_RX QOS Vote", qos_vote, msm_qos_ctl_get, + msm_qos_ctl_put), }; static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, @@ -4176,9 +4254,6 @@ static void update_mi2s_clk_val(int dai_id, int stream) 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) @@ -4529,6 +4604,11 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) */ mutex_lock(&mi2s_intf_conf[index].lock); if (++mi2s_intf_conf[index].ref_cnt == 1) { + /* Check if msm needs to provide the clock to the interface */ + if (!mi2s_intf_conf[index].msm_is_mi2s_master) { + mi2s_clk[index].clk_id = mi2s_ebit_clk[index]; + fmt = SND_SOC_DAIFMT_CBM_CFM; + } ret = msm_mi2s_set_sclk(substream, true); if (ret < 0) { dev_err(rtd->card->dev, @@ -4548,9 +4628,6 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) 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 (ret < 0) { pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", @@ -5325,12 +5402,13 @@ static struct snd_soc_dai_link msm_common_dai_links[] = { .id = MSM_FRONTEND_DAI_MULTIMEDIA15, }, { - .name = MSM_DAILINK_NAME(Compress9), - .stream_name = "Compress9", + .name = MSM_DAILINK_NAME(ULL_NOIRQ_2), + .stream_name = "MM_NOIRQ_2", .cpu_dai_name = "MultiMedia16", - .platform_name = "msm-compress-dsp", + .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", diff --git a/dsp/q6adm.c b/dsp/q6adm.c index 19151139eeff..bc217237be91 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -2388,7 +2388,8 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology, struct adm_cmd_device_open_v5 open; struct adm_cmd_device_open_v6 open_v6; int ret = 0; - int port_idx, copp_idx, flags; + int port_idx, flags; + int copp_idx = -1; 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", @@ -2442,8 +2443,17 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int 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); + /* + * Routing driver reuses the same adm for streams with the same + * app_type, sample_rate etc. + * This isn't allowed for ULL streams as per the DSP interface + */ + if (perf_mode != ULTRA_LOW_LATENCY_PCM_MODE) + 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) { From 7e057cf8cd5e84aecf8ad82750ac8b6c6f0da68a Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Wed, 9 Aug 2017 23:55:15 +0530 Subject: [PATCH 010/276] audio-lnx: Enable compilation for sdm670 audio Enable compilation for sdm670 audio. Update for both internal and external codecs. Change-Id: I030837a2c0180a5a31d0ab786ad5de607027050f Signed-off-by: Laxminath Kasam --- Makefile | 9 +++++++++ asoc/Makefile | 2 +- asoc/codecs/Makefile | 14 +++++++++++++- asoc/codecs/msm_sdw/Makefile | 1 - asoc/codecs/msm_sdw/msm_sdw.h | 2 +- asoc/codecs/msm_sdw/msm_sdw_cdc.c | 22 ++++++++++------------ asoc/codecs/wcd_cpe_core.c | 2 +- asoc/codecs/wcd_cpe_services.c | 3 +-- asoc/sdm660-ext-dai-links.c | 2 +- 9 files changed, 37 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 05b04c637301..ff83693730d3 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,10 @@ ifeq ($(CONFIG_ARCH_SDM845), y) include $(srctree)/techpack/audio/config/sdm845auto.conf export endif +ifeq ($(CONFIG_ARCH_SDM670), y) +include $(srctree)/techpack/audio/config/sdm670auto.conf +export +endif # Use USERINCLUDE when you must reference the UAPI directories only. USERINCLUDE += \ @@ -18,6 +22,11 @@ ifeq ($(CONFIG_ARCH_SDM845), y) LINUXINCLUDE += \ -include $(srctree)/techpack/audio/config/sdm845autoconf.h endif +ifeq ($(CONFIG_ARCH_SDM670), y) +LINUXINCLUDE += \ + -include $(srctree)/techpack/audio/config/sdm670autoconf.h +endif + obj-y += asoc/ obj-y += dsp/ diff --git a/asoc/Makefile b/asoc/Makefile index 67cb9f3aac6b..3fccff6726ba 100644 --- a/asoc/Makefile +++ b/asoc/Makefile @@ -21,7 +21,7 @@ obj-$(CONFIG_SND_SOC_MACHINE_MSM8998) += snd-soc-msm8998.o # for SDM660 sound card driver snd-soc-sdm660-common-objs := sdm660-common.o -obj-$(CONFIG_SND_SOC_SDM660_COMMON) += snd-soc-sdm660-common.o +obj-$(CONFIG_SND_SOC_SDM670) += snd-soc-sdm660-common.o # for SDM660 sound card driver snd-soc-int-codec-objs := sdm660-internal.o diff --git a/asoc/codecs/Makefile b/asoc/codecs/Makefile index 04218e8103e1..bb3a9356e867 100644 --- a/asoc/codecs/Makefile +++ b/asoc/codecs/Makefile @@ -1,12 +1,24 @@ snd-soc-wcd9xxx-v2-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o wcdcal-hwdep.o wcd9xxx-soc-init.o snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o snd-soc-wsa881x-objs := wsa881x.o wsa881x-tables.o wsa881x-regmap.o wsa881x-temp-sensor.o -snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o wcd-mbhc-adc.o +snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o +ifneq (,$(filter $(CONFIG_SND_SOC_WCD_MBHC_LEGACY),y m)) + snd-soc-wcd-mbhc-objs += wcd-mbhc-legacy.o +endif +ifneq (,$(filter $(CONFIG_SND_SOC_WCD_MBHC_ADC),y m)) + snd-soc-wcd-mbhc-objs += wcd-mbhc-adc.o +endif snd-soc-wcd-dsp-utils-objs := wcd-dsp-utils.o snd-soc-wcd-dsp-mgr-objs := wcd-dsp-mgr.o snd-soc-wcd-spi-objs := wcd-spi.o +snd-soc-wcd9335-objs := wcd9335.o +snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o +obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o +obj-$(CONFIG_SND_SOC_WCD_CPE) += snd-soc-wcd-cpe.o obj-$(CONFIG_SND_SOC_WCD934X) += wcd934x/ +obj-$(CONFIG_SND_SOC_SDM660_CDC) += sdm660_cdc/ +obj-$(CONFIG_SND_SOC_MSM_SDW) += msm_sdw/ ifeq ($(CONFIG_COMMON_CLK_MSM), y) obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clk.o endif diff --git a/asoc/codecs/msm_sdw/Makefile b/asoc/codecs/msm_sdw/Makefile index 64e932b9d262..9518f4db5b18 100644 --- a/asoc/codecs/msm_sdw/Makefile +++ b/asoc/codecs/msm_sdw/Makefile @@ -1,3 +1,2 @@ snd-soc-msm-sdw-objs := msm_sdw_cdc.o msm_sdw_regmap.o msm-sdw-tables.o msm_sdw_cdc_utils.o obj-$(CONFIG_SND_SOC_MSM_SDW) += snd-soc-msm-sdw.o -ccflags-y += -I$(srctree)/sound/soc/msm diff --git a/asoc/codecs/msm_sdw/msm_sdw.h b/asoc/codecs/msm_sdw/msm_sdw.h index 376ebc6c38db..3285f5f5512d 100644 --- a/asoc/codecs/msm_sdw/msm_sdw.h +++ b/asoc/codecs/msm_sdw/msm_sdw.h @@ -13,7 +13,7 @@ #define MSM_SDW_H #include -#include +#include #include "msm_sdw_registers.h" #define MSM_SDW_MAX_REGISTER 0x400 diff --git a/asoc/codecs/msm_sdw/msm_sdw_cdc.c b/asoc/codecs/msm_sdw/msm_sdw_cdc.c index cfe42e00e358..3ff184c7fab3 100644 --- a/asoc/codecs/msm_sdw/msm_sdw_cdc.c +++ b/asoc/codecs/msm_sdw/msm_sdw_cdc.c @@ -14,25 +14,25 @@ #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_sdw.h" #include "msm_sdw_registers.h" +#include "../msm-cdc-pinctrl.h" #define MSM_SDW_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000) @@ -937,9 +937,8 @@ static int msm_sdw_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, static int msm_sdw_vi_feed_mixer_get(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_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec); @@ -951,9 +950,8 @@ static int msm_sdw_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, static int msm_sdw_vi_feed_mixer_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 snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec); struct soc_multi_mixer_control *mixer = diff --git a/asoc/codecs/wcd_cpe_core.c b/asoc/codecs/wcd_cpe_core.c index 7748ace12686..a7fcce316e6d 100644 --- a/asoc/codecs/wcd_cpe_core.c +++ b/asoc/codecs/wcd_cpe_core.c @@ -23,12 +23,12 @@ #include #include #include -#include #include #include #include "core.h" #include "cpe_core.h" #include "cpe_err.h" +#include "cpe_cmi.h" #include "wcd_cpe_core.h" #include "wcd_cpe_services.h" #include "wcd_cmi_api.h" diff --git a/asoc/codecs/wcd_cpe_services.c b/asoc/codecs/wcd_cpe_services.c index 7a0e70311407..522ce7abacd6 100644 --- a/asoc/codecs/wcd_cpe_services.c +++ b/asoc/codecs/wcd_cpe_services.c @@ -18,8 +18,7 @@ #include #include #include -#include -#include +#include "wcd9335_registers.h" #include "core.h" #include "cpe_cmi.h" #include "wcd_cpe_services.h" diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c index 1e89976b4ab3..71ba1fa9060f 100644 --- a/asoc/sdm660-ext-dai-links.c +++ b/asoc/sdm660-ext-dai-links.c @@ -16,7 +16,7 @@ #include #include #include -#include "qdsp6v2/msm-pcm-routing-v2.h" +#include "msm-pcm-routing-v2.h" #include "sdm660-common.h" #include "sdm660-external.h" #include "codecs/core.h" From 8c87858751bf61c0e2dfc413ad8923aaad4a1665 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Thu, 10 Aug 2017 00:22:42 +0530 Subject: [PATCH 011/276] autoconf: Add conf file support for sdm670 audio Add new conf files to support sdm670 audio. Also update sdm845 to support required configs. Change-Id: I3b0ac16d80bba10a3226dbe5c1b39c64b5780ac9 Signed-off-by: Laxminath Kasam --- config/sdm670auto.conf | 46 ++++++++++++++++++++++++++++++++ config/sdm670autoconf.h | 59 +++++++++++++++++++++++++++++++++++++++++ config/sdm845auto.conf | 3 +-- config/sdm845autoconf.h | 2 -- 4 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 config/sdm670auto.conf create mode 100644 config/sdm670autoconf.h diff --git a/config/sdm670auto.conf b/config/sdm670auto.conf new file mode 100644 index 000000000000..59e27dc313f5 --- /dev/null +++ b/config/sdm670auto.conf @@ -0,0 +1,46 @@ +CONFIG_PINCTRL_LPI=y +CONFIG_PINCTRL_WCD=y +CONFIG_AUDIO_EXT_CLK=y +CONFIG_SND_SOC_WCD9XXX_V2=y +CONFIG_SND_SOC_WCD_MBHC=y +CONFIG_SND_SOC_WSA881X=y +CONFIG_SND_SOC_WCD_DSP_MGR=y +CONFIG_SND_SOC_WCD_SPI=y +CONFIG_SND_SOC_WCD_CPE=y +CONFIG_SND_SOC_WCD9335=y +CONFIG_SND_SOC_WCD934X=y +CONFIG_SND_SOC_WCD934X_MBHC=y +CONFIG_SND_SOC_WCD934X_DSD=y +CONFIG_MSM_QDSP6V2_CODECS=y +CONFIG_MSM_ULTRASOUND=y +CONFIG_MSM_QDSP6_APRV2_GLINK=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_REGMAP_SWR=y +CONFIG_MSM_QDSP6_SSR=y +CONFIG_MSM_QDSP6_PDR=y +CONFIG_MSM_QDSP6_NOTIFIER=y +CONFIG_SND_SOC_MSM_HOSTLESS_PCM=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_SND_SOC_SDM670=y +CONFIG_MSM_GLINK_SPI_XPRT=y +CONFIG_SOUNDWIRE=y +CONFIG_SOUNDWIRE_WCD_CTRL=y +CONFIG_SND_SOC_QDSP6V2=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_WCD9XXX_CODEC_CORE=y +CONFIG_MSM_CDC_PINCTRL=y +CONFIG_SND_SOC_WCD_MBHC_ADC=y +CONFIG_SND_SOC_WCD_MBHC_LEGACY=y +CONFIG_QTI_PP=y +CONFIG_SND_HWDEP=y +CONFIG_DTS_EAGLE=y +CONFIG_DOLBY_DS2=y +CONFIG_DOLBY_LICENSE=y +CONFIG_DTS_SRS_TM=y +CONFIG_SND_SOC_EXT_CODEC=y +CONFIG_SND_SOC_INT_CODEC=y +CONFIG_SND_SOC_MSM_STUB=y +CONFIG_WCD_DSP_GLINK=y +CONFIG_MSM_AVTIMER=y +CONFIG_SND_SOC_SDM660_CDC=y +CONFIG_SND_SOC_MSM_SDW=y diff --git a/config/sdm670autoconf.h b/config/sdm670autoconf.h new file mode 100644 index 000000000000..7b3a3515da36 --- /dev/null +++ b/config/sdm670autoconf.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 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. + */ + +#define CONFIG_PINCTRL_LPI 1 +#define CONFIG_PINCTRL_WCD 1 +#define CONFIG_AUDIO_EXT_CLK 1 +#define CONFIG_SND_SOC_WCD9XXX_V2 1 +#define CONFIG_SND_SOC_WCD_CPE 1 +#define CONFIG_SND_SOC_WCD_MBHC 1 +#define CONFIG_SND_SOC_WSA881X 1 +#define CONFIG_SND_SOC_WCD_DSP_MGR 1 +#define CONFIG_SND_SOC_WCD_SPI 1 +#define CONFIG_SND_SOC_WCD9335 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_SND_SOC_WCD934X_MBHC 1 +#define CONFIG_SND_SOC_WCD934X_DSD 1 +#define CONFIG_MSM_QDSP6V2_CODECS 1 +#define CONFIG_MSM_ULTRASOUND 1 +#define CONFIG_MSM_QDSP6_APRV2_GLINK 1 +#define CONFIG_MSM_ADSP_LOADER 1 +#define CONFIG_REGMAP_SWR 1 +#define CONFIG_MSM_QDSP6_SSR 1 +#define CONFIG_MSM_QDSP6_PDR 1 +#define CONFIG_MSM_QDSP6_NOTIFIER 1 +#define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_SND_SOC_SDM670 1 +#define CONFIG_MSM_GLINK_SPI_XPRT 1 +#define CONFIG_SOUNDWIRE 1 +#define CONFIG_SOUNDWIRE_WCD_CTRL 1 +#define CONFIG_SND_SOC_WCD_MBHC_ADC 1 +#define CONFIG_SND_SOC_WCD_MBHC_LEGACY 1 +#define CONFIG_SND_SOC_QDSP6V2 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_QTI_PP 1 +#define CONFIG_SND_HWDEP 1 +#define CONFIG_DTS_EAGLE 1 +#define CONFIG_DOLBY_DS2 1 +#define CONFIG_DOLBY_LICENSE 1 +#define CONFIG_DTS_SRS_TM 1 +#define CONFIG_WCD9XXX_CODEC_CORE 1 +#define CONFIG_MSM_CDC_PINCTRL 1 +#define CONFIG_SND_SOC_MSM_STUB 1 +#define CONFIG_WCD_DSP_GLINK 1 +#define CONFIG_MSM_AVTIMER 1 +#define CONFIG_SND_SOC_EXT_CODEC 1 +#define CONFIG_SND_SOC_INT_CODEC 1 +#define CONFIG_SND_SOC_SDM660_CDC 1 +#define CONFIG_SND_SOC_MSM_SDW 1 diff --git a/config/sdm845auto.conf b/config/sdm845auto.conf index b6d4231ff275..97f2127ce606 100644 --- a/config/sdm845auto.conf +++ b/config/sdm845auto.conf @@ -1,5 +1,3 @@ -CONFIG_WCD934X_CODEC=y -CONFIG_WCD9335_CODEC=y CONFIG_PINCTRL_WCD=y CONFIG_SND_SOC_WCD934X=y CONFIG_AUDIO_EXT_CLK=y @@ -30,6 +28,7 @@ CONFIG_SND_SOC_QDSP6V2=y CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y CONFIG_MSM_CDC_PINCTRL=y CONFIG_WCD9XXX_CODEC_CORE=y +CONFIG_SND_SOC_WCD_MBHC_ADC=y CONFIG_QTI_PP=y CONFIG_SND_HWDEP=y CONFIG_DTS_EAGLE=y diff --git a/config/sdm845autoconf.h b/config/sdm845autoconf.h index 7e9de815db80..ea14533dece3 100644 --- a/config/sdm845autoconf.h +++ b/config/sdm845autoconf.h @@ -11,8 +11,6 @@ * GNU General Public License for more details. */ -#define CONFIG_WCD934X_CODEC 1 -#define CONFIG_WCD9335_CODEC 1 #define CONFIG_PINCTRL_WCD 1 #define CONFIG_SND_SOC_WCD934X 1 #define CONFIG_AUDIO_EXT_CLK 1 From 38070be30b1d7bbcc7e9e862f81fabface2a0707 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Thu, 17 Aug 2017 18:21:59 +0530 Subject: [PATCH 012/276] audio-lnx: Add latest snapshot for audio drivers. Propagate the changes based on latest snapshot for audio kernel source tree at below cutoff of kernel msm-4.9 - (aed56b2df75 - "drm/msm/sde: update te vsync enable sequence change") Change-Id: I7ed5102146986b81e5cb9ca55432360b3549b60c Signed-off-by: Laxminath Kasam --- asoc/codecs/msm_sdw/msm_sdw_cdc.c | 12 +- asoc/codecs/pdata.h | 2 + asoc/codecs/wcd-dsp-mgr.c | 22 +- asoc/codecs/wcd934x/wcd934x-dsp-cntl.c | 8 +- asoc/codecs/wcd9xxx-core.c | 4 + asoc/codecs/wcd9xxx-utils.c | 13 + asoc/codecs/wsa881x.h | 28 +- asoc/msm-audio-effects-q6-v2.c | 60 ++--- asoc/msm-dai-fe.c | 14 +- asoc/msm-lsm-client.c | 14 +- asoc/msm-pcm-routing-v2.c | 98 ++++++- asoc/msm8998.c | 7 +- asoc/sdm660-common.c | 15 ++ asoc/sdm660-common.h | 1 + asoc/sdm660-ext-dai-links.c | 18 +- asoc/sdm660-external.c | 126 ++++++--- asoc/sdm660-external.h | 2 + asoc/sdm660-internal.c | 8 +- asoc/sdm845.c | 15 +- dsp/codecs/audio_utils_aio.c | 57 ++-- dsp/q6core.c | 256 +++++++++++++++++- dsp/q6voice.c | 353 ++++++++++++++++++++++++- include/dsp/apr_audio-v2.h | 70 +++++ include/dsp/q6core.h | 6 + include/dsp/q6voice.h | 118 ++++++++- soc/pinctrl-lpi.c | 42 +-- 26 files changed, 1192 insertions(+), 177 deletions(-) diff --git a/asoc/codecs/msm_sdw/msm_sdw_cdc.c b/asoc/codecs/msm_sdw/msm_sdw_cdc.c index 3ff184c7fab3..05d1d801ce74 100644 --- a/asoc/codecs/msm_sdw/msm_sdw_cdc.c +++ b/asoc/codecs/msm_sdw/msm_sdw_cdc.c @@ -938,7 +938,7 @@ static int msm_sdw_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_widget *widget = - snd_soc_dapm_kcontrol_widget(kcontrol); + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec); @@ -951,7 +951,7 @@ static int msm_sdw_vi_feed_mixer_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_dapm_widget *widget = - snd_soc_dapm_kcontrol_widget(kcontrol); + snd_soc_dapm_kcontrol_widget(kcontrol); struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); struct msm_sdw_priv *msm_sdw_p = snd_soc_codec_get_drvdata(codec); struct soc_multi_mixer_control *mixer = @@ -1376,15 +1376,19 @@ int msm_sdw_codec_info_create_codec_entry(struct snd_info_entry *codec_root, struct snd_info_entry *version_entry; struct msm_sdw_priv *msm_sdw; struct snd_soc_card *card; + char name[80]; if (!codec_root || !codec) return -EINVAL; msm_sdw = snd_soc_codec_get_drvdata(codec); card = codec->component.card; + + snprintf(name, sizeof(name), "%x.%s", (u32)msm_sdw->sdw_base_addr, + "msm-sdw-codec"); msm_sdw->entry = snd_info_create_subdir(codec_root->module, - "152c1000.msm-sdw-codec", - codec_root); + (const char *)name, + codec_root); if (!msm_sdw->entry) { dev_err(codec->dev, "%s: failed to create msm_sdw entry\n", __func__); diff --git a/asoc/codecs/pdata.h b/asoc/codecs/pdata.h index fa16b3d1c8f5..6df80eb064d6 100644 --- a/asoc/codecs/pdata.h +++ b/asoc/codecs/pdata.h @@ -179,6 +179,8 @@ struct wcd9xxx_pdata { int irq_base; int num_irqs; int reset_gpio; + bool has_buck_vsel_gpio; + struct device_node *buck_vsel_ctl_np; struct device_node *wcd_rst_np; struct wcd9xxx_amic amic_settings; struct slim_device slimbus_slave_device; diff --git a/asoc/codecs/wcd-dsp-mgr.c b/asoc/codecs/wcd-dsp-mgr.c index a6d46aef9724..6cc9f8c79d21 100644 --- a/asoc/codecs/wcd-dsp-mgr.c +++ b/asoc/codecs/wcd-dsp-mgr.c @@ -417,22 +417,23 @@ static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp, /* Go through the list of segments and download one by one */ list_for_each_entry(seg, wdsp->seg_list, list) { ret = wdsp_load_each_segment(wdsp, seg); - if (ret < 0) { - wdsp_broadcast_event_downseq(wdsp, - WDSP_EVENT_DLOAD_FAILED, - NULL); + if (ret) goto dload_error; - } } + /* Flush the list before setting status and notifying components */ + wdsp_flush_segment_list(wdsp->seg_list); + WDSP_SET_STATUS(wdsp, status); /* Notify all components that image is downloaded */ wdsp_broadcast_event_downseq(wdsp, post, NULL); +done: + return ret; dload_error: wdsp_flush_segment_list(wdsp->seg_list); -done: + wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED, NULL); return ret; } @@ -486,10 +487,14 @@ static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) /* Make sure wdsp is in good state */ if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) { WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status); - ret = -EINVAL; - goto done; + return -EINVAL; } + /* + * Acquire SSR mutex lock to make sure enablement of DSP + * does not race with SSR handling. + */ + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); /* Download the read-write sections of image */ ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE); if (ret < 0) { @@ -510,6 +515,7 @@ static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp) wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL); WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED); done: + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); return ret; } diff --git a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c index d82748b2689e..031940ecbaf9 100644 --- a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -763,10 +763,6 @@ static int wcd_control_handler(struct device *dev, void *priv_data, case WDSP_EVENT_DLOAD_FAILED: case WDSP_EVENT_POST_SHUTDOWN: - if (event == WDSP_EVENT_POST_DLOAD_CODE) - /* Mark DSP online since code download is complete */ - wcd_cntl_change_online_state(cntl, 1); - /* Disable CPAR */ wcd_cntl_cpar_ctrl(cntl, false); /* Disable all the clocks */ @@ -775,6 +771,10 @@ static int wcd_control_handler(struct device *dev, void *priv_data, dev_err(codec->dev, "%s: Failed to disable clocks, err = %d\n", __func__, ret); + + if (event == WDSP_EVENT_POST_DLOAD_CODE) + /* Mark DSP online since code download is complete */ + wcd_cntl_change_online_state(cntl, 1); break; case WDSP_EVENT_PRE_DLOAD_DATA: diff --git a/asoc/codecs/wcd9xxx-core.c b/asoc/codecs/wcd9xxx-core.c index 2ab5e894864e..81408e8928ef 100644 --- a/asoc/codecs/wcd9xxx-core.c +++ b/asoc/codecs/wcd9xxx-core.c @@ -1280,6 +1280,10 @@ static int wcd9xxx_slim_probe(struct slim_device *slim) ret = -EINVAL; goto err_codec; } + + if (pdata->has_buck_vsel_gpio) + msm_cdc_pinctrl_select_active_state(pdata->buck_vsel_ctl_np); + device_id = slim_get_device_id(slim); if (!device_id) { dev_err(&slim->dev, "%s: Error, no device id\n", __func__); diff --git a/asoc/codecs/wcd9xxx-utils.c b/asoc/codecs/wcd9xxx-utils.c index a1ea93843f28..eee90a28952d 100644 --- a/asoc/codecs/wcd9xxx-utils.c +++ b/asoc/codecs/wcd9xxx-utils.c @@ -342,6 +342,19 @@ struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev) goto err_parse_dt_prop; } + pdata->has_buck_vsel_gpio = of_property_read_bool(dev->of_node, + "qcom,has-buck-vsel-gpio"); + if (pdata->has_buck_vsel_gpio) { + pdata->buck_vsel_ctl_np = of_parse_phandle(dev->of_node, + "qcom,buck-vsel-gpio-node", 0); + if (!pdata->buck_vsel_ctl_np) { + dev_err(dev, "%s No entry for %s property in node %s\n", + __func__, "qcom,buck-vsel-gpio-node", + dev->of_node->full_name); + goto err_parse_dt_prop; + } + } + if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-mclk-clk-rate", &prop_val))) pdata->mclk_rate = prop_val; diff --git a/asoc/codecs/wsa881x.h b/asoc/codecs/wsa881x.h index be234ac0cd07..fbc60d86a12b 100644 --- a/asoc/codecs/wsa881x.h +++ b/asoc/codecs/wsa881x.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. +/* 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 @@ -20,9 +20,10 @@ #define WSA881X_MAX_SWR_PORTS 4 +#if IS_ENABLED(CONFIG_SND_SOC_WSA881X) extern int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, - u8 num_port, unsigned int *ch_mask, - unsigned int *ch_rate); + u8 num_port, unsigned int *ch_mask, + unsigned int *ch_rate); extern const u8 wsa881x_reg_readable[WSA881X_CACHE_SIZE]; extern struct regmap_config wsa881x_regmap_config; @@ -31,4 +32,25 @@ extern int wsa881x_codec_info_create_codec_entry( struct snd_soc_codec *codec); void wsa881x_regmap_defaults(struct regmap *regmap, u8 version); +#else +extern int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, + u8 num_port, unsigned int *ch_mask, + unsigned int *ch_rate) +{ + return 0; +} + +extern int wsa881x_codec_info_create_codec_entry( + struct snd_info_entry *codec_root, + struct snd_soc_codec *codec) +{ + return 0; +} + +void wsa881x_regmap_defaults(struct regmap *regmap, u8 version) +{ +} + +#endif + #endif /* _WSA881X_H */ diff --git a/asoc/msm-audio-effects-q6-v2.c b/asoc/msm-audio-effects-q6-v2.c index 962d64e94ba3..2385bac36f36 100644 --- a/asoc/msm-audio-effects-q6-v2.c +++ b/asoc/msm-audio-effects-q6-v2.c @@ -154,7 +154,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "VIRT ENABLE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_VIRTUALIZER; *updt_params++ = @@ -182,7 +182,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "VIRT STRENGTH", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_VIRTUALIZER; *updt_params++ = @@ -210,7 +210,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "VIRT OUT_TYPE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_VIRTUALIZER; *updt_params++ = @@ -238,7 +238,7 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "VIRT GAIN_ADJUST", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_VIRTUALIZER; *updt_params++ = @@ -316,7 +316,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_ENABLE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -344,7 +344,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_MODE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -372,7 +372,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_PRESET", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -400,7 +400,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_WET_MIX", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -428,7 +428,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_GAIN_ADJUST", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -456,7 +456,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_ROOM_LEVEL", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -484,7 +484,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_ROOM_HF_LEVEL", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -512,7 +512,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_DECAY_TIME", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -540,7 +540,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_DECAY_HF_RATIO", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -568,7 +568,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_REFLECTIONS_LEVEL", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -596,7 +596,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_REFLECTIONS_DELAY", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -624,7 +624,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_LEVEL", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -652,7 +652,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_DELAY", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -680,7 +680,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_DIFFUSION", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -708,7 +708,7 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "REVERB_DENSITY", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_REVERB; *updt_params++ = @@ -787,7 +787,7 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "BASS_BOOST_ENABLE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_BASS_BOOST; *updt_params++ = @@ -815,7 +815,7 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "BASS_BOOST_MODE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_BASS_BOOST; *updt_params++ = @@ -843,7 +843,7 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "BASS_BOOST_STRENGTH", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_BASS_BOOST; *updt_params++ = @@ -920,7 +920,7 @@ int msm_audio_effects_pbe_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "PBE_ENABLE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_PBE; *updt_params++ = @@ -946,7 +946,7 @@ int msm_audio_effects_pbe_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "PBE_PARAM", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_PBE; *updt_params++ = @@ -1031,7 +1031,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "EQ_ENABLE", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_POPLESS_EQUALIZER; *updt_params++ = @@ -1099,7 +1099,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "EQ_CONFIG", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_POPLESS_EQUALIZER; *updt_params++ = @@ -1150,7 +1150,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "EQ_BAND_INDEX", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_POPLESS_EQUALIZER; *updt_params++ = @@ -1182,7 +1182,7 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, MAX_INBAND_PARAM_SZ, "EQ_SINGLE_BAND_FREQ", rc); if (rc != 0) - break; + goto invalid_config; *updt_params++ = AUDPROC_MODULE_ID_POPLESS_EQUALIZER; *updt_params++ = @@ -1271,7 +1271,7 @@ static int __msm_audio_effects_volume_handler(struct audio_client *ac, "VOLUME/VOLUME2_GAIN_2CH", rc); if (rc != 0) - break; + goto invalid_config; if (instance == SOFT_VOLUME_INSTANCE_2) *updt_params++ = ASM_MODULE_ID_VOL_CTRL2; @@ -1320,7 +1320,7 @@ static int __msm_audio_effects_volume_handler(struct audio_client *ac, "VOLUME/VOLUME2_GAIN_MASTER", rc); if (rc != 0) - break; + goto invalid_config; if (instance == SOFT_VOLUME_INSTANCE_2) *updt_params++ = ASM_MODULE_ID_VOL_CTRL2; diff --git a/asoc/msm-dai-fe.c b/asoc/msm-dai-fe.c index 30a4d59f2ba4..89a9cc24a2e0 100644 --- a/asoc/msm-dai-fe.c +++ b/asoc/msm-dai-fe.c @@ -2376,8 +2376,20 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .rate_min = 8000, .rate_max = 384000, }, + .capture = { + .stream_name = "MultiMedia10 Capture", + .aif_name = "MM_UL10", + .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 = "MultiMedia10", .probe = fe_dai_probe, }, diff --git a/asoc/msm-lsm-client.c b/asoc/msm-lsm-client.c index e96d111a61e4..339125e2d775 100644 --- a/asoc/msm-lsm-client.c +++ b/asoc/msm-lsm-client.c @@ -1682,7 +1682,7 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s REG_SND_MODEL failed err %d\n", __func__, err); - return err; + goto done; } break; case SNDRV_LSM_SET_PARAMS: { @@ -1852,13 +1852,15 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: Invalid params event_status_v3\n", __func__); - return -EINVAL; + err = -EINVAL; + goto done; } if (copy_from_user(&userarg, arg, sizeof(userarg))) { dev_err(rtd->dev, "%s: err copyuser event_status_v3\n", __func__); - return -EFAULT; + err = -EFAULT; + goto done; } if (userarg.payload_size > @@ -1866,7 +1868,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, pr_err("%s: payload_size %d is invalid, max allowed = %d\n", __func__, userarg.payload_size, LISTEN_MAX_STATUS_PAYLOAD_SIZE); - return -EINVAL; + err = -EINVAL; + goto done; } size = sizeof(struct snd_lsm_event_status_v3) + @@ -1876,7 +1879,8 @@ static int msm_lsm_ioctl(struct snd_pcm_substream *substream, dev_err(rtd->dev, "%s: Allocation failed event status size %d\n", __func__, size); - return -EFAULT; + err = -EFAULT; + goto done; } user->payload_size = userarg.payload_size; err = msm_lsm_ioctl_shared(substream, cmd, user); diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index aa9b3abf6a48..8e3fe895d98d 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -1093,7 +1093,7 @@ int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode, port_type = MSM_AFE_PORT_TYPE_RX; } else if (stream_type == SNDRV_PCM_STREAM_CAPTURE) { session_type = SESSION_TYPE_TX; - if (passthr_mode != LEGACY_PCM) + if ((passthr_mode != LEGACY_PCM) && (passthr_mode != LISTEN)) path_type = ADM_PATH_COMPRESSED_TX; else path_type = ADM_PATH_LIVE_REC; @@ -3645,6 +3645,11 @@ static const struct snd_kcontrol_new ext_ec_ref_mux_ul16 = 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_ul10 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL10 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], @@ -7251,6 +7256,59 @@ static const struct snd_kcontrol_new mmul9_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new mmul10_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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, @@ -11489,6 +11547,7 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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_UL10", "MultiMedia10 Capture", 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("MM_UL16", "MultiMedia16 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), @@ -12225,6 +12284,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia9 Mixer", SND_SOC_NOPM, 0, 0, mmul9_mixer_controls, ARRAY_SIZE(mmul9_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia10 Mixer", SND_SOC_NOPM, 0, 0, + mmul10_mixer_controls, ARRAY_SIZE(mmul10_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia16 Mixer", SND_SOC_NOPM, 0, 0, mmul16_mixer_controls, ARRAY_SIZE(mmul16_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia17 Mixer", SND_SOC_NOPM, 0, 0, @@ -12557,6 +12618,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { &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_UL10 MUX", SND_SOC_NOPM, 0, 0, + &ext_ec_ref_mux_ul10), SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL16 MUX", SND_SOC_NOPM, 0, 0, &ext_ec_ref_mux_ul16), SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL17 MUX", SND_SOC_NOPM, 0, 0, @@ -12810,9 +12873,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia8 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia3 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia10 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia16 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia5 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, {"MultiMedia5 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"MultiMedia10 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, {"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, {"MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, {"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, @@ -13379,6 +13444,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"}, {"MultiMedia3 Mixer", "MI2S_TX", "MI2S_TX"}, {"MultiMedia5 Mixer", "MI2S_TX", "MI2S_TX"}, + {"MultiMedia10 Mixer", "MI2S_TX", "MI2S_TX"}, {"MultiMedia16 Mixer", "MI2S_TX", "MI2S_TX"}, {"MultiMedia1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"MultiMedia2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, @@ -13395,17 +13461,21 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia10 Mixer", "AUX_PCM_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"}, + {"MultiMedia10 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"}, {"MultiMedia16 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"}, {"MultiMedia16 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"}, + {"MultiMedia10 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"}, + {"MultiMedia10 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, {"MultiMedia16 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, {"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia2 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"}, @@ -13418,13 +13488,16 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia6 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"MultiMedia3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"MultiMedia5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia10 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"}, + {"MultiMedia10 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"}, {"MultiMedia16 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"}, + {"MultiMedia10 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia16 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia6 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"MultiMedia6 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, @@ -13559,6 +13632,14 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia9 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"MultiMedia9 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia10 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"MultiMedia10 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"MultiMedia10 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"MultiMedia10 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"MultiMedia10 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"MultiMedia10 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"MultiMedia10 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"MultiMedia10 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, {"MultiMedia20 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"MultiMedia20 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"MultiMedia20 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, @@ -13586,6 +13667,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia5 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, {"MultiMedia6 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, {"MultiMedia8 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, + {"MultiMedia10 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, {"MultiMedia16 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, {"MultiMedia16 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, @@ -13682,6 +13764,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia10 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"}, @@ -13701,6 +13784,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia1 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia3 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia4 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia10 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"}, @@ -13716,6 +13800,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MM_UL6", NULL, "MultiMedia6 Mixer"}, {"MM_UL8", NULL, "MultiMedia8 Mixer"}, {"MM_UL9", NULL, "MultiMedia9 Mixer"}, + {"MM_UL10", NULL, "MultiMedia10 Mixer"}, {"MM_UL16", NULL, "MultiMedia16 Mixer"}, {"MM_UL17", NULL, "MultiMedia17 Mixer"}, {"MM_UL18", NULL, "MultiMedia18 Mixer"}, @@ -14104,6 +14189,16 @@ static const struct snd_soc_dapm_route intercon[] = { {"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_UL10 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL10 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL10 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL10 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"AUDIO_REF_EC_UL10 MUX", "SLIM_1_TX", "SLIMBUS_1_TX"}, + {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_RX_0", "QUAT_TDM_RX_0"}, + {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_RX_1", "QUAT_TDM_RX_1"}, + {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_RX_2", "QUAT_TDM_RX_2"}, + {"AUDIO_REF_EC_UL10 MUX", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, {"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"}, @@ -14127,6 +14222,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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_UL10", NULL, "AUDIO_REF_EC_UL10 MUX"}, {"MM_UL16", NULL, "AUDIO_REF_EC_UL16 MUX"}, {"MM_UL17", NULL, "AUDIO_REF_EC_UL17 MUX"}, {"MM_UL18", NULL, "AUDIO_REF_EC_UL18 MUX"}, diff --git a/asoc/msm8998.c b/asoc/msm8998.c index b2fc005ebbb4..5f0128d05b0c 100644 --- a/asoc/msm8998.c +++ b/asoc/msm8998.c @@ -5036,12 +5036,13 @@ static struct snd_soc_dai_link msm_common_dai_links[] = { .id = MSM_FRONTEND_DAI_MULTIMEDIA7, }, { - .name = MSM_DAILINK_NAME(Compress3), - .stream_name = "Compress3", + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", .cpu_dai_name = "MultiMedia10", - .platform_name = "msm-compress-dsp", + .platform_name = "msm-pcm-dsp.1", .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", diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c index 16ecfaf9e8c7..f73e1cdd8f60 100644 --- a/asoc/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -44,6 +44,8 @@ enum { EXT_DISP_RX_IDX_MAX, }; +bool codec_reg_done; + /* TDM default config */ static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { { /* PRI TDM */ @@ -2016,6 +2018,12 @@ int msm_common_snd_controls_size(void) } EXPORT_SYMBOL(msm_common_snd_controls_size); +void msm_set_codec_reg_done(bool done) +{ + codec_reg_done = done; +} +EXPORT_SYMBOL(msm_set_codec_reg_done); + static inline int param_is_mask(int p) { return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && @@ -3027,6 +3035,12 @@ static const struct of_device_id sdm660_asoc_machine_of_match[] = { .data = "tasha_codec"}, { .compatible = "qcom,sdm660-asoc-snd-tavil", .data = "tavil_codec"}, + { .compatible = "qcom,sdm670-asoc-snd", + .data = "internal_codec"}, + { .compatible = "qcom,sdm670-asoc-snd-tasha", + .data = "tasha_codec"}, + { .compatible = "qcom,sdm670-asoc-snd-tavil", + .data = "tavil_codec"}, {}, }; @@ -3044,6 +3058,7 @@ static int msm_asoc_machine_probe(struct platform_device *pdev) if (!pdata) return -ENOMEM; + msm_set_codec_reg_done(false); match = of_match_node(sdm660_asoc_machine_of_match, pdev->dev.of_node); if (!match) diff --git a/asoc/sdm660-common.h b/asoc/sdm660-common.h index 682dcd5d055e..0544b968d159 100644 --- a/asoc/sdm660-common.h +++ b/asoc/sdm660-common.h @@ -122,4 +122,5 @@ 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); int msm_common_snd_controls_size(void); +void msm_set_codec_reg_done(bool done); #endif diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c index 71ba1fa9060f..a76c16d76d00 100644 --- a/asoc/sdm660-ext-dai-links.c +++ b/asoc/sdm660-ext-dai-links.c @@ -29,8 +29,15 @@ #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_card snd_soc_card_msm_card_tavil = { + .name = "sdm670-tavil-snd-card", + .late_probe = msm_snd_card_tavil_late_probe, +}; + +static struct snd_soc_card snd_soc_card_msm_card_tasha = { + .name = "sdm670-tasha-snd-card", + .late_probe = msm_snd_card_tasha_late_probe, +}; static struct snd_soc_ops msm_ext_slimbus_be_ops = { .hw_params = msm_snd_hw_params, @@ -990,13 +997,14 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .id = MSM_FRONTEND_DAI_MULTIMEDIA7, }, {/* hw:x,16 */ - .name = MSM_DAILINK_NAME(Compress3), - .stream_name = "Compress3", + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", .cpu_dai_name = "MultiMedia10", - .platform_name = "msm-compress-dsp", + .platform_name = "msm-pcm-dsp.1", .dynamic = 1, .dpcm_capture = 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", diff --git a/asoc/sdm660-external.c b/asoc/sdm660-external.c index 9085282964c0..dd8983c973e4 100644 --- a/asoc/sdm660-external.c +++ b/asoc/sdm660-external.c @@ -56,7 +56,6 @@ static int msm_ext_spk_control = 1; static struct wcd_mbhc_config *wcd_mbhc_cfg_ptr; -bool codec_reg_done; struct msm_asoc_wcd93xx_codec { void* (*get_afe_config_fn)(struct snd_soc_codec *codec, @@ -604,23 +603,23 @@ static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol, static void *def_ext_mbhc_cal(void) { - void *tavil_wcd_cal; + void *wcd_mbhc_cal; struct wcd_mbhc_btn_detect_cfg *btn_cfg; u16 *btn_high; - tavil_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, + wcd_mbhc_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS, WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL); - if (!tavil_wcd_cal) + if (!wcd_mbhc_cal) return NULL; -#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tavil_wcd_cal)->X) = (Y)) +#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(wcd_mbhc_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)) +#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(wcd_mbhc_cal)->X) = (Y)) S(num_btn, WCD_MBHC_DEF_BUTTONS); #undef S - btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal); + btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(wcd_mbhc_cal); btn_high = ((void *)&btn_cfg->_v_btn_low) + (sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn); @@ -633,7 +632,7 @@ static void *def_ext_mbhc_cal(void) btn_high[6] = 500; btn_high[7] = 500; - return tavil_wcd_cal; + return wcd_mbhc_cal; } static inline int param_is_mask(int p) @@ -1479,6 +1478,79 @@ static struct snd_soc_dapm_route wcd_audio_paths[] = { {"MIC BIAS4", NULL, "MCLK"}, }; +int msm_snd_card_tasha_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_ext_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err_mbhc_cal; + } + wcd_mbhc_cfg_ptr->calibration = mbhc_calibration; + ret = tasha_mbhc_hs_detect(rtd->codec, wcd_mbhc_cfg_ptr); + 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; +} + +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; + } + + mbhc_calibration = def_ext_mbhc_cal(); + if (!mbhc_calibration) { + ret = -ENOMEM; + goto err; + } + wcd_mbhc_cfg_ptr->calibration = mbhc_calibration; + ret = tavil_mbhc_hs_detect(rtd->codec, wcd_mbhc_cfg_ptr); + if (ret) { + dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n", + __func__, ret); + goto err_free_mbhc_cal; + } + return 0; + +err_free_mbhc_cal: + kfree(mbhc_calibration); +err: + return ret; +} + /** * msm_audrx_init - Audio init function of sound card instantiate. * @@ -1699,7 +1771,6 @@ int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) if (!entry) { pr_debug("%s: Cannot create codecs module entry\n", __func__); - pdata->codec_root = NULL; goto done; } pdata->codec_root = entry; @@ -1722,50 +1793,17 @@ int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) if (!entry) { pr_debug("%s: Cannot create codecs module entry\n", __func__); - ret = 0; - goto err_snd_module; + goto done; } 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; - } - - } - codec_reg_done = true; done: + msm_set_codec_reg_done(true); return 0; -err_snd_module: err_afe_cfg: -err_mbhc_cal: return ret; } EXPORT_SYMBOL(msm_audrx_init); diff --git a/asoc/sdm660-external.h b/asoc/sdm660-external.h index acf5735c2502..d53e7c7d9518 100644 --- a/asoc/sdm660-external.h +++ b/asoc/sdm660-external.h @@ -30,6 +30,8 @@ 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); +int msm_snd_card_tavil_late_probe(struct snd_soc_card *card); +int msm_snd_card_tasha_late_probe(struct snd_soc_card *card); #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 *); diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c index fc2378d4e8d2..b5b05b10c9a5 100644 --- a/asoc/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -1314,6 +1314,7 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) msm_dig_codec_info_create_codec_entry(codec_root, dig_cdc); msm_anlg_codec_info_create_codec_entry(codec_root, ana_cdc); done: + msm_set_codec_reg_done(true); return 0; } @@ -1914,13 +1915,14 @@ static struct snd_soc_dai_link msm_int_dai[] = { .id = MSM_FRONTEND_DAI_MULTIMEDIA7, }, {/* hw:x,16 */ - .name = MSM_DAILINK_NAME(Compress3), - .stream_name = "Compress3", + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", .cpu_dai_name = "MultiMedia10", - .platform_name = "msm-compress-dsp", + .platform_name = "msm-pcm-dsp.1", .dynamic = 1, .dpcm_capture = 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", diff --git a/asoc/sdm845.c b/asoc/sdm845.c index e38def3c9e95..7f29450062a9 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -5126,12 +5126,13 @@ static struct snd_soc_dai_link msm_common_dai_links[] = { .id = MSM_FRONTEND_DAI_MULTIMEDIA7, }, { - .name = MSM_DAILINK_NAME(Compress3), - .stream_name = "Compress3", + .name = MSM_DAILINK_NAME(MultiMedia10), + .stream_name = "MultiMedia10", .cpu_dai_name = "MultiMedia10", - .platform_name = "msm-compress-dsp", + .platform_name = "msm-pcm-dsp.1", .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", @@ -6696,16 +6697,18 @@ static int msm_init_wsa_dev(struct platform_device *pdev, ret = of_property_read_u32(pdev->dev.of_node, "qcom,wsa-max-devs", &wsa_max_devs); if (ret) { - dev_dbg(&pdev->dev, + dev_info(&pdev->dev, "%s: wsa-max-devs property missing in DT %s, ret = %d\n", __func__, pdev->dev.of_node->full_name, ret); - goto err; + card->num_aux_devs = 0; + return 0; } if (wsa_max_devs == 0) { dev_warn(&pdev->dev, "%s: Max WSA devices is 0 for this target?\n", __func__); - goto err; + card->num_aux_devs = 0; + return 0; } /* Get count of WSA device phandles for this platform */ diff --git a/dsp/codecs/audio_utils_aio.c b/dsp/codecs/audio_utils_aio.c index 52dced4f3558..c67a24d7aa15 100644 --- a/dsp/codecs/audio_utils_aio.c +++ b/dsp/codecs/audio_utils_aio.c @@ -30,7 +30,9 @@ #ifdef CONFIG_USE_DEV_CTRL_VOLUME #include #endif /*CONFIG_USE_DEV_CTRL_VOLUME*/ +static DEFINE_MUTEX(lock); #ifdef CONFIG_DEBUG_FS + int audio_aio_debug_open(struct inode *inode, struct file *file) { file->private_data = inode->i_private; @@ -43,29 +45,37 @@ ssize_t audio_aio_debug_read(struct file *file, char __user *buf, const int debug_bufmax = 4096; static char buffer[4096]; int n = 0; - struct q6audio_aio *audio = file->private_data; + struct q6audio_aio *audio; - 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)); + mutex_lock(&lock); + if (file->private_data != NULL) { + 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)); + } + mutex_unlock(&lock); buffer[n] = 0; return simple_read_from_buffer(buf, count, ppos, buffer, n); } @@ -580,6 +590,7 @@ 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(&lock); mutex_lock(&audio->lock); mutex_lock(&audio->read_lock); mutex_lock(&audio->write_lock); @@ -622,6 +633,8 @@ int audio_aio_release(struct inode *inode, struct file *file) #endif kfree(audio->codec_cfg); kfree(audio); + file->private_data = NULL; + mutex_unlock(&lock); return 0; } diff --git a/dsp/q6core.c b/dsp/q6core.c index dd97a495221b..181a800cbf1f 100644 --- a/dsp/q6core.c +++ b/dsp/q6core.c @@ -20,7 +20,9 @@ #include #include #include +#include #include +#include "adsp_err.h" #define TIMEOUT_MS 1000 /* @@ -36,16 +38,30 @@ enum { CORE_MAX_CAL }; +enum ver_query_status { + VER_QUERY_UNATTEMPTED, + VER_QUERY_UNSUPPORTED, + VER_QUERY_SUPPORTED +}; + +struct q6core_avcs_ver_info { + enum ver_query_status status; + struct avcs_fwk_ver_info ver_info; +}; + struct q6core_str { struct apr_svc *core_handle_q; wait_queue_head_t bus_bw_req_wait; wait_queue_head_t cmd_req_wait; + wait_queue_head_t avcs_fwk_ver_req_wait; u32 bus_bw_resp_received; enum cmd_flags { FLAG_NONE, FLAG_CMDRSP_LICENSE_RESULT } cmd_resp_received_flag; + u32 avcs_fwk_ver_resp_received; struct mutex cmd_lock; + struct mutex ver_lock; union { struct avcs_cmdrsp_get_license_validation_result cmdrsp_license_result; @@ -54,6 +70,7 @@ struct q6core_str { struct cal_type_data *cal_data[CORE_MAX_CAL]; uint32_t mem_map_cal_handle; int32_t adsp_status; + struct q6core_avcs_ver_info q6core_avcs_ver_info; }; static struct q6core_str q6core_lcl; @@ -65,9 +82,61 @@ struct generic_get_data_ { }; static struct generic_get_data_ *generic_get_data; +static int parse_fwk_version_info(uint32_t *payload) +{ + size_t fwk_ver_size; + size_t svc_size; + int num_services; + int ret = 0; + + pr_debug("%s: Payload info num services %d\n", + __func__, payload[4]); + /* + * payload1[4] is the number of services running on DSP + * Based on this info, we copy the payload into core + * avcs version info structure. + */ + num_services = payload[4]; + q6core_lcl.q6core_avcs_ver_info.ver_info.avcs_fwk_version. + num_services = num_services; + if (num_services > VSS_MAX_AVCS_NUM_SERVICES) { + pr_err("%s: num_services: %d greater than max services: %d\n", + __func__, num_services, VSS_MAX_AVCS_NUM_SERVICES); + ret = -EINVAL; + goto done; + } + fwk_ver_size = sizeof(struct avcs_get_fwk_version); + svc_size = num_services * sizeof(struct avs_svc_api_info); + /* + * Dynamically allocate memory for all + * the services based on num_services + */ + q6core_lcl.q6core_avcs_ver_info.ver_info.services = NULL; + q6core_lcl.q6core_avcs_ver_info.ver_info.services = + kzalloc(svc_size, GFP_ATOMIC); + if (q6core_lcl.q6core_avcs_ver_info.ver_info.services == NULL) { + ret = -ENOMEM; + goto done; + } + /* + * memcpy is done twice because the memory allocated for + * q6core_lcl.q6core_avcs_ver_info.ver_info is not + * contiguous. + */ + memcpy(&q6core_lcl.q6core_avcs_ver_info.ver_info, + (uint8_t *)payload, fwk_ver_size); + memcpy(q6core_lcl.q6core_avcs_ver_info.ver_info.services, + (uint8_t *)&payload[sizeof(struct avcs_get_fwk_version)/ + sizeof(uint32_t)], svc_size); + ret = 0; +done: + return ret; +} + static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) { uint32_t *payload1; + int ret = 0; if (data == NULL) { pr_err("%s: data argument is null\n", __func__); @@ -118,6 +187,17 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) q6core_lcl.bus_bw_resp_received = 1; wake_up(&q6core_lcl.bus_bw_req_wait); break; + case AVCS_CMD_GET_FWK_VERSION: + pr_debug("%s: Cmd = AVCS_CMD_GET_FWK_VERSION status[%s]\n", + __func__, adsp_err_get_err_str(payload1[1])); + /* ADSP status to match Linux error standard */ + q6core_lcl.adsp_status = -payload1[1]; + if (payload1[1] == ADSP_EUNSUPPORTED) + q6core_lcl.q6core_avcs_ver_info.status = + VER_QUERY_UNSUPPORTED; + q6core_lcl.avcs_fwk_ver_resp_received = 1; + wake_up(&q6core_lcl.avcs_fwk_ver_req_wait); + break; default: pr_err("%s: Invalid cmd rsp[0x%x][0x%x] opcode %d\n", __func__, @@ -130,7 +210,7 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) case RESET_EVENTS:{ pr_debug("%s: Reset event received in Core service\n", __func__); - apr_reset(q6core_lcl.core_handle_q); + /* no reset done as the data will not change after SSR*/ q6core_lcl.core_handle_q = NULL; break; } @@ -161,6 +241,18 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) q6core_lcl.cmd_resp_received_flag = FLAG_CMDRSP_LICENSE_RESULT; wake_up(&q6core_lcl.cmd_req_wait); break; + case AVCS_CMDRSP_GET_FWK_VERSION: + pr_debug("%s: Received AVCS_CMDRSP_GET_FWK_VERSION\n", + __func__); + payload1 = data->payload; + q6core_lcl.q6core_avcs_ver_info.status = VER_QUERY_SUPPORTED; + q6core_lcl.avcs_fwk_ver_resp_received = 1; + ret = parse_fwk_version_info(payload1); + if (ret < 0) + pr_err("%s: Failed to parse payload:%d\n", + __func__, ret); + wake_up(&q6core_lcl.avcs_fwk_ver_req_wait); + break; default: pr_err("%s: Message id from adsp core svc: 0x%x\n", __func__, data->opcode); @@ -217,6 +309,157 @@ struct cal_block_data *cal_utils_get_cal_block_by_key( return NULL; } +static int q6core_send_get_avcs_fwk_ver_cmd(void) +{ + struct apr_hdr avcs_ver_cmd; + int ret; + + avcs_ver_cmd.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + avcs_ver_cmd.pkt_size = sizeof(struct apr_hdr); + avcs_ver_cmd.src_port = 0; + avcs_ver_cmd.dest_port = 0; + avcs_ver_cmd.token = 0; + avcs_ver_cmd.opcode = AVCS_CMD_GET_FWK_VERSION; + + q6core_lcl.adsp_status = 0; + q6core_lcl.avcs_fwk_ver_resp_received = 0; + + ret = apr_send_pkt(q6core_lcl.core_handle_q, + (uint32_t *) &avcs_ver_cmd); + if (ret < 0) { + pr_err("%s: failed to send apr packet, ret=%d\n", __func__, + ret); + goto done; + } + + ret = wait_event_timeout(q6core_lcl.avcs_fwk_ver_req_wait, + (q6core_lcl.avcs_fwk_ver_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout for AVCS fwk version info\n", + __func__); + ret = -ETIMEDOUT; + goto done; + } + + if (q6core_lcl.adsp_status < 0) { + /* + * adsp_err_get_err_str expects a positive value but we store + * the DSP error as negative to match the Linux error standard. + * Pass in the negated value so adsp_err_get_err_str returns + * the correct string. + */ + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(-q6core_lcl.adsp_status)); + ret = adsp_err_get_lnx_err_code(q6core_lcl.adsp_status); + goto done; + } + + ret = 0; + +done: + return ret; +} + +int q6core_get_service_version(uint32_t service_id, + struct avcs_fwk_ver_info *ver_info, + size_t size) +{ + int i; + uint32_t num_services; + size_t svc_size; + + svc_size = q6core_get_avcs_service_size(service_id); + if (svc_size != size) { + pr_err("%s: Expected size: %ld, Provided size: %ld", + __func__, svc_size, size); + return -EINVAL; + } + + num_services = + q6core_lcl.q6core_avcs_ver_info.ver_info. + avcs_fwk_version.num_services; + + if (ver_info == NULL) { + pr_err("%s: NULL parameter ver_info\n", __func__); + return -EINVAL; + } + + memcpy(ver_info, &q6core_lcl.q6core_avcs_ver_info. + ver_info.avcs_fwk_version, sizeof(struct avcs_get_fwk_version)); + + if (service_id == AVCS_SERVICE_ID_ALL) { + memcpy(&ver_info->services[0], &q6core_lcl. + q6core_avcs_ver_info.ver_info.services[0], + (num_services * sizeof(struct avs_svc_api_info))); + } else { + for (i = 0; i < num_services; i++) { + if (q6core_lcl.q6core_avcs_ver_info. + ver_info.services[i].service_id == service_id) { + memcpy(&ver_info->services[0], + &q6core_lcl.q6core_avcs_ver_info. + ver_info.services[i], size); + break; + } + } + } + + return 0; +} +EXPORT_SYMBOL(q6core_get_service_version); + +size_t q6core_get_avcs_service_size(uint32_t service_id) +{ + int ret = 0; + uint32_t num_services; + + num_services = + q6core_lcl.q6core_avcs_ver_info.ver_info. + avcs_fwk_version.num_services; + + mutex_lock(&(q6core_lcl.ver_lock)); + pr_debug("%s: q6core_avcs_ver_info.status(%d)\n", __func__, + q6core_lcl.q6core_avcs_ver_info.status); + + switch (q6core_lcl.q6core_avcs_ver_info.status) { + case VER_QUERY_SUPPORTED: + pr_debug("%s: AVCS FWK version query already attempted\n", + __func__); + ret = num_services * sizeof(struct avs_svc_api_info); + break; + case VER_QUERY_UNSUPPORTED: + ret = -EOPNOTSUPP; + break; + case VER_QUERY_UNATTEMPTED: + pr_debug("%s: Attempting AVCS FWK version query\n", __func__); + if (q6core_is_adsp_ready()) { + ret = q6core_send_get_avcs_fwk_ver_cmd(); + if (ret == 0) + ret = num_services * + sizeof(struct avs_svc_api_info); + } else { + pr_err("%s: ADSP is not ready to query version\n", + __func__); + ret = -ENODEV; + } + break; + default: + pr_err("%s: Invalid version query status %d\n", __func__, + q6core_lcl.q6core_avcs_ver_info.status); + ret = -EINVAL; + break; + } + mutex_unlock(&(q6core_lcl.ver_lock)); + + if (service_id != AVCS_SERVICE_ID_ALL) + return sizeof(struct avs_svc_api_info); + + return ret; +} +EXPORT_SYMBOL(q6core_get_avcs_service_size); + int32_t core_set_license(uint32_t key, uint32_t module_id) { struct avcs_cmd_set_license *cmd_setl = NULL; @@ -827,18 +1070,16 @@ static int q6core_init_cal_data(void) static int __init core_init(void) { + memset(&q6core_lcl, 0, sizeof(struct q6core_str)); 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); + init_waitqueue_head(&q6core_lcl.avcs_fwk_ver_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; + mutex_init(&q6core_lcl.ver_lock); q6core_init_cal_data(); + return 0; } module_init(core_init); @@ -846,6 +1087,7 @@ module_init(core_init); static void __exit core_exit(void) { mutex_destroy(&q6core_lcl.cmd_lock); + mutex_destroy(&q6core_lcl.ver_lock); q6core_delete_cal_data(); } module_exit(core_exit); diff --git a/dsp/q6voice.c b/dsp/q6voice.c index cfcc822d5484..28e50d9f55eb 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -24,8 +24,9 @@ #include #include #include -#include +#include #include +#include #include "adsp_err.h" #define TIMEOUT_MS 300 @@ -33,6 +34,9 @@ #define CMD_STATUS_SUCCESS 0 #define CMD_STATUS_FAIL 1 +#define NUM_CHANNELS_MONO 1 +#define NUM_CHANNELS_STEREO 2 +#define CVP_VERSION_2 2 enum { VOC_TOKEN_NONE, @@ -83,6 +87,11 @@ static int voice_send_cvp_device_channels_cmd(struct voice_data *v); static int voice_send_cvp_media_format_cmd(struct voice_data *v, uint32_t param_type); static int voice_send_cvp_topology_commit_cmd(struct voice_data *v); +static int voice_send_cvp_channel_info_cmd(struct voice_data *v); +static int voice_send_cvp_channel_info_v2(struct voice_data *v, + uint32_t param_type); +static int voice_get_avcs_version_per_service(uint32_t service_id); + static int voice_cvs_stop_playback(struct voice_data *v); static int voice_cvs_start_playback(struct voice_data *v); @@ -3793,6 +3802,295 @@ int voc_unmap_rtac_block(uint32_t *mem_map_handle) return result; } +static int voice_send_cvp_channel_info_v2(struct voice_data *v, + uint32_t param_type) +{ + int ret; + struct cvp_set_channel_info_cmd_v2 cvp_set_channel_info_cmd; + void *apr_cvp; + u16 cvp_handle; + struct vss_icommon_param_data_channel_info_v2_t + *channel_info_param_data = + &cvp_set_channel_info_cmd. + cvp_set_ch_info_param_v2.param_data; + struct vss_param_vocproc_dev_channel_info_t *channel_info = + &channel_info_param_data->channel_info; + + 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); + memset(&cvp_set_channel_info_cmd, 0, sizeof(cvp_set_channel_info_cmd)); + + cvp_set_channel_info_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_set_channel_info_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_set_channel_info_cmd) - APR_HDR_SIZE); + cvp_set_channel_info_cmd.hdr.src_svc = 0; + cvp_set_channel_info_cmd.hdr.src_domain = APR_DOMAIN_APPS; + cvp_set_channel_info_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_set_channel_info_cmd.hdr.dest_svc = 0; + cvp_set_channel_info_cmd.hdr.dest_domain = APR_DOMAIN_ADSP; + cvp_set_channel_info_cmd.hdr.dest_port = cvp_handle; + cvp_set_channel_info_cmd.hdr.token = 0; + cvp_set_channel_info_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_PARAM_V2; + + cvp_set_channel_info_cmd.cvp_set_ch_info_param_v2.mem_size = + sizeof(struct vss_icommon_param_data_channel_info_v2_t); + + channel_info_param_data->module_id = VSS_MODULE_CVD_GENERIC; + channel_info_param_data->param_size = + sizeof(struct vss_param_vocproc_dev_channel_info_t); + + /* Device specific data */ + switch (param_type) { + case RX_PATH: + channel_info_param_data->param_id = + VSS_PARAM_VOCPROC_RX_CHANNEL_INFO; + channel_info->num_channels = v->dev_rx.no_of_channels; + channel_info->bits_per_sample = v->dev_rx.bits_per_sample; + break; + + case TX_PATH: + channel_info_param_data->param_id = + VSS_PARAM_VOCPROC_TX_CHANNEL_INFO; + channel_info->num_channels = v->dev_tx.no_of_channels; + channel_info->bits_per_sample = v->dev_tx.bits_per_sample; + break; + + case EC_REF_PATH: + channel_info_param_data->param_id = + VSS_PARAM_VOCPROC_EC_REF_CHANNEL_INFO; + channel_info->num_channels = v->dev_rx.no_of_channels; + channel_info->bits_per_sample = v->dev_rx.bits_per_sample; + break; + default: + pr_err("%s: Invalid param type\n", + __func__); + ret = -EINVAL; + goto done; + } + + if (channel_info->num_channels == NUM_CHANNELS_MONO) { + channel_info->channel_mapping[0] = PCM_CHANNEL_FC; + } else if (channel_info->num_channels == NUM_CHANNELS_STEREO) { + channel_info->channel_mapping[0] = PCM_CHANNEL_FL; + channel_info->channel_mapping[1] = PCM_CHANNEL_FR; + } else { + pr_err("%s: Unsupported num channels: %d\n", + __func__, channel_info->num_channels); + ret = -EINVAL; + goto done; + } + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_set_channel_info_cmd); + if (ret < 0) { + pr_err("%s: Failed to send VSS_ICOMMON_CMD_SET_PARAM_V2\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__); + ret = -ETIMEDOUT; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s] handle = %d\n", __func__, + adsp_err_get_err_str(v->async_err), cvp_handle); + ret = adsp_err_get_lnx_err_code(v->async_err); + goto done; + } + ret = 0; +done: + return ret; +} + +static int voice_send_cvp_channel_info_cmd(struct voice_data *v) +{ + int ret = 0; + + ret = voice_send_cvp_channel_info_v2(v, RX_PATH); + if (ret < 0) { + pr_err("%s: Error in sending cvp_channel_info RX: %d\n", + __func__, ret); + goto done; + } + + ret = voice_send_cvp_channel_info_v2(v, TX_PATH); + if (ret < 0) { + pr_err("%s: Error in sending cvp_channel_info TX: %d\n", + __func__, ret); + goto done; + } + + ret = voice_send_cvp_channel_info_v2(v, EC_REF_PATH); + if (ret < 0) { + pr_err("%s: Error in sending cvp_channel_info EC Ref: %d\n", + __func__, ret); + goto done; + } +done: + return ret; +} + +static int voice_send_cvp_mfc_config_v2(struct voice_data *v) +{ + int ret; + struct cvp_set_mfc_config_cmd_v2 cvp_set_mfc_config_cmd; + void *apr_cvp; + u16 cvp_handle; + struct vss_icommon_param_data_mfc_config_v2_t *cvp_config_param_data = + &cvp_set_mfc_config_cmd.cvp_set_mfc_param_v2.param_data; + struct vss_param_mfc_config_info_t *mfc_config_info = + &cvp_config_param_data->mfc_config_info; + + 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); + memset(&cvp_set_mfc_config_cmd, 0, sizeof(cvp_set_mfc_config_cmd)); + + cvp_set_mfc_config_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_set_mfc_config_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_set_mfc_config_cmd) - APR_HDR_SIZE); + cvp_set_mfc_config_cmd.hdr.src_svc = 0; + cvp_set_mfc_config_cmd.hdr.src_domain = APR_DOMAIN_APPS; + cvp_set_mfc_config_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_set_mfc_config_cmd.hdr.dest_svc = 0; + cvp_set_mfc_config_cmd.hdr.dest_domain = APR_DOMAIN_ADSP; + cvp_set_mfc_config_cmd.hdr.dest_port = cvp_handle; + cvp_set_mfc_config_cmd.hdr.token = 0; + cvp_set_mfc_config_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_PARAM_V2; + cvp_set_mfc_config_cmd.cvp_set_mfc_param_v2.mem_size = + sizeof(struct vss_icommon_param_data_mfc_config_v2_t); + + cvp_config_param_data->module_id = AUDPROC_MODULE_ID_MFC; + cvp_config_param_data->param_id = + AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT; + cvp_config_param_data->param_size = + sizeof(struct vss_param_mfc_config_info_t); + + mfc_config_info->num_channels = v->dev_rx.no_of_channels; + mfc_config_info->bits_per_sample = 16; + mfc_config_info->sample_rate = v->dev_rx.sample_rate; + + if (mfc_config_info->num_channels == NUM_CHANNELS_MONO) { + mfc_config_info->channel_type[0] = PCM_CHANNEL_FC; + } else if (mfc_config_info->num_channels == NUM_CHANNELS_STEREO) { + mfc_config_info->channel_type[0] = PCM_CHANNEL_FL; + mfc_config_info->channel_type[1] = PCM_CHANNEL_FR; + } else { + pr_err("%s: Unsupported num channels: %d\n", + __func__, mfc_config_info->num_channels); + ret = -EINVAL; + goto done; + } + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_set_mfc_config_cmd); + if (ret < 0) { + pr_err("%s: Failed to send VSS_ICOMMON_CMD_SET_PARAM_V2 %d\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: wait_event timeout\n", __func__); + ret = -ETIMEDOUT; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s] handle = %d\n", __func__, + adsp_err_get_err_str(v->async_err), cvp_handle); + ret = adsp_err_get_lnx_err_code(v->async_err); + goto done; + } + ret = 0; +done: + return ret; +} + +static int voice_send_cvp_mfc_config_cmd(struct voice_data *v) +{ + int ret = 0; + + if (common.cvp_version >= CVP_VERSION_2) { + ret = voice_send_cvp_mfc_config_v2(v); + } else { + pr_warn("%s: CVP Version not supported\n", __func__); + ret = -EINVAL; + } + + return ret; +} + +static int voice_get_avcs_version_per_service(uint32_t service_id) +{ + int ret = 0; + size_t svc_size; + struct avcs_fwk_ver_info ver_info = {{0}, NULL}; + + if (service_id == AVCS_SERVICE_ID_ALL) { + pr_err("%s: Invalid service id: %d", __func__, + AVCS_SERVICE_ID_ALL); + return -EINVAL; + } + + svc_size = sizeof(struct avs_svc_api_info); + ver_info.services = kzalloc(svc_size, GFP_KERNEL); + if (ver_info.services == NULL) + return -ENOMEM; + + ret = q6core_get_service_version(service_id, &ver_info, svc_size); + if (ret < 0) + goto done; + + ret = ver_info.services[0].api_version; + common.is_avcs_version_queried = true; +done: + kfree(ver_info.services); + return ret; +} + static int voice_setup_vocproc(struct voice_data *v) { int ret = 0; @@ -3803,6 +4101,18 @@ static int voice_setup_vocproc(struct voice_data *v) goto fail; } + if (common.is_avcs_version_queried == false) + common.cvp_version = voice_get_avcs_version_per_service( + APRV2_IDS_SERVICE_ID_ADSP_CVP_V); + + if (common.cvp_version < 0) { + pr_err("%s: Invalid CVP version %d\n", + __func__, common.cvp_version); + ret = -EINVAL; + goto fail; + } + pr_debug("%s: CVP Version %d\n", __func__, common.cvp_version); + ret = voice_send_cvp_media_fmt_info_cmd(v); if (ret < 0) { pr_err("%s: Set media format info failed err:%d\n", __func__, @@ -3817,6 +4127,15 @@ static int voice_setup_vocproc(struct voice_data *v) goto fail; } + /* Send MFC config only when the no of channels are more than 1 */ + if (v->dev_rx.no_of_channels > NUM_CHANNELS_MONO) { + ret = voice_send_cvp_mfc_config_cmd(v); + if (ret < 0) { + pr_warn("%s: Set mfc config failed err:%d\n", + __func__, ret); + } + } + voice_send_cvs_register_cal_cmd(v); voice_send_cvp_register_dev_cfg_cmd(v); voice_send_cvp_register_cal_cmd(v); @@ -3962,11 +4281,18 @@ static int voice_send_cvp_device_channels_cmd(struct voice_data *v) static int voice_send_cvp_media_fmt_info_cmd(struct voice_data *v) { - int ret; + int ret = 0; - ret = voice_send_cvp_device_channels_cmd(v); - if (ret < 0) + if (common.cvp_version < CVP_VERSION_2) + ret = voice_send_cvp_device_channels_cmd(v); + else + ret = voice_send_cvp_channel_info_cmd(v); + + if (ret < 0) { + pr_err("%s: Set channel info failed err: %d\n", __func__, + ret); goto done; + } if (voice_get_cvd_int_version(common.cvd_version) >= CVD_INT_VERSION_2_3) { @@ -3994,7 +4320,7 @@ static int voice_send_cvp_media_format_cmd(struct voice_data *v, void *apr_cvp; u16 cvp_handle; struct vss_icommon_param_data_t *media_fmt_param_data = - &cvp_set_media_format_cmd.cvp_set_param_v2.param_data; + &cvp_set_media_format_cmd.cvp_set_media_param_v2.param_data; struct vss_param_endpoint_media_format_info_t *media_fmt_info = &media_fmt_param_data->media_format_info; @@ -4032,7 +4358,7 @@ static int voice_send_cvp_media_format_cmd(struct voice_data *v, cvp_set_media_format_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_PARAM_V2; /* Fill param data */ - cvp_set_media_format_cmd.cvp_set_param_v2.mem_size = + cvp_set_media_format_cmd.cvp_set_media_param_v2.mem_size = sizeof(struct vss_icommon_param_data_t); media_fmt_param_data->module_id = VSS_MODULE_CVD_GENERIC; media_fmt_param_data->param_size = @@ -6197,6 +6523,15 @@ int voc_enable_device(uint32_t session_id) goto done; } + /* Send MFC config only when the no of channels are > 1 */ + if (v->dev_rx.no_of_channels > NUM_CHANNELS_MONO) { + ret = voice_send_cvp_mfc_config_cmd(v); + if (ret < 0) { + pr_warn("%s: Set mfc config failed err: %d\n", + __func__, ret); + } + } + voice_send_cvp_register_dev_cfg_cmd(v); voice_send_cvp_register_cal_cmd(v); voice_send_cvp_register_vol_cal_cmd(v); @@ -7054,7 +7389,8 @@ static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv) case VSS_ICOMMON_CMD_SET_PARAM_V2: switch (data->token) { case VOC_SET_MEDIA_FORMAT_PARAM_TOKEN: - pr_debug("%s: VSS_ICOMMON_CMD_SET_PARAM_V2 called by voice_send_cvp_media_format_cmd\n", + case VOC_GENERIC_SET_PARAM_TOKEN: + pr_debug("%s: VSS_ICOMMON_CMD_SET_PARAM_V2 called\n", __func__); v->cvp_state = CMD_STATUS_SUCCESS; v->async_err = ptr[1]; @@ -8566,7 +8902,8 @@ static int __init voice_init(void) 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.cvp_version = 0; + common.is_avcs_version_queried = false; /* Initialize EC Ref media format info */ common.ec_ref_ext = false; common.ec_media_fmt_info.port_id = AFE_PORT_INVALID; diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index 7fb2256bff8b..66d6e475e896 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -8537,6 +8537,8 @@ struct asm_eq_params { #define VSS_ICOMMON_CMD_GET_PARAM_V2 0x0001133E #define VSS_ICOMMON_RSP_GET_PARAM 0x00011008 +#define VSS_MAX_AVCS_NUM_SERVICES 25 + /* ID of the Bass Boost module. * This module supports the following parameter IDs: * - #AUDPROC_PARAM_ID_BASS_BOOST_ENABLE @@ -9197,6 +9199,74 @@ struct asm_aptx_dec_fmt_blk_v2 { */ } __packed; +/* Q6Core Specific */ +#define AVCS_CMD_GET_FWK_VERSION (0x0001292C) +#define AVCS_CMDRSP_GET_FWK_VERSION (0x0001292D) + +#define AVCS_SERVICE_ID_ALL (0xFFFFFFFF) +#define APRV2_IDS_SERVICE_ID_ADSP_CVP_V (0xB) + +struct avcs_get_fwk_version { + /* + * Indicates the major version of the AVS build. + * This value is incremented on chipset family boundaries. + */ + uint32_t build_major_version; + + /* + * Minor version of the AVS build. + * This value represents the mainline to which the AVS build belongs. + */ + uint32_t build_minor_version; + + /* Indicates the AVS branch version to which the image belongs. */ + uint32_t build_branch_version; + + /* Indicates the AVS sub-branch or customer product line information. */ + uint32_t build_subbranch_version; + + /* Number of supported AVS services in the current build. */ + uint32_t num_services; +}; + +struct avs_svc_api_info { + /* + * APRV2 service IDs for the individual static services. + * + * @values + * - APRV2_IDS_SERVICE_ID_ADSP_CORE_V + * - APRV2_IDS_SERVICE_ID_ADSP_AFE_V + * - APRV2_IDS_SERVICE_ID_ADSP_ASM_V + * - APRV2_IDS_SERVICE_ID_ADSP_ADM_V + * - APRV2_IDS_SERVICE_ID_ADSP_MVM_V + * - APRV2_IDS_SERVICE_ID_ADSP_CVS_V + * - APRV2_IDS_SERVICE_ID_ADSP_CVP_V + * - APRV2_IDS_SERVICE_ID_ADSP_LSM_V + */ + uint32_t service_id; + + /* + * Indicates the API version of the service. + * + * Each new API update that warrants a change on the HLOS side triggers + * an increment in the version. + */ + uint32_t api_version; + + /* + * Indicates the API increments on a sub-branch (not on the mainline). + * + * API branch version numbers can increment independently on different + * sub-branches. + */ + uint32_t api_branch_version; +}; + +struct avcs_fwk_ver_info { + struct avcs_get_fwk_version avcs_fwk_version; + struct avs_svc_api_info *services; +} __packed; + /* LSM Specific */ #define VW_FEAT_DIM (39) diff --git a/include/dsp/q6core.h b/include/dsp/q6core.h index c0327ae1f77e..1f1adad031e3 100644 --- a/include/dsp/q6core.h +++ b/include/dsp/q6core.h @@ -13,6 +13,7 @@ #ifndef __Q6CORE_H__ #define __Q6CORE_H__ #include +#include @@ -21,6 +22,11 @@ bool q6core_is_adsp_ready(void); +int q6core_get_service_version(uint32_t service_id, + struct avcs_fwk_ver_info *ver_info, + size_t size); +size_t q6core_get_avcs_service_size(uint32_t service_id); + #define ADSP_CMD_SET_DTS_EAGLE_DATA_ID 0x00012919 #define DTS_EAGLE_LICENSE_ID 0x00028346 struct adsp_dts_eagle { diff --git a/include/dsp/q6voice.h b/include/dsp/q6voice.h index 56326b174b38..f6f0d8c80591 100644 --- a/include/dsp/q6voice.h +++ b/include/dsp/q6voice.h @@ -124,7 +124,7 @@ struct media_format_info { }; enum { - VOC_NO_SET_PARAM_TOKEN = 0, + VOC_GENERIC_SET_PARAM_TOKEN = 0, VOC_RTAC_SET_PARAM_TOKEN, VOC_SET_MEDIA_FORMAT_PARAM_TOKEN, VOC_SET_PARAM_TOKEN_MAX @@ -239,6 +239,19 @@ struct vss_param_endpoint_media_format_info_t { uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; } __packed; +struct vss_param_vocproc_dev_channel_info_t { + uint32_t num_channels; + uint32_t bits_per_sample; + uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; +} __packed; + +struct vss_param_mfc_config_info_t { + uint32_t sample_rate; + uint16_t bits_per_sample; + uint16_t num_channels; + uint16_t channel_type[VSS_NUM_CHANNELS_MAX]; +} __packed; + struct vss_icommon_param_data_t { /* Valid ID of the module. */ uint32_t module_id; @@ -260,6 +273,88 @@ struct vss_icommon_param_data_t { }; } __packed; +struct vss_icommon_param_data_channel_info_v2_t { + /* Valid ID of the module. */ + uint32_t module_id; + /* Valid ID of the parameter. */ + uint32_t param_id; + /* + * Data size of the structure relating to the param_id/module_id + * combination in uint8_t bytes. + */ + uint16_t param_size; + /* This field must be set to zero. */ + uint16_t reserved; + struct vss_param_vocproc_dev_channel_info_t channel_info; +} __packed; + +struct vss_icommon_cmd_set_param_channel_info_v2_t { + /* + * Pointer to the unique identifier for an address (physical/virtual). + * + * If the parameter data payload is within the message payload + * (in-band), set this field to 0. The parameter data begins at the + * specified data payload address. + * + * If the parameter data is out-of-band, this field is the handle to + * the physical address in the shared memory that holds the parameter + * data. + */ + uint32_t mem_handle; + /* + * Location of the parameter data payload. + * + * The payload is an array of vss_icommon_param_data_t. If the + * mem_handle is 0, this field is ignored. + */ + uint64_t mem_address; + /* Size of the parameter data payload in bytes. */ + uint32_t mem_size; + struct vss_icommon_param_data_channel_info_v2_t param_data; +} __packed; + +struct vss_icommon_param_data_mfc_config_v2_t { + /* Valid ID of the module. */ + uint32_t module_id; + /* Valid ID of the parameter. */ + uint32_t param_id; + /* + * Data size of the structure relating to the param_id/module_id + * combination in uint8_t bytes. + */ + uint16_t param_size; + /* This field must be set to zero. */ + uint16_t reserved; + struct vss_param_mfc_config_info_t mfc_config_info; +} __packed; + +struct vss_icommon_cmd_set_param_mfc_config_v2_t { + /* + * Pointer to the unique identifier for an address (physical/virtual). + * + * If the parameter data payload is within the message payload + * (in-band), set this field to 0. The parameter data begins at the + * specified data payload address. + * + * If the parameter data is out-of-band, this field is the handle to + * the physical address in the shared memory that holds the parameter + * data. + */ + + uint32_t mem_handle; + /* + * Location of the parameter data payload. + * + * The payload is an array of vss_icommon_param_data_t. If the + * mem_handle is 0, this field is ignored. + */ + uint64_t mem_address; + /* Size of the parameter data payload in bytes. */ + uint32_t mem_size; + + struct vss_icommon_param_data_mfc_config_v2_t param_data; +} __packed; + /* Payload structure for the VSS_ICOMMON_CMD_SET_PARAM_V2 command. */ struct vss_icommon_cmd_set_param_v2_t { /* @@ -674,6 +769,12 @@ struct vss_imemory_cmd_unmap_t { #define VSS_IRECORD_MODE_TX_RX_MIXING 0x00010F7B /* Select mixed Tx and Rx paths. */ +#define VSS_PARAM_VOCPROC_TX_CHANNEL_INFO 0x0001328E + +#define VSS_PARAM_VOCPROC_RX_CHANNEL_INFO 0x0001328F + +#define VSS_PARAM_VOCPROC_EC_REF_CHANNEL_INFO 0x00013290 + #define VSS_PARAM_TX_PORT_ENDPOINT_MEDIA_INFO 0x00013253 #define VSS_PARAM_RX_PORT_ENDPOINT_MEDIA_INFO 0x00013254 @@ -1485,7 +1586,18 @@ struct cvp_set_dev_channels_cmd { struct cvp_set_media_format_cmd { struct apr_hdr hdr; - struct vss_icommon_cmd_set_param_v2_t cvp_set_param_v2; + struct vss_icommon_cmd_set_param_v2_t cvp_set_media_param_v2; +} __packed; + +struct cvp_set_channel_info_cmd_v2 { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_param_channel_info_v2_t + cvp_set_ch_info_param_v2; +} __packed; + +struct cvp_set_mfc_config_cmd_v2 { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_param_mfc_config_v2_t cvp_set_mfc_param_v2; } __packed; struct cvp_set_vp3_data_cmd { @@ -1756,6 +1868,8 @@ struct common_data { bool srvcc_rec_flag; bool is_destroy_cvd; char cvd_version[CVD_VERSION_STRING_MAX_SIZE]; + int cvp_version; + bool is_avcs_version_queried; bool is_per_vocoder_cal_enabled; bool is_sound_focus_resp_success; bool is_source_tracking_resp_success; diff --git a/soc/pinctrl-lpi.c b/soc/pinctrl-lpi.c index db5a9e5a4d02..1e7ead817399 100644 --- a/soc/pinctrl-lpi.c +++ b/soc/pinctrl-lpi.c @@ -26,7 +26,7 @@ #include "core.h" #include "pinctrl-utils.h" -#define LPI_ADDRESS_SIZE 0xC000 +#define LPI_ADDRESS_SIZE 0x20000 #define LPI_GPIO_REG_VAL_CTL 0x00 #define LPI_GPIO_REG_DIR_CTL 0x04 @@ -109,35 +109,35 @@ static const u32 lpi_offset[] = { 0x00000000, 0x00001000, 0x00002000, - 0x00002010, 0x00003000, - 0x00003010, 0x00004000, - 0x00004010, 0x00005000, - 0x00005010, - 0x00005020, - 0x00005030, 0x00006000, - 0x00006010, 0x00007000, - 0x00007010, - 0x00005040, - 0x00005050, 0x00008000, - 0x00008010, - 0x00008020, - 0x00008030, - 0x00008040, - 0x00008050, - 0x00008060, - 0x00008070, 0x00009000, - 0x00009010, 0x0000A000, - 0x0000A010, 0x0000B000, - 0x0000B010, + 0x0000C000, + 0x0000D000, + 0x0000E000, + 0x0000F000, + 0x00010000, + 0x00011000, + 0x00012000, + 0x00013000, + 0x00014000, + 0x00015000, + 0x00016000, + 0x00017000, + 0x00018000, + 0x00019000, + 0x0001A000, + 0x0001B000, + 0x0001C000, + 0x0001D000, + 0x0001E000, + 0x0001F000, }; static const char *const lpi_gpio_functions[] = { From 13a66c87f44511e203182c81c993f036a4c799be Mon Sep 17 00:00:00 2001 From: Asish Bhattacharya Date: Fri, 1 Sep 2017 00:04:39 +0530 Subject: [PATCH 013/276] autoconf: sdm845: add conf to enable display port Add audio configurations to enable audio functionality over DisplayPort on sdm845. Change-Id: If8b5f6c25bca08a61b951cb97167e9ead7271dd3 Signed-off-by: Asish Bhattacharya --- config/sdm845auto.conf | 1 + config/sdm845autoconf.h | 1 + 2 files changed, 2 insertions(+) diff --git a/config/sdm845auto.conf b/config/sdm845auto.conf index 97f2127ce606..64ca29f9da4a 100644 --- a/config/sdm845auto.conf +++ b/config/sdm845auto.conf @@ -39,3 +39,4 @@ CONFIG_SND_SOC_MACHINE_SDM845=y CONFIG_SND_SOC_MSM_STUB=y CONFIG_WCD_DSP_GLINK=y CONFIG_MSM_AVTIMER=y +CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y diff --git a/config/sdm845autoconf.h b/config/sdm845autoconf.h index ea14533dece3..5bd70f614fa6 100644 --- a/config/sdm845autoconf.h +++ b/config/sdm845autoconf.h @@ -52,3 +52,4 @@ #define CONFIG_SND_SOC_MSM_STUB 1 #define CONFIG_WCD_DSP_GLINK 1 #define CONFIG_MSM_AVTIMER 1 +#define CONFIG_SND_SOC_MSM_HDMI_CODEC_RX 1 From 24d1c0878add199218982e806ffdbfb24ad8485a Mon Sep 17 00:00:00 2001 From: Shiv Maliyappanahalli Date: Thu, 31 Aug 2017 15:26:09 -0700 Subject: [PATCH 014/276] asoc: codecs: add object to compile audio hdmi codec driver HDMI codec driver is not compiling as the corresponding object file is not set in the makefile. Add object file to SND_SOC_MSM_HDMI_CODEC_RX config. Change-Id: I6068f551db357e5bfa73ff5b51014347f38b925c Signed-off-by: Shiv Maliyappanahalli --- asoc/codecs/Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/asoc/codecs/Makefile b/asoc/codecs/Makefile index bb3a9356e867..7c55c59b7875 100644 --- a/asoc/codecs/Makefile +++ b/asoc/codecs/Makefile @@ -42,3 +42,4 @@ wcd-core-objs := wcd9xxx-rst.o wcd9xxx-core-init.o \ wcd9335-regmap.o wcd9335-tables.o \ msm-cdc-pinctrl.o msm-cdc-supply.o obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd-core.o +obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) := msm_hdmi_codec_rx.o From 8b3bdea209db74decf81b861d005fa814514b45b Mon Sep 17 00:00:00 2001 From: Shiv Maliyappanahalli Date: Thu, 31 Aug 2017 18:02:20 -0700 Subject: [PATCH 015/276] asoc: codecs: fix compilation error for wcd codec The previous commit fb24ad8485a assigns obj-y with msm_hdmi_codec_rx.o instead of appending, thus giving compilation error as previously assigned object files are removed. Fix by appending obj-y with msm_hdmi_codec_rx.o instead of assigning. Change-Id: I7c5ea5627e2e1888e3fc0b53a6650ab64b9a29a8 Signed-off-by: Shiv Maliyappanahalli --- asoc/codecs/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/codecs/Makefile b/asoc/codecs/Makefile index 7c55c59b7875..f0477f7eeb54 100644 --- a/asoc/codecs/Makefile +++ b/asoc/codecs/Makefile @@ -42,4 +42,4 @@ wcd-core-objs := wcd9xxx-rst.o wcd9xxx-core-init.o \ wcd9335-regmap.o wcd9335-tables.o \ msm-cdc-pinctrl.o msm-cdc-supply.o obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd-core.o -obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) := msm_hdmi_codec_rx.o +obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) += msm_hdmi_codec_rx.o From 8f7ccc2e6f1faf10b4d948c20f86c0e847050912 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Mon, 28 Aug 2017 17:35:04 +0530 Subject: [PATCH 016/276] audio-lnx: Add latest snapshot for audio drivers. Propagate the changes based on latest snapshot for audio kernel source tree at below cutoff of kernel msm-4.9 - (040750bfa78 - "Revert "ARM: dts: msm: Add DT node for aop-qmp clock controller on SDM845 v2"") CRs-Fixed: 2104096 Change-Id: I0927c40b3a188dbf892e7ec4c1c7810953724929 Signed-off-by: Laxminath Kasam --- asoc/codecs/msm-cdc-pinctrl.c | 10 ++++- asoc/codecs/wcd-spi.c | 4 ++ asoc/codecs/wcd9335.c | 2 +- asoc/codecs/wcd934x/wcd934x-mbhc.c | 2 +- asoc/codecs/wcd934x/wcd934x.c | 40 ++++++++++++++++-- asoc/codecs/wcd9xxx-core.c | 1 + asoc/codecs/wcd9xxx-irq.c | 27 +++++++++++- asoc/msm-compress-q6-v2.c | 20 +++++++++ asoc/msm-dai-q6-v2.c | 1 - asoc/msm-qti-pp-config.c | 41 ++++++++++++++++++ asoc/msm-qti-pp-config.h | 1 + asoc/sdm660-common.c | 6 +-- asoc/sdm660-common.h | 9 ---- asoc/sdm660-external.c | 68 ------------------------------ asoc/sdm660-external.h | 4 -- asoc/sdm845.c | 1 + dsp/audio_notifier.c | 2 +- dsp/codecs/audio_utils_aio.c | 2 + dsp/q6adm.c | 2 +- dsp/q6core.c | 2 +- dsp/q6voice.c | 2 +- include/dsp/apr_audio-v2.h | 2 +- include/dsp/q6voice.h | 2 +- ipc/apr.c | 1 + ipc/apr_tal_glink.c | 2 +- soc/soundwire.c | 11 +++-- soc/swr-wcd-ctrl.c | 24 ++++++++--- soc/swr-wcd-ctrl.h | 1 + 28 files changed, 177 insertions(+), 113 deletions(-) diff --git a/asoc/codecs/msm-cdc-pinctrl.c b/asoc/codecs/msm-cdc-pinctrl.c index c4d0ec19931d..79e322f1a277 100644 --- a/asoc/codecs/msm-cdc-pinctrl.c +++ b/asoc/codecs/msm-cdc-pinctrl.c @@ -217,8 +217,14 @@ static int msm_cdc_pinctrl_remove(struct platform_device *pdev) gpio_data = dev_get_drvdata(&pdev->dev); - if (gpio_data && gpio_data->pinctrl) - devm_pinctrl_put(gpio_data->pinctrl); + /* to free the requested gpio before exiting */ + if (gpio_data) { + if (gpio_is_valid(gpio_data->gpio)) + gpio_free(gpio_data->gpio); + + if (gpio_data->pinctrl) + devm_pinctrl_put(gpio_data->pinctrl); + } devm_kfree(&pdev->dev, gpio_data); diff --git a/asoc/codecs/wcd-spi.c b/asoc/codecs/wcd-spi.c index 957d6428427c..67cb46c74925 100644 --- a/asoc/codecs/wcd-spi.c +++ b/asoc/codecs/wcd-spi.c @@ -1374,6 +1374,10 @@ static void wcd_spi_component_unbind(struct device *dev, { struct spi_device *spi = to_spi_device(dev); struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data; + + debugfs_remove_recursive(dbg_data->dir); + dbg_data->dir = NULL; wcd_spi->m_dev = NULL; wcd_spi->m_ops = NULL; diff --git a/asoc/codecs/wcd9335.c b/asoc/codecs/wcd9335.c index 2f877160bf66..21de9f5f6d6f 100644 --- a/asoc/codecs/wcd9335.c +++ b/asoc/codecs/wcd9335.c @@ -112,7 +112,7 @@ /* Convert from vout ctl to micbias voltage in mV */ #define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50) -#define TASHA_ZDET_NUM_MEASUREMENTS 150 +#define TASHA_ZDET_NUM_MEASUREMENTS 900 #define TASHA_MBHC_GET_C1(c) ((c & 0xC000) >> 14) #define TASHA_MBHC_GET_X1(x) (x & 0x3FFF) /* z value compared in milliOhm */ diff --git a/asoc/codecs/wcd934x/wcd934x-mbhc.c b/asoc/codecs/wcd934x/wcd934x-mbhc.c index 9595ba8e5c9c..4eb14de31075 100644 --- a/asoc/codecs/wcd934x/wcd934x-mbhc.c +++ b/asoc/codecs/wcd934x/wcd934x-mbhc.c @@ -44,7 +44,7 @@ /* Z floating defined in ohms */ #define TAVIL_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE -#define TAVIL_ZDET_NUM_MEASUREMENTS 150 +#define TAVIL_ZDET_NUM_MEASUREMENTS 900 #define TAVIL_MBHC_GET_C1(c) ((c & 0xC000) >> 14) #define TAVIL_MBHC_GET_X1(x) (x & 0x3FFF) /* Z value compared in milliOhm */ diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index ce0141f9f43b..66c7830e2a3f 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -128,6 +128,8 @@ static const struct snd_kcontrol_new name##_mux = \ #define WCD934X_DIG_CORE_REG_MIN WCD934X_CDC_ANC0_CLK_RESET_CTL #define WCD934X_DIG_CORE_REG_MAX 0xFFF +#define WCD934X_CHILD_DEVICES_MAX 6 + #define WCD934X_MAX_MICBIAS 4 #define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone" #define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone" @@ -626,6 +628,11 @@ struct tavil_priv { int power_active_ref; int sidetone_coeff_array[IIR_MAX][BAND_MAX] [WCD934X_CDC_SIDETONE_IIR_COEFF_MAX]; + + struct spi_device *spi; + struct platform_device *pdev_child_devices + [WCD934X_CHILD_DEVICES_MAX]; + int child_count; }; static const struct tavil_reg_mask_val tavil_spkr_default[] = { @@ -5105,6 +5112,13 @@ static void tavil_restore_iir_coeff(struct tavil_priv *tavil, int iir_idx) int band_idx = 0, coeff_idx = 0; struct snd_soc_codec *codec = tavil->codec; + /* + * snd_soc_write call crashes at rmmod if there is no machine + * driver and hence no codec pointer available + */ + if (!codec) + return; + for (band_idx = 0; band_idx < BAND_MAX; band_idx++) { snd_soc_write(codec, (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), @@ -9397,6 +9411,9 @@ static int tavil_soc_codec_remove(struct snd_soc_codec *codec) control = dev_get_drvdata(codec->dev->parent); devm_kfree(codec->dev, control->rx_chs); + /* slimslave deinit in wcd core looks for this value */ + control->num_rx_port = 0; + control->num_tx_port = 0; control->rx_chs = NULL; control->tx_chs = NULL; tavil_cleanup_irqs(tavil); @@ -9736,6 +9753,7 @@ static void tavil_codec_add_spi_device(struct tavil_priv *tavil, goto err_dt_parse; } + tavil->spi = spi; /* Put the reference to SPI master */ put_device(&master->dev); @@ -9782,6 +9800,7 @@ static void tavil_add_child_devices(struct work_struct *work) } platdata = &tavil->swr.plat_data; + tavil->child_count = 0; for_each_child_of_node(wcd9xxx->dev->of_node, node) { @@ -9849,6 +9868,10 @@ static void tavil_add_child_devices(struct work_struct *work) __func__); tavil->swr.ctrl_data = swr_ctrl_data; } + if (tavil->child_count < WCD934X_CHILD_DEVICES_MAX) + tavil->pdev_child_devices[tavil->child_count++] = pdev; + else + goto err_mem; } return; @@ -10076,11 +10099,24 @@ static int tavil_probe(struct platform_device *pdev) static int tavil_remove(struct platform_device *pdev) { struct tavil_priv *tavil; + int count = 0; tavil = platform_get_drvdata(pdev); if (!tavil) return -EINVAL; + /* do dsd deinit before codec->component->regmap becomes freed */ + if (tavil->dsd_config) { + tavil_dsd_deinit(tavil->dsd_config); + tavil->dsd_config = NULL; + } + + if (tavil->spi) + spi_unregister_device(tavil->spi); + for (count = 0; count < tavil->child_count && + count < WCD934X_CHILD_DEVICES_MAX; count++) + platform_device_unregister(tavil->pdev_child_devices[count]); + mutex_destroy(&tavil->micb_lock); mutex_destroy(&tavil->svs_mutex); mutex_destroy(&tavil->codec_mutex); @@ -10091,10 +10127,6 @@ static int tavil_remove(struct platform_device *pdev) snd_soc_unregister_codec(&pdev->dev); clk_put(tavil->wcd_ext_clk); wcd_resmgr_remove(tavil->resmgr); - if (tavil->dsd_config) { - tavil_dsd_deinit(tavil->dsd_config); - tavil->dsd_config = NULL; - } devm_kfree(&pdev->dev, tavil); return 0; } diff --git a/asoc/codecs/wcd9xxx-core.c b/asoc/codecs/wcd9xxx-core.c index 81408e8928ef..7f74aef8f0c0 100644 --- a/asoc/codecs/wcd9xxx-core.c +++ b/asoc/codecs/wcd9xxx-core.c @@ -592,6 +592,7 @@ static void wcd9xxx_device_exit(struct wcd9xxx *wcd9xxx) { device_init_wakeup(wcd9xxx->dev, false); wcd9xxx_irq_exit(&wcd9xxx->core_res); + mfd_remove_devices(wcd9xxx->dev); wcd9xxx_bringdown(wcd9xxx->dev); wcd9xxx_reset_low(wcd9xxx->dev); wcd9xxx_core_res_deinit(&wcd9xxx->core_res); diff --git a/asoc/codecs/wcd9xxx-irq.c b/asoc/codecs/wcd9xxx-irq.c index 65100c983f06..b192e992d1fa 100644 --- a/asoc/codecs/wcd9xxx-irq.c +++ b/asoc/codecs/wcd9xxx-irq.c @@ -52,6 +52,8 @@ static int phyirq_to_virq( struct wcd9xxx_core_resource *wcd9xxx_res, int irq); static unsigned int wcd9xxx_irq_get_upstream_irq( struct wcd9xxx_core_resource *wcd9xxx_res); +static void wcd9xxx_irq_put_downstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res); static void wcd9xxx_irq_put_upstream_irq( struct wcd9xxx_core_resource *wcd9xxx_res); static int wcd9xxx_map_irq( @@ -632,6 +634,7 @@ void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res) disable_irq_wake(wcd9xxx_res->irq); free_irq(wcd9xxx_res->irq, wcd9xxx_res); wcd9xxx_res->irq = 0; + wcd9xxx_irq_put_downstream_irq(wcd9xxx_res); wcd9xxx_irq_put_upstream_irq(wcd9xxx_res); } mutex_destroy(&wcd9xxx_res->irq_lock); @@ -754,6 +757,29 @@ static unsigned int wcd9xxx_irq_get_upstream_irq( return data->irq; } +static void wcd9xxx_irq_put_downstream_irq( + struct wcd9xxx_core_resource *wcd9xxx_res) +{ + int irq, virq, ret; + + /* + * IRQ migration hits error if the chip data and handles + * are not made NULL. make associated data and handles + * to NULL at irq_exit + */ + for (irq = 0; irq < wcd9xxx_res->num_irqs; irq++) { + virq = wcd9xxx_map_irq(wcd9xxx_res, irq); + pr_debug("%s: irq %d -> %d\n", __func__, irq, virq); + ret = irq_set_chip_data(virq, NULL); + if (ret) { + pr_err("%s: Failed to configure irq %d (%d)\n", + __func__, irq, ret); + return; + } + irq_set_chip_and_handler(virq, NULL, NULL); + } +} + static void wcd9xxx_irq_put_upstream_irq( struct wcd9xxx_core_resource *wcd9xxx_res) { @@ -821,7 +847,6 @@ static int wcd9xxx_irq_remove(struct platform_device *pdev) wmb(); irq_domain_remove(data->domain); kfree(data); - domain->host_data = NULL; return 0; } diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 89efb904ae3f..a9f28ca8ef89 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -1281,6 +1281,9 @@ static int msm_compr_configure_dsp_for_playback .step = SOFT_VOLUME_STEP, .rampingcurve = SOFT_VOLUME_CURVE_LINEAR, }; + struct snd_kcontrol *kctl; + struct snd_ctl_elem_value kctl_elem_value; + uint16_t target_asm_bit_width = 0; pr_debug("%s: stream_id %d\n", __func__, ac->stream_id); stream_index = STREAM_ARRAY_INDEX(ac->stream_id); @@ -1289,6 +1292,23 @@ static int msm_compr_configure_dsp_for_playback return -EINVAL; } + kctl = snd_soc_card_get_kcontrol(soc_prtd->card, + DSP_BIT_WIDTH_MIXER_CTL); + if (kctl) { + kctl->get(kctl, &kctl_elem_value); + target_asm_bit_width = kctl_elem_value.value.integer.value[0]; + if (target_asm_bit_width > 0) { + pr_debug("%s enforce ASM bitwidth to %d from %d\n", + __func__, + target_asm_bit_width, + bits_per_sample); + bits_per_sample = target_asm_bit_width; + } + } else { + pr_info("%s: failed to get mixer ctl for %s.\n", + __func__, DSP_BIT_WIDTH_MIXER_CTL); + } + 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; diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 583934ddf6f1..6b620a5c8ed0 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -3632,7 +3632,6 @@ static int msm_dai_q6_dai_mi2s_remove(struct snd_soc_dai *dai) clear_bit(STATUS_PORT_STARTED, mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask); } - kfree(mi2s_dai_data); return 0; } diff --git a/asoc/msm-qti-pp-config.c b/asoc/msm-qti-pp-config.c index 2064056ab350..0d1018236aec 100644 --- a/asoc/msm-qti-pp-config.c +++ b/asoc/msm-qti-pp-config.c @@ -392,6 +392,7 @@ static int msm_afe_tert_mi2s_lb_vol_ctrl; static int msm_afe_quat_mi2s_lb_vol_ctrl; static int msm_afe_slimbus_7_lb_vol_ctrl; static int msm_afe_slimbus_8_lb_vol_ctrl; +static int msm_asm_bit_width; 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); @@ -412,6 +413,38 @@ static int msm_qti_pp_set_fm_vol_mixer(struct snd_kcontrol *kcontrol, return 0; } +static int msm_asm_bit_width_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s get ASM bitwidth = %d\n", + __func__, msm_asm_bit_width); + + ucontrol->value.integer.value[0] = msm_asm_bit_width; + + return 0; +} + +static int msm_asm_bit_width_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 16: + msm_asm_bit_width = 16; + break; + case 24: + msm_asm_bit_width = 24; + break; + case 32: + msm_asm_bit_width = 32; + break; + default: + msm_asm_bit_width = 0; + break; + } + + return 0; +} + static int msm_qti_pp_get_pri_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1132,6 +1165,11 @@ static const struct snd_kcontrol_new int_fm_vol_mixer_controls[] = { msm_qti_pp_set_quat_mi2s_fm_vol_mixer, fm_rx_vol_gain), }; +static const struct snd_kcontrol_new dsp_bit_width_controls[] = { + SOC_SINGLE_EXT(DSP_BIT_WIDTH_MIXER_CTL, SND_SOC_NOPM, 0, 0x20, + 0, msm_asm_bit_width_get, msm_asm_bit_width_put), +}; + 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, @@ -1403,5 +1441,8 @@ void msm_qti_pp_add_controls(struct snd_soc_platform *platform) snd_soc_add_platform_controls(platform, msm_multichannel_ec_controls, ARRAY_SIZE(msm_multichannel_ec_controls)); + + snd_soc_add_platform_controls(platform, dsp_bit_width_controls, + ARRAY_SIZE(dsp_bit_width_controls)); } #endif /* CONFIG_QTI_PP */ diff --git a/asoc/msm-qti-pp-config.h b/asoc/msm-qti-pp-config.h index 14e9b10d0061..ade2f5e507ec 100644 --- a/asoc/msm-qti-pp-config.h +++ b/asoc/msm-qti-pp-config.h @@ -14,6 +14,7 @@ #define _MSM_QTI_PP_H_ #include +#define DSP_BIT_WIDTH_MIXER_CTL "ASM Bit Width" int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, uint32_t *payload); int msm_adsp_init_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd); diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c index f73e1cdd8f60..8006472ff338 100644 --- a/asoc/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -3186,8 +3186,6 @@ static int msm_asoc_machine_probe(struct platform_device *pdev) gpio_free(pdata->hph_en0_gpio); pdata->hph_en0_gpio = 0; } - if (pdata->snd_card_val != INT_SND_CARD) - msm_ext_cdc_deinit(pdata); devm_kfree(&pdev->dev, pdata); return ret; } @@ -3199,10 +3197,8 @@ static int msm_asoc_machine_remove(struct platform_device *pdev) if (pdata->snd_card_val == INT_SND_CARD) mutex_destroy(&pdata->cdc_int_mclk0_mutex); - else - msm_ext_cdc_deinit(pdata); - msm_free_auxdev_mem(pdev); + msm_free_auxdev_mem(pdev); gpio_free(pdata->us_euro_gpio); gpio_free(pdata->hph_en1_gpio); gpio_free(pdata->hph_en0_gpio); diff --git a/asoc/sdm660-common.h b/asoc/sdm660-common.h index 0544b968d159..c7de18f7f4db 100644 --- a/asoc/sdm660-common.h +++ b/asoc/sdm660-common.h @@ -78,14 +78,6 @@ enum { EXT_SND_CARD_TAVIL, }; -struct msm_snd_interrupt { - void __iomem *mpm_wakeup; - void __iomem *intr1_cfg_apps; - void __iomem *lpi_gpio_intr_cfg; - void __iomem *lpi_gpio_cfg; - void __iomem *lpi_gpio_inout; -}; - struct msm_asoc_mach_data { int us_euro_gpio; /* used by gpio driver API */ int hph_en1_gpio; @@ -112,7 +104,6 @@ struct msm_asoc_mach_data { struct mutex cdc_int_mclk0_mutex; struct delayed_work disable_int_mclk0_work; struct afe_clk_set digital_cdc_core_clk; - struct msm_snd_interrupt msm_snd_intr_lpi; }; int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, diff --git a/asoc/sdm660-external.c b/asoc/sdm660-external.c index dd8983c973e4..0358ad8ad9b4 100644 --- a/asoc/sdm660-external.c +++ b/asoc/sdm660-external.c @@ -35,22 +35,6 @@ #define CODEC_EXT_CLK_RATE 9600000 #define ADSP_STATE_READY_TIMEOUT_MS 3000 -#define TLMM_CENTER_MPM_WAKEUP_INT_EN_0 0x03596000 -#define LPI_GPIO_22_WAKEUP_VAL 0x00000002 - -#define TLMM_LPI_DIR_CONN_INTR1_CFG_APPS 0x0359D004 -#define LPI_GPIO_22_INTR1_CFG_VAL 0x01 -#define LPI_GPIO_22_INTR1_CFG_MASK 0x03 - -#define TLMM_LPI_GPIO_INTR_CFG1 0x0359B004 -#define LPI_GPIO_INTR_CFG1_VAL 0x00000113 - -#define TLMM_LPI_GPIO22_CFG 0x15078040 -#define LPI_GPIO22_CFG_VAL 0x0000009 - -#define TLMM_LPI_GPIO22_INOUT 0x179D1318 -#define LPI_GPIO22_INOUT_VAL 0x0020000 - #define WSA8810_NAME_1 "wsa881x.20170211" #define WSA8810_NAME_2 "wsa881x.20170212" @@ -1209,28 +1193,6 @@ static void msm_afe_clear_config(void) afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); } -static void msm_snd_interrupt_config(struct msm_asoc_mach_data *pdata) -{ - int val; - - val = ioread32(pdata->msm_snd_intr_lpi.mpm_wakeup); - val |= LPI_GPIO_22_WAKEUP_VAL; - iowrite32(val, pdata->msm_snd_intr_lpi.mpm_wakeup); - - val = ioread32(pdata->msm_snd_intr_lpi.intr1_cfg_apps); - val &= ~(LPI_GPIO_22_INTR1_CFG_MASK); - val |= LPI_GPIO_22_INTR1_CFG_VAL; - iowrite32(val, pdata->msm_snd_intr_lpi.intr1_cfg_apps); - - iowrite32(LPI_GPIO_INTR_CFG1_VAL, - pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg); - iowrite32(LPI_GPIO22_CFG_VAL, - pdata->msm_snd_intr_lpi.lpi_gpio_cfg); - val = ioread32(pdata->msm_snd_intr_lpi.lpi_gpio_inout); - val |= LPI_GPIO22_INOUT_VAL; - iowrite32(val, pdata->msm_snd_intr_lpi.lpi_gpio_inout); -} - static int msm_adsp_power_up_config(struct snd_soc_codec *codec) { int ret = 0; @@ -1262,7 +1224,6 @@ static int msm_adsp_power_up_config(struct snd_soc_codec *codec) ret = -ETIMEDOUT; goto err_fail; } - msm_snd_interrupt_config(pdata); ret = msm_afe_set_config(codec); if (ret) @@ -1883,36 +1844,7 @@ int msm_ext_cdc_init(struct platform_device *pdev, ret); ret = 0; } - pdata->msm_snd_intr_lpi.mpm_wakeup = - ioremap(TLMM_CENTER_MPM_WAKEUP_INT_EN_0, 4); - pdata->msm_snd_intr_lpi.intr1_cfg_apps = - ioremap(TLMM_LPI_DIR_CONN_INTR1_CFG_APPS, 4); - pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg = - ioremap(TLMM_LPI_GPIO_INTR_CFG1, 4); - pdata->msm_snd_intr_lpi.lpi_gpio_cfg = - ioremap(TLMM_LPI_GPIO22_CFG, 4); - pdata->msm_snd_intr_lpi.lpi_gpio_inout = - ioremap(TLMM_LPI_GPIO22_INOUT, 4); err: return ret; } EXPORT_SYMBOL(msm_ext_cdc_init); - -/** - * msm_ext_cdc_deinit - external codec machine specific deinit. - */ -void msm_ext_cdc_deinit(struct msm_asoc_mach_data *pdata) -{ - if (pdata->msm_snd_intr_lpi.mpm_wakeup) - iounmap(pdata->msm_snd_intr_lpi.mpm_wakeup); - if (pdata->msm_snd_intr_lpi.intr1_cfg_apps) - iounmap(pdata->msm_snd_intr_lpi.intr1_cfg_apps); - if (pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg) - iounmap(pdata->msm_snd_intr_lpi.lpi_gpio_intr_cfg); - if (pdata->msm_snd_intr_lpi.lpi_gpio_cfg) - iounmap(pdata->msm_snd_intr_lpi.lpi_gpio_cfg); - if (pdata->msm_snd_intr_lpi.lpi_gpio_inout) - iounmap(pdata->msm_snd_intr_lpi.lpi_gpio_inout); - -} -EXPORT_SYMBOL(msm_ext_cdc_deinit); diff --git a/asoc/sdm660-external.h b/asoc/sdm660-external.h index d53e7c7d9518..0648c014b6d2 100644 --- a/asoc/sdm660-external.h +++ b/asoc/sdm660-external.h @@ -36,7 +36,6 @@ int msm_snd_card_tasha_late_probe(struct snd_soc_card *card); 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(struct platform_device *pdev); -void msm_ext_cdc_deinit(struct msm_asoc_mach_data *pdata); #else inline int msm_ext_cdc_init(struct platform_device *pdev, struct msm_asoc_mach_data *pdata, @@ -49,8 +48,5 @@ inline int msm_ext_cdc_init(struct platform_device *pdev, inline void msm_ext_register_audio_notifier(struct platform_device *pdev) { } -inline void msm_ext_cdc_deinit(void) -{ -} #endif #endif diff --git a/asoc/sdm845.c b/asoc/sdm845.c index 7f29450062a9..a3654d8ecdb2 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -7149,6 +7149,7 @@ static int msm_asoc_machine_remove(struct platform_device *pdev) msm_release_pinctrl(pdev); snd_soc_unregister_card(card); + audio_notifier_deregister("sdm845"); return 0; } diff --git a/dsp/audio_notifier.c b/dsp/audio_notifier.c index bcb5f9d98378..a1b5d1e6f8bd 100644 --- a/dsp/audio_notifier.c +++ b/dsp/audio_notifier.c @@ -510,7 +510,7 @@ int audio_notifier_deregister(char *client_name) int ret = 0; int ret2; struct list_head *ptr, *next; - struct client_data *client_data; + struct client_data *client_data = NULL; if (client_name == NULL) { pr_err("%s: client_name is NULL\n", __func__); diff --git a/dsp/codecs/audio_utils_aio.c b/dsp/codecs/audio_utils_aio.c index c67a24d7aa15..e8f22e79f8cb 100644 --- a/dsp/codecs/audio_utils_aio.c +++ b/dsp/codecs/audio_utils_aio.c @@ -1067,6 +1067,8 @@ static int audio_aio_async_write(struct q6audio_aio *audio, struct audio_client *ac; struct audio_aio_write_param param; + memset(¶m, 0, sizeof(param)); + if (!audio || !buf_node) { pr_err("%s NULL pointer audio=[0x%pK], buf_node=[0x%pK]\n", __func__, audio, buf_node); diff --git a/dsp/q6adm.c b/dsp/q6adm.c index bc217237be91..461e155d4176 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -2438,7 +2438,7 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology, flags = ADM_LEGACY_DEVICE_SESSION; } - if ((topology == VPM_TX_SM_ECNS_COPP_TOPOLOGY) || + if ((topology == VPM_TX_SM_ECNS_V2_COPP_TOPOLOGY) || (topology == VPM_TX_DM_FLUENCE_COPP_TOPOLOGY) || (topology == VPM_TX_DM_RFECNS_COPP_TOPOLOGY)) rate = 16000; diff --git a/dsp/q6core.c b/dsp/q6core.c index 181a800cbf1f..8d82704055fc 100644 --- a/dsp/q6core.c +++ b/dsp/q6core.c @@ -373,7 +373,7 @@ int q6core_get_service_version(uint32_t service_id, svc_size = q6core_get_avcs_service_size(service_id); if (svc_size != size) { - pr_err("%s: Expected size: %ld, Provided size: %ld", + pr_err("%s: Expected size: %zu, Provided size: %zu\n", __func__, svc_size, size); return -EINVAL; } diff --git a/dsp/q6voice.c b/dsp/q6voice.c index 28e50d9f55eb..7cd340f96177 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -8038,7 +8038,7 @@ uint32_t voice_get_topology(uint32_t topology_idx) 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; + topology = VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS_V2; } else { pr_err("%s: cal index %x is invalid!\n", __func__, topology_idx); diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index 66d6e475e896..2256b7b4d9b8 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -3666,7 +3666,7 @@ struct afe_lpass_core_shared_clk_config_command { #define DEFAULT_POPP_TOPOLOGY 0x00010BE4 #define COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY 0x0001076B #define COMPRESSED_PASSTHROUGH_NONE_TOPOLOGY 0x00010774 -#define VPM_TX_SM_ECNS_COPP_TOPOLOGY 0x00010F71 +#define VPM_TX_SM_ECNS_V2_COPP_TOPOLOGY 0x00010F89 #define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY 0x00010F72 #define VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY 0x00010F75 #define VPM_TX_DM_RFECNS_COPP_TOPOLOGY 0x00010F86 diff --git a/include/dsp/q6voice.h b/include/dsp/q6voice.h index f6f0d8c80591..7818707b5aa9 100644 --- a/include/dsp/q6voice.h +++ b/include/dsp/q6voice.h @@ -1218,7 +1218,7 @@ struct vss_istream_cmd_set_packet_exchange_mode_t { #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_SM_ECNS_V2 0x00010F89 #define VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE 0x00010F72 #define VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT 0x00010F77 diff --git a/ipc/apr.c b/ipc/apr.c index bec513ad270e..8fc62f406cb7 100644 --- a/ipc/apr.c +++ b/ipc/apr.c @@ -820,6 +820,7 @@ static void dispatch_event(unsigned long code, uint16_t proc) uint16_t clnt; int i, j; + memset(&data, 0, sizeof(data)); data.opcode = RESET_EVENTS; data.reset_event = code; diff --git a/ipc/apr_tal_glink.c b/ipc/apr_tal_glink.c index f99f9ed22e7a..7de7ddafdb54 100644 --- a/ipc/apr_tal_glink.c +++ b/ipc/apr_tal_glink.c @@ -239,7 +239,7 @@ int apr_tal_rx_intents_config(struct apr_svc_ch_dev *apr_ch, int num_of_intents, uint32_t size) { int i; - int rc; + int rc = 0; if (!apr_ch || !num_of_intents || !size) { pr_err("%s: Invalid parameter\n", __func__); diff --git a/soc/soundwire.c b/soc/soundwire.c index 210de34e6666..6095c0a82e94 100644 --- a/soc/soundwire.c +++ b/soc/soundwire.c @@ -79,11 +79,16 @@ void swr_remove_device(struct swr_device *swr_dev) { struct swr_device *swr_dev_loop, *safe; + /* + * master still has reference to all nodes and deletes + * at platform_unregister, so need to init the deleted + * entry + */ list_for_each_entry_safe(swr_dev_loop, safe, &swr_dev->master->devices, dev_list) { if (swr_dev == swr_dev_loop) - list_del(&swr_dev_loop->dev_list); + list_del_init(&swr_dev_loop->dev_list); } } EXPORT_SYMBOL(swr_remove_device); @@ -789,9 +794,7 @@ static void swr_unregister_device(struct swr_device *swr) static void swr_master_release(struct device *dev) { - struct swr_master *master = to_swr_master(dev); - - kfree(master); + /* kfree of master done at swrm_remove of device */ } #define swr_master_attr_gr NULL diff --git a/soc/swr-wcd-ctrl.c b/soc/swr-wcd-ctrl.c index e7d7c797dd9e..bb78372aa9e5 100644 --- a/soc/swr-wcd-ctrl.c +++ b/soc/swr-wcd-ctrl.c @@ -396,11 +396,17 @@ static int swrm_clk_request(struct swr_mstr_ctrl *swrm, bool enable) return -EINVAL; if (enable) { - swrm->clk(swrm->handle, true); - swrm->state = SWR_MSTR_UP; - } else { + swrm->clk_ref_count++; + if (swrm->clk_ref_count == 1) { + swrm->clk(swrm->handle, true); + swrm->state = SWR_MSTR_UP; + } + } else if (--swrm->clk_ref_count == 0) { swrm->clk(swrm->handle, false); swrm->state = SWR_MSTR_DOWN; + } else if (swrm->clk_ref_count < 0) { + pr_err("%s: swrm clk count mismatch\n", __func__); + swrm->clk_ref_count = 0; } return 0; } @@ -1170,7 +1176,10 @@ static irqreturn_t swr_mstr_interrupt(int irq, void *dev) u8 devnum = 0; int ret = IRQ_HANDLED; - pm_runtime_get_sync(&swrm->pdev->dev); + mutex_lock(&swrm->reslock); + swrm_clk_request(swrm, true); + mutex_unlock(&swrm->reslock); + intr_sts = swrm->read(swrm->handle, SWRM_INTERRUPT_STATUS); intr_sts &= SWRM_INTERRUPT_STATUS_RMSK; for (i = 0; i < SWRM_INTERRUPT_MAX; i++) { @@ -1258,8 +1267,10 @@ static irqreturn_t swr_mstr_interrupt(int irq, void *dev) break; } } - pm_runtime_mark_last_busy(&swrm->pdev->dev); - pm_runtime_put_autosuspend(&swrm->pdev->dev); + + mutex_lock(&swrm->reslock); + swrm_clk_request(swrm, false); + mutex_unlock(&swrm->reslock); return ret; } @@ -1447,6 +1458,7 @@ static int swrm_probe(struct platform_device *pdev) swrm->wcmd_id = 0; swrm->slave_status = 0; swrm->num_rx_chs = 0; + swrm->clk_ref_count = 0; swrm->state = SWR_MSTR_RESUME; init_completion(&swrm->reset); init_completion(&swrm->broadcast); diff --git a/soc/swr-wcd-ctrl.h b/soc/swr-wcd-ctrl.h index 5a4f79b4ecf6..52a60a3c6087 100644 --- a/soc/swr-wcd-ctrl.h +++ b/soc/swr-wcd-ctrl.h @@ -78,6 +78,7 @@ struct swr_mstr_ctrl { struct device *dev; struct resource *supplies; struct clk *mclk; + int clk_ref_count; struct completion reset; struct completion broadcast; struct mutex mlock; From 540284901837726119cd0a1f672787587fffae80 Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Mon, 4 Sep 2017 11:42:26 +0530 Subject: [PATCH 017/276] dsp: add CELT encoder support Add CELT encoder support to enable audio broadcast over split a2dp. Change-Id: If0d927913c5e525ff1cebf857323ab70be2122a1 Signed-off-by: Preetam Singh Ranawat --- asoc/msm-dai-q6-v2.c | 15 +++++++++-- dsp/q6afe.c | 3 ++- include/dsp/apr_audio-v2.h | 51 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 6b620a5c8ed0..25eb2ecff35a 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -49,6 +49,7 @@ enum { 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, + ENC_FMT_CELT = ASM_MEDIA_FMT_CELT, }; enum { @@ -2146,7 +2147,12 @@ static int msm_dai_q6_afe_enc_cfg_get(struct snd_kcontrol *kcontrol, 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)); + sizeof(struct asm_custom_enc_cfg_t)); + break; + case ENC_FMT_CELT: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_celt_enc_cfg_t)); break; default: pr_debug("%s: unknown format = %d\n", @@ -2190,7 +2196,12 @@ static int msm_dai_q6_afe_enc_cfg_put(struct snd_kcontrol *kcontrol, 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)); + sizeof(struct asm_custom_enc_cfg_t)); + break; + case ENC_FMT_CELT: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_celt_enc_cfg_t)); break; default: pr_debug("%s: Ignore enc config for unknown format = %d\n", diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 78be555eb8eb..a6041b377525 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -2824,7 +2824,8 @@ static int q6afe_send_enc_config(u16 port_id, 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) { + format != ASM_MEDIA_FMT_APTX && format != ASM_MEDIA_FMT_APTX_HD && + format != ASM_MEDIA_FMT_CELT) { pr_err("%s:Unsuppported format Ignore AFE config\n", __func__); return 0; } diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index 2256b7b4d9b8..3d9a56cdfefd 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -3252,7 +3252,7 @@ struct asm_aac_enc_cfg_v2_t { #define PCM_CHANNEL_R 2 #define PCM_CHANNEL_C 3 -struct asm_custom_enc_cfg_aptx_t { +struct asm_custom_enc_cfg_t { uint32_t sample_rate; /* Mono or stereo */ uint16_t num_channels; @@ -3263,6 +3263,52 @@ struct asm_custom_enc_cfg_aptx_t { uint8_t channel_mapping[8]; uint32_t custom_size; } __packed; +#define ASM_MEDIA_FMT_CELT 0x00013221 +struct asm_celt_specific_enc_cfg_t { + /* + * Bit rate used for encoding. + * This is used to calculate the upper threshold + * for bytes per frame if vbr_flag is 1. + * Or else, this will be used as a regular constant + * bit rate for encoder output. + * @Range : 32000 to 1536000 + * @Default: 128 + */ + uint32_t bit_rate; + /* + * Frame size used for encoding. + * @Range : 64, 128, 256, 512 + * @Default: 256 + */ + uint16_t frame_size; + /* + * complexity of algorithm. + * @Range : 0-10 + * @Default: 3 + */ + uint16_t complexity; + /* + * Switch variable for prediction feature. + * Used to choose between the level of interframe + * predictions allowed while encoding. + * @Range: + * 0: Independent Frames. + * 1: Short Term interframe prediction allowed. + * 2: Long term prediction allowed. + * @Default: 2 + */ + uint16_t prediction_mode; + /* + * Variable Bit Rate flag. + * @Default: 0 + */ + uint16_t vbr_flag; +} __packed; + +struct asm_celt_enc_cfg_t { + struct asm_custom_enc_cfg_t custom_config; + struct asm_celt_specific_enc_cfg_t celt_specific_config; +} __packed; struct afe_enc_fmt_id_param_t { /* @@ -3331,7 +3377,8 @@ struct afe_port_media_type_t { 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 asm_custom_enc_cfg_t custom_config; + struct asm_celt_enc_cfg_t celt_config; }; struct afe_enc_config { From 5eaced6d700c7aa2f33f81583ac87b328bece566 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Thu, 7 Sep 2017 10:31:26 -0700 Subject: [PATCH 018/276] ASoC: codecs: wcd934x: Fix slimbus overflow Remove MCLK_CONTROL and FS_CNT_CONTROL from the list of volatile registers so that regmap sync happens for these registers when coming out of power collapse. These clock registers need to be synced otherwise codec state is not restored correctly after power collapse. CRs-fixed: 2053506 Change-Id: I9b5385a357f9ad05ad085603410704e916e3985e Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd934x/wcd934x-regmap.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x-regmap.c b/asoc/codecs/wcd934x/wcd934x-regmap.c index 7f648fe1c938..ff18bd80a971 100644 --- a/asoc/codecs/wcd934x/wcd934x-regmap.c +++ b/asoc/codecs/wcd934x/wcd934x-regmap.c @@ -1932,12 +1932,10 @@ static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg) case WCD934X_ANA_BIAS: case WCD934X_ANA_BUCK_CTL: case WCD934X_ANA_RCO: - case WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL: case WCD934X_CODEC_RPM_CLK_GATE: case WCD934X_BIAS_VBG_FINE_ADJ: case WCD934X_CODEC_CPR_SVS_CX_VDD: case WCD934X_CODEC_CPR_SVS2_CX_VDD: - case WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL: return true; } From e908ba91db76c49884b2bf3dceca3849ab4043c0 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Thu, 31 Aug 2017 18:36:00 -0700 Subject: [PATCH 019/276] ASoC: wcd-spi: initialize local variable 'status' Variable 'status' is used without initialization. Initialize it to the right value. Change-Id: I7f43d8905d8192e5f308e9fd00a52eb531f2f698 Signed-off-by: Xiaoyu Ye --- asoc/codecs/wcd-spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/codecs/wcd-spi.c b/asoc/codecs/wcd-spi.c index 67cb46c74925..072a7538ef1d 100644 --- a/asoc/codecs/wcd-spi.c +++ b/asoc/codecs/wcd-spi.c @@ -522,7 +522,7 @@ static int wcd_spi_cmd_rdsr(struct spi_device *spi, struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0]; struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1]; u8 rdsr_cmd; - u32 status; + u32 status = 0; int ret; rdsr_cmd = WCD_SPI_CMD_RDSR; From 72863154a8fc83dc326d06f3d2862f1a45443050 Mon Sep 17 00:00:00 2001 From: Vikram Panduranga Date: Wed, 6 Sep 2017 15:47:35 -0700 Subject: [PATCH 020/276] ASoC: add SLIM_7_TX routing for Multimedia10 Add missing FE to BE link for Multimedia10. Change-Id: Ibf9a80c942107b1f68b5954b21f87d4f7494b1fc Signed-off-by: Vikram Panduranga --- asoc/msm-pcm-routing-v2.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 8e3fe895d98d..29dfade94f0f 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -7281,6 +7281,9 @@ static const struct snd_kcontrol_new mmul10_mixer_controls[] = { SOC_SINGLE_EXT("SLIM_6_TX", MSM_BACKEND_DAI_SLIMBUS_6_TX, MSM_FRONTEND_DAI_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), From 98526effb3dd34bf6112fdcd01c2bf7b3a2517fe Mon Sep 17 00:00:00 2001 From: yidongh Date: Tue, 5 Sep 2017 17:57:55 +0800 Subject: [PATCH 021/276] asoc: initialize L/R/C gain for both mono/stereo playback When there's a dynamic channel change from mono to stereo, volume burst is observed because center channel gain for stereo is not initialized. Initialize center channel gain for both mono and stereo playback. CRs-Fixed: 2095081 Change-Id: I02254b5696a17e2792ea26a2265ab01326f5cf9e Signed-off-by: Yidong Huang --- asoc/msm-compress-q6-v2.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index a9f28ca8ef89..cd9c28195b87 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -388,12 +388,9 @@ static int msm_compr_set_volume(struct snd_compr_stream *cstream, } else { gain_list[0] = volume_l; gain_list[1] = volume_r; - /* force sending FR/FL/FC volume for mono */ - if (prtd->num_channels == 1) { - gain_list[2] = volume_l; - num_channels = 3; - use_default = true; - } + gain_list[2] = volume_l; + num_channels = 3; + use_default = true; rc = q6asm_set_multich_gain(prtd->audio_client, num_channels, gain_list, chmap, use_default); } From da488bf1ff3eafdb5a6155480c4442df1fe47cd4 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Wed, 30 Aug 2017 22:16:25 +0530 Subject: [PATCH 022/276] asoc: remove unused audio files Update to remove unused driver files from techpack audio. CRs-Fixed: 2105780 Change-Id: I1dcf1c6e75838863eee0556f7919068dfc47772d Signed-off-by: Laxminath Kasam --- asoc/Makefile | 4 - asoc/codecs/Makefile | 19 +- asoc/codecs/wcd934x/Makefile | 3 +- asoc/codecs/wsa881x-analog.c | 1446 --------- asoc/codecs/wsa881x-analog.h | 50 - asoc/codecs/wsa881x-irq.c | 610 ---- asoc/codecs/wsa881x-irq.h | 82 - asoc/codecs/wsa881x-registers-analog.h | 206 -- asoc/codecs/wsa881x-regmap-analog.c | 499 --- asoc/codecs/wsa881x-tables-analog.c | 171 - asoc/msm-audio-pinctrl.c | 316 -- asoc/msm-audio-pinctrl.h | 43 - asoc/msm8996.c | 4007 ------------------------ asoc/sdm660-external.c | 1 - 14 files changed, 8 insertions(+), 7449 deletions(-) delete mode 100644 asoc/codecs/wsa881x-analog.c delete mode 100644 asoc/codecs/wsa881x-analog.h delete mode 100644 asoc/codecs/wsa881x-irq.c delete mode 100644 asoc/codecs/wsa881x-irq.h delete mode 100644 asoc/codecs/wsa881x-registers-analog.h delete mode 100644 asoc/codecs/wsa881x-regmap-analog.c delete mode 100644 asoc/codecs/wsa881x-tables-analog.c delete mode 100644 asoc/msm-audio-pinctrl.c delete mode 100644 asoc/msm-audio-pinctrl.h delete mode 100644 asoc/msm8996.c diff --git a/asoc/Makefile b/asoc/Makefile index 3fccff6726ba..cc28148dcf3e 100644 --- a/asoc/Makefile +++ b/asoc/Makefile @@ -11,10 +11,6 @@ obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o 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_MACHINE_MSM8998) += snd-soc-msm8998.o diff --git a/asoc/codecs/Makefile b/asoc/codecs/Makefile index f0477f7eeb54..9b02c8a51cd7 100644 --- a/asoc/codecs/Makefile +++ b/asoc/codecs/Makefile @@ -1,4 +1,6 @@ -snd-soc-wcd9xxx-v2-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o wcdcal-hwdep.o wcd9xxx-soc-init.o +snd-soc-wcd9xxx-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o \ + wcdcal-hwdep.o wcd9xxx-soc-init.o wcd-dsp-utils.o \ + wcd-dsp-mgr.o audio-ext-clk-up.o snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o snd-soc-wsa881x-objs := wsa881x.o wsa881x-tables.o wsa881x-regmap.o wsa881x-temp-sensor.o snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o @@ -8,8 +10,6 @@ endif ifneq (,$(filter $(CONFIG_SND_SOC_WCD_MBHC_ADC),y m)) snd-soc-wcd-mbhc-objs += wcd-mbhc-adc.o endif -snd-soc-wcd-dsp-utils-objs := wcd-dsp-utils.o -snd-soc-wcd-dsp-mgr-objs := wcd-dsp-mgr.o snd-soc-wcd-spi-objs := wcd-spi.o snd-soc-wcd9335-objs := wcd9335.o snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o @@ -19,18 +19,10 @@ obj-$(CONFIG_SND_SOC_WCD_CPE) += snd-soc-wcd-cpe.o obj-$(CONFIG_SND_SOC_WCD934X) += wcd934x/ obj-$(CONFIG_SND_SOC_SDM660_CDC) += sdm660_cdc/ obj-$(CONFIG_SND_SOC_MSM_SDW) += msm_sdw/ -ifeq ($(CONFIG_COMMON_CLK_MSM), y) - obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clk.o -endif -ifeq ($(CONFIG_COMMON_CLK_QCOM), y) - obj-$(CONFIG_AUDIO_EXT_CLK) += audio-ext-clk-up.o -endif -obj-$(CONFIG_SND_SOC_WCD9XXX_V2) += snd-soc-wcd9xxx-v2.o -obj-$(CONFIG_SND_SOC_WCD_CPE) += snd-soc-wcd-cpe.o +obj-$(CONFIG_SND_SOC_WCD9XXX_V2) += snd-soc-wcd9xxx.o obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o -obj-$(CONFIG_SND_SOC_WCD_DSP_MGR) += snd-soc-wcd-dsp-mgr.o snd-soc-wcd-dsp-utils.o obj-$(CONFIG_SND_SOC_WCD_SPI) += snd-soc-wcd-spi.o snd-soc-msm-stub-objs := msm_stub.o @@ -41,5 +33,8 @@ wcd-core-objs := wcd9xxx-rst.o wcd9xxx-core-init.o \ wcd9xxx-slimslave.o wcd9xxx-utils.o \ wcd9335-regmap.o wcd9335-tables.o \ msm-cdc-pinctrl.o msm-cdc-supply.o +wcd-core-objs += wcd934x/wcd934x-regmap.o +wcd-core-objs += wcd934x/wcd934x-tables.o + obj-$(CONFIG_WCD9XXX_CODEC_CORE) += wcd-core.o obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) += msm_hdmi_codec_rx.o diff --git a/asoc/codecs/wcd934x/Makefile b/asoc/codecs/wcd934x/Makefile index c4db59b0e9d7..12781f6d4556 100644 --- a/asoc/codecs/wcd934x/Makefile +++ b/asoc/codecs/wcd934x/Makefile @@ -2,6 +2,5 @@ # Makefile for wcd934x codec driver. # snd-soc-wcd934x-objs := wcd934x.o wcd934x-dsp-cntl.o \ - wcd934x-mbhc.o wcd934x-dsd.o \ - wcd934x-regmap.o wcd934x-tables.o + wcd934x-mbhc.o wcd934x-dsd.o obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o diff --git a/asoc/codecs/wsa881x-analog.c b/asoc/codecs/wsa881x-analog.c deleted file mode 100644 index 4de962497765..000000000000 --- a/asoc/codecs/wsa881x-analog.c +++ /dev/null @@ -1,1446 +0,0 @@ -/* - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "wsa881x-analog.h" -#include "wsa881x-temp-sensor.h" -#include "../msm/msm-audio-pinctrl.h" - -#define SPK_GAIN_12DB 4 -#define WIDGET_NAME_MAX_SIZE 80 - -/* - * Private data Structure for wsa881x. All parameters related to - * WSA881X codec needs to be defined here. - */ -struct wsa881x_pdata { - struct regmap *regmap[2]; - struct i2c_client *client[2]; - struct snd_soc_codec *codec; - - /* track wsa881x status during probe */ - int status; - bool boost_enable; - bool visense_enable; - int spk_pa_gain; - struct i2c_msg xfer_msg[2]; - struct mutex xfer_lock; - bool regmap_flag; - bool wsa_active; - int index; - int (*enable_mclk)(struct snd_soc_card *, bool); - struct wsa881x_tz_priv tz_pdata; - int bg_cnt; - int clk_cnt; - int enable_cnt; - int version; - struct mutex bg_lock; - struct mutex res_lock; - struct delayed_work ocp_ctl_work; -}; - -enum { - WSA881X_STATUS_PROBING, - WSA881X_STATUS_I2C, -}; - -#define WSA881X_OCP_CTL_TIMER_SEC 2 -#define WSA881X_OCP_CTL_TEMP_CELSIUS 25 -#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60 - -static int wsa881x_ocp_poll_timer_sec = WSA881X_OCP_CTL_POLL_TIMER_SEC; -module_param(wsa881x_ocp_poll_timer_sec, int, 0664); -MODULE_PARM_DESC(wsa881x_ocp_poll_timer_sec, "timer for ocp ctl polling"); - -static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, - bool enable); - -const char *wsa_tz_names[] = {"wsa881x.0e", "wsa881x.0f"}; - -struct wsa881x_pdata wsa_pdata[MAX_WSA881X_DEVICE]; - -static bool pinctrl_init; - -static int wsa881x_populate_dt_pdata(struct device *dev); -static int wsa881x_reset(struct wsa881x_pdata *pdata, bool enable); -static int wsa881x_startup(struct wsa881x_pdata *pdata); -static int wsa881x_shutdown(struct wsa881x_pdata *pdata); - -static int delay_array_msec[] = {10, 20, 30, 40, 50}; - -static int wsa881x_i2c_addr = -1; -static int wsa881x_probing_count; -static int wsa881x_presence_count; - -static const char * const wsa881x_spk_pa_gain_text[] = { -"POS_13P5_DB", "POS_12_DB", "POS_10P5_DB", "POS_9_DB", "POS_7P5_DB", -"POS_6_DB", "POS_4P5_DB", "POS_3_DB", "POS_1P5_DB", "POS_0_DB"}; - -static const struct soc_enum wsa881x_spk_pa_gain_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa881x_spk_pa_gain_text), - wsa881x_spk_pa_gain_text), -}; - -static int wsa881x_spk_pa_gain_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - ucontrol->value.integer.value[0] = wsa881x->spk_pa_gain; - - dev_dbg(codec->dev, "%s: spk_pa_gain = %ld\n", __func__, - ucontrol->value.integer.value[0]); - - return 0; -} - -static int wsa881x_spk_pa_gain_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - if (ucontrol->value.integer.value[0] < 0 || - ucontrol->value.integer.value[0] > 0xC) { - dev_err(codec->dev, "%s: Unsupported gain val %ld\n", - __func__, ucontrol->value.integer.value[0]); - return -EINVAL; - } - wsa881x->spk_pa_gain = ucontrol->value.integer.value[0]; - dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", - __func__, ucontrol->value.integer.value[0]); - - return 0; -} - -static int get_i2c_wsa881x_device_index(u16 reg) -{ - u16 mask = 0x0f00; - int value = 0; - - value = ((reg & mask) >> 8) & 0x000f; - - switch (value) { - case 0: - return 0; - case 1: - return 1; - default: - break; - } - return -EINVAL; -} - -static int wsa881x_i2c_write_device(struct wsa881x_pdata *wsa881x, - unsigned int reg, unsigned int val) -{ - int i = 0, rc = 0; - int wsa881x_index; - struct i2c_msg *msg; - int ret = 0; - int bytes = 1; - u8 reg_addr = 0; - u8 data[bytes + 1]; - - wsa881x_index = get_i2c_wsa881x_device_index(reg); - if (wsa881x_index < 0) { - pr_err("%s:invalid register to write\n", __func__); - return -EINVAL; - } - if (wsa881x->regmap_flag) { - rc = regmap_write(wsa881x->regmap[wsa881x_index], reg, val); - for (i = 0; rc && i < ARRAY_SIZE(delay_array_msec); i++) { - pr_err("Failed writing reg=%u - retry(%d)\n", reg, i); - /* retry after delay of increasing order */ - msleep(delay_array_msec[i]); - rc = regmap_write(wsa881x->regmap[wsa881x_index], - reg, val); - } - if (rc) - pr_err("Failed writing reg=%u rc=%d\n", reg, rc); - else - pr_err("write success register = %x val = %x\n", - reg, val); - } else { - reg_addr = (u8)reg; - msg = &wsa881x->xfer_msg[0]; - msg->addr = wsa881x->client[wsa881x_index]->addr; - msg->len = bytes + 1; - msg->flags = 0; - data[0] = reg; - data[1] = (u8)val; - msg->buf = data; - ret = i2c_transfer(wsa881x->client[wsa881x_index]->adapter, - wsa881x->xfer_msg, 1); - /* Try again if the write fails */ - if (ret != 1) { - ret = i2c_transfer( - wsa881x->client[wsa881x_index]->adapter, - wsa881x->xfer_msg, 1); - if (ret != 1) { - pr_err("failed to write the device\n"); - return ret; - } - } - pr_debug("write success reg = %x val = %x\n", reg, data[1]); - } - return rc; -} - -static int wsa881x_i2c_read_device(struct wsa881x_pdata *wsa881x, - unsigned int reg) -{ - int wsa881x_index; - int i = 0, rc = 0; - unsigned int val; - struct i2c_msg *msg; - int ret = 0; - u8 reg_addr = 0; - u8 dest[5]; - - wsa881x_index = get_i2c_wsa881x_device_index(reg); - if (wsa881x_index < 0) { - pr_err("%s:invalid register to read\n", __func__); - return -EINVAL; - } - if (wsa881x->regmap_flag) { - rc = regmap_read(wsa881x->regmap[wsa881x_index], reg, &val); - for (i = 0; rc && i < ARRAY_SIZE(delay_array_msec); i++) { - pr_err("Failed reading reg=%u - retry(%d)\n", reg, i); - /* retry after delay of increasing order */ - msleep(delay_array_msec[i]); - rc = regmap_read(wsa881x->regmap[wsa881x_index], - reg, &val); - } - if (rc) { - pr_err("Failed reading reg=%u rc=%d\n", reg, rc); - return rc; - } - pr_debug("read success reg = %x val = %x\n", - reg, val); - } else { - reg_addr = (u8)reg; - msg = &wsa881x->xfer_msg[0]; - msg->addr = wsa881x->client[wsa881x_index]->addr; - msg->len = 1; - msg->flags = 0; - msg->buf = ®_addr; - - msg = &wsa881x->xfer_msg[1]; - msg->addr = wsa881x->client[wsa881x_index]->addr; - msg->len = 1; - msg->flags = I2C_M_RD; - msg->buf = dest; - ret = i2c_transfer(wsa881x->client[wsa881x_index]->adapter, - wsa881x->xfer_msg, 2); - - /* Try again if read fails first time */ - if (ret != 2) { - ret = i2c_transfer( - wsa881x->client[wsa881x_index]->adapter, - wsa881x->xfer_msg, 2); - if (ret != 2) { - pr_err("failed to read wsa register:%d\n", - reg); - return ret; - } - } - val = dest[0]; - } - return val; -} - -static unsigned int wsa881x_i2c_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct wsa881x_pdata *wsa881x; - unsigned int val; - int ret; - - if (codec == NULL) { - pr_err("%s: invalid codec\n", __func__); - return -EINVAL; - } - wsa881x = snd_soc_codec_get_drvdata(codec); - if (!wsa881x->wsa_active) { - ret = snd_soc_cache_read(codec, reg, &val); - if (ret >= 0) - return val; - dev_err(codec->dev, - "cache read failed for reg: 0x%x ret: %d\n", - reg, ret); - return ret; - } - return wsa881x_i2c_read_device(wsa881x, reg); -} - -static int wsa881x_i2c_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int val) -{ - struct wsa881x_pdata *wsa881x; - int ret = 0; - - if (codec == NULL) { - pr_err("%s: invalid codec\n", __func__); - return -EINVAL; - } - wsa881x = snd_soc_codec_get_drvdata(codec); - if (!wsa881x->wsa_active) { - ret = snd_soc_cache_write(codec, reg, val); - if (ret != 0) - dev_err(codec->dev, "cache write to %x failed: %d\n", - reg, ret); - return ret; - } - return wsa881x_i2c_write_device(wsa881x, reg, val); -} - -static int wsa881x_i2c_get_client_index(struct i2c_client *client, - int *wsa881x_index) -{ - int ret = 0; - - switch (client->addr) { - case WSA881X_I2C_SPK0_SLAVE0_ADDR: - case WSA881X_I2C_SPK0_SLAVE1_ADDR: - *wsa881x_index = WSA881X_I2C_SPK0_SLAVE0; - break; - case WSA881X_I2C_SPK1_SLAVE0_ADDR: - case WSA881X_I2C_SPK1_SLAVE1_ADDR: - *wsa881x_index = WSA881X_I2C_SPK1_SLAVE0; - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static int wsa881x_boost_ctrl(struct snd_soc_codec *codec, bool enable) -{ - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - pr_debug("%s: enable:%d\n", __func__, enable); - if (enable) { - if (!WSA881X_IS_2_0(wsa881x->version)) { - snd_soc_update_bits(codec, WSA881X_ANA_CTL, - 0x01, 0x01); - snd_soc_update_bits(codec, WSA881X_ANA_CTL, - 0x04, 0x04); - snd_soc_update_bits(codec, WSA881X_BOOST_PS_CTL, - 0x40, 0x00); - snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, - 0xF0, 0xB0); - snd_soc_update_bits(codec, WSA881X_BOOST_ZX_CTL, - 0x20, 0x00); - snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, - 0x80, 0x80); - } else { - snd_soc_update_bits(codec, WSA881X_BOOST_LOOP_STABILITY, - 0x03, 0x03); - snd_soc_update_bits(codec, WSA881X_BOOST_MISC2_CTL, - 0xFF, 0x14); - snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, - 0x80, 0x80); - snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, - 0x03, 0x00); - snd_soc_update_bits(codec, - WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, - 0x0C, 0x04); - snd_soc_update_bits(codec, - WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, - 0x03, 0x00); - snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, - 0xF0, 0x70); - snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x03, 0x01); - snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, - 0x08, 0x08); - snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x04, 0x04); - snd_soc_update_bits(codec, WSA881X_BOOST_CURRENT_LIMIT, - 0x0F, 0x08); - snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, - 0x80, 0x80); - } - /* For WSA8810, start-up time is 1500us as per qcrg sequence */ - usleep_range(1500, 1510); - } else { - /* ENSURE: Class-D amp is shutdown. CLK is still on */ - snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x00); - /* boost settle time is 1500us as per qcrg sequence */ - usleep_range(1500, 1510); - } - return 0; -} - -static int wsa881x_visense_txfe_ctrl(struct snd_soc_codec *codec, bool enable, - u8 isense1_gain, u8 isense2_gain, - u8 vsense_gain) -{ - u8 value = 0; - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - pr_debug("%s: enable:%d\n", __func__, enable); - - if (enable) { - if (WSA881X_IS_2_0(wsa881x->version)) { - snd_soc_update_bits(codec, WSA881X_OTP_REG_28, - 0x3F, 0x3A); - snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG1, - 0xFF, 0xB2); - snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG2, - 0xFF, 0x05); - } - snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_VSENSE_VCM, - 0x08, 0x00); - if (WSA881X_IS_2_0(wsa881x->version)) { - snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2, - 0x1C, 0x04); - } else { - snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2, - 0x08, 0x08); - snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2, - 0x02, 0x02); - } - value = ((isense2_gain << 6) | (isense1_gain << 4) | - (vsense_gain << 3)); - snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, - 0xF8, value); - snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, - 0x01, 0x01); - } else { - if (WSA881X_IS_2_0(wsa881x->version)) - snd_soc_update_bits(codec, - WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x10, 0x10); - else - snd_soc_update_bits(codec, - WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x08, 0x08); - /* - * 200us sleep is needed after visense txfe disable as per - * HW requirement. - */ - usleep_range(200, 210); - - snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN, - 0x01, 0x00); - } - return 0; -} - -static int wsa881x_visense_adc_ctrl(struct snd_soc_codec *codec, bool enable) -{ - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - pr_debug("%s: enable:%d\n", __func__, enable); - if (enable) { - if (!WSA881X_IS_2_0(wsa881x->version)) - snd_soc_update_bits(codec, WSA881X_ADC_SEL_IBIAS, - 0x70, 0x40); - snd_soc_update_bits(codec, WSA881X_ADC_EN_SEL_IBIAS, - 0x07, 0x04); - snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, 0x80, 0x80); - snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, 0x80, 0x80); - } else { - /* Ensure: Speaker Protection has been stopped */ - snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, 0x80, 0x00); - snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, 0x80, 0x00); - } - - return 0; -} - -static void wsa881x_bandgap_ctrl(struct snd_soc_codec *codec, bool enable) -{ - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - dev_dbg(codec->dev, "%s: enable:%d, bg_count:%d\n", __func__, - enable, wsa881x->bg_cnt); - mutex_lock(&wsa881x->bg_lock); - if (enable) { - ++wsa881x->bg_cnt; - if (wsa881x->bg_cnt == 1) { - snd_soc_update_bits(codec, WSA881X_TEMP_OP, - 0x08, 0x08); - /* 400usec sleep is needed as per HW requirement */ - usleep_range(400, 410); - snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x04); - } - } else { - --wsa881x->bg_cnt; - if (wsa881x->bg_cnt <= 0) { - WARN_ON(wsa881x->bg_cnt < 0); - wsa881x->bg_cnt = 0; - snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x00); - snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x08, 0x00); - } - } - mutex_unlock(&wsa881x->bg_lock); -} - -static void wsa881x_clk_ctrl(struct snd_soc_codec *codec, bool enable) -{ - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - dev_dbg(codec->dev, "%s:ss enable:%d, clk_count:%d\n", __func__, - enable, wsa881x->clk_cnt); - mutex_lock(&wsa881x->res_lock); - if (enable) { - ++wsa881x->clk_cnt; - if (wsa881x->clk_cnt == 1) { - snd_soc_write(codec, WSA881X_CDC_RST_CTL, 0x02); - snd_soc_write(codec, WSA881X_CDC_RST_CTL, 0x03); - snd_soc_write(codec, WSA881X_CLOCK_CONFIG, 0x01); - snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x01); - snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x01); - } - } else { - --wsa881x->clk_cnt; - if (wsa881x->clk_cnt <= 0) { - WARN_ON(wsa881x->clk_cnt < 0); - wsa881x->clk_cnt = 0; - snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x00); - snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x00); - if (WSA881X_IS_2_0(wsa881x->version)) - snd_soc_update_bits(codec, - WSA881X_CDC_TOP_CLK_CTL, 0x01, 0x00); - } - } - mutex_unlock(&wsa881x->res_lock); -} - -static int wsa881x_rdac_ctrl(struct snd_soc_codec *codec, bool enable) -{ - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - pr_debug("%s: enable:%d\n", __func__, enable); - if (enable) { - snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x08, 0x00); - snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0x08, 0x08); - snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x20, 0x20); - snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x20, 0x00); - snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x40, 0x40); - snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x80, 0x80); - if (WSA881X_IS_2_0(wsa881x->version)) { - snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, - 0x01, 0x01); - snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, - 0x30, 0x30); - snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, - 0x0C, 0x00); - } - snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0xF0, 0x40); - snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x01, 0x01); - } else { - /* Ensure class-D amp is off */ - snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x80, 0x00); - } - return 0; -} - -static int wsa881x_spkr_pa_ctrl(struct snd_soc_codec *codec, bool enable) -{ - int ret = 0; - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - pr_debug("%s: enable:%d\n", __func__, enable); - if (enable) { - /* - * Ensure: Boost is enabled and stable, Analog input is up - * and outputting silence - */ - if (!WSA881X_IS_2_0(wsa881x->version)) { - snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_I, - 0xFF, 0x01); - snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, - 0x02, 0x02); - snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_V, - 0xFF, 0x10); - snd_soc_update_bits(codec, WSA881X_SPKR_PWRSTG_DBG, - 0xA0, 0xA0); - snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, - 0x80, 0x80); - usleep_range(700, 710); - snd_soc_update_bits(codec, WSA881X_SPKR_PWRSTG_DBG, - 0x00, 0x00); - snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_V, - 0xFF, 0x00); - snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, - 0x02, 0x00); - snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_I, - 0xFF, 0x00); - } else - snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, - 0x80, 0x80); - /* add 1000us delay as per qcrg */ - usleep_range(1000, 1010); - snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x01, 0x01); - if (WSA881X_IS_2_0(wsa881x->version)) - snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, - 0x01, 0x00); - usleep_range(1000, 1010); - snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0xF0, - (wsa881x->spk_pa_gain << 4)); - if (wsa881x->visense_enable) { - ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, - "wsa_vi"); - if (ret) { - pr_err("%s: gpio set cannot be activated %s\n", - __func__, "wsa_vi"); - return ret; - } - wsa881x_visense_txfe_ctrl(codec, true, - 0x00, 0x01, 0x00); - wsa881x_visense_adc_ctrl(codec, true); - } - } else { - /* - * Ensure: Boost is still on, Stream from Analog input and - * Speaker Protection has been stopped and input is at 0V - */ - if (WSA881X_IS_2_0(wsa881x->version)) { - snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, - 0x01, 0x01); - usleep_range(1000, 1010); - snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL, - 0x01, 0x00); - msleep(20); - snd_soc_update_bits(codec, WSA881X_ANA_CTL, - 0x03, 0x00); - usleep_range(200, 210); - } - snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x80, 0x00); - } - return 0; -} - -static int wsa881x_get_boost(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - ucontrol->value.integer.value[0] = wsa881x->boost_enable; - return 0; -} - -static int wsa881x_set_boost(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - int value = ucontrol->value.integer.value[0]; - - dev_dbg(codec->dev, "%s: Boost enable current %d, new %d\n", - __func__, wsa881x->boost_enable, value); - wsa881x->boost_enable = value; - return 0; -} - -static int wsa881x_get_visense(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - ucontrol->value.integer.value[0] = wsa881x->visense_enable; - return 0; -} - -static int wsa881x_set_visense(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - int value = ucontrol->value.integer.value[0]; - - dev_dbg(codec->dev, "%s: VIsense enable current %d, new %d\n", - __func__, wsa881x->visense_enable, value); - wsa881x->visense_enable = value; - return 0; -} - -static const struct snd_kcontrol_new wsa881x_snd_controls[] = { - SOC_SINGLE_EXT("BOOST Switch", SND_SOC_NOPM, 0, 1, 0, - wsa881x_get_boost, wsa881x_set_boost), - - SOC_SINGLE_EXT("VISENSE Switch", SND_SOC_NOPM, 0, 1, 0, - wsa881x_get_visense, wsa881x_set_visense), - - SOC_ENUM_EXT("WSA_SPK PA Gain", wsa881x_spk_pa_gain_enum[0], - wsa881x_spk_pa_gain_get, wsa881x_spk_pa_gain_put), -}; - -static const char * const rdac_text[] = { - "ZERO", "Switch", -}; - -static const struct soc_enum rdac_enum = - SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(rdac_text), rdac_text); - -static const struct snd_kcontrol_new rdac_mux[] = { - SOC_DAPM_ENUM("RDAC", rdac_enum) -}; - -static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = w->codec; - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - int ret = 0; - - dev_dbg(codec->dev, "%s: %s %d boost %d visense %d\n", - __func__, w->name, event, - wsa881x->boost_enable, wsa881x->visense_enable); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - ret = wsa881x_startup(wsa881x); - if (ret) { - pr_err("%s: wsa startup failed ret: %d", __func__, ret); - return ret; - } - wsa881x_clk_ctrl(codec, true); - snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x02, 0x02); - if (!WSA881X_IS_2_0(wsa881x->version)) - snd_soc_update_bits(codec, WSA881X_BIAS_REF_CTRL, - 0x0F, 0x08); - wsa881x_bandgap_ctrl(codec, true); - if (!WSA881X_IS_2_0(wsa881x->version)) - snd_soc_update_bits(codec, WSA881X_SPKR_BBM_CTL, - 0x02, 0x02); - snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80); - snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06); - if (!WSA881X_IS_2_0(wsa881x->version)) { - snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL2, - 0x04, 0x04); - snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_INT, - 0x09, 0x09); - } - snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0xF0, 0x20); - if (WSA881X_IS_2_0(wsa881x->version)) - snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, - 0x0E, 0x0E); - if (wsa881x->boost_enable) - wsa881x_boost_ctrl(codec, true); - break; - case SND_SOC_DAPM_POST_PMU: - wsa881x_rdac_ctrl(codec, true); - break; - case SND_SOC_DAPM_PRE_PMD: - wsa881x_rdac_ctrl(codec, false); - if (wsa881x->visense_enable) { - wsa881x_visense_adc_ctrl(codec, false); - wsa881x_visense_txfe_ctrl(codec, false, - 0x00, 0x01, 0x00); - ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, - "wsa_vi"); - if (ret) { - pr_err("%s: gpio set cannot be suspended %s\n", - __func__, "wsa_vi"); - return ret; - } - } - break; - case SND_SOC_DAPM_POST_PMD: - if (wsa881x->boost_enable) - wsa881x_boost_ctrl(codec, false); - wsa881x_clk_ctrl(codec, false); - wsa881x_bandgap_ctrl(codec, false); - ret = wsa881x_shutdown(wsa881x); - if (ret < 0) { - pr_err("%s: wsa shutdown failed ret: %d", - __func__, ret); - return ret; - } - break; - default: - pr_err("%s: invalid event:%d\n", __func__, event); - return -EINVAL; - } - return 0; -} - -static void wsa881x_ocp_ctl_work(struct work_struct *work) -{ - struct wsa881x_pdata *wsa881x; - struct delayed_work *dwork; - struct snd_soc_codec *codec; - unsigned long temp_val; - - dwork = to_delayed_work(work); - wsa881x = container_of(dwork, struct wsa881x_pdata, ocp_ctl_work); - - if (!wsa881x) - return; - - codec = wsa881x->codec; - wsa881x_get_temp(wsa881x->tz_pdata.tz_dev, &temp_val); - dev_dbg(codec->dev, " temp = %ld\n", temp_val); - - if (temp_val <= WSA881X_OCP_CTL_TEMP_CELSIUS) - snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x00); - else - snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); - - schedule_delayed_work(&wsa881x->ocp_ctl_work, - msecs_to_jiffies(wsa881x_ocp_poll_timer_sec * 1000)); -} - -static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - struct snd_soc_codec *codec = w->codec; - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - pr_debug("%s: %s %d\n", __func__, w->name, event); - - switch (event) { - case SND_SOC_DAPM_PRE_PMU: - snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x80); - break; - case SND_SOC_DAPM_POST_PMU: - wsa881x_spkr_pa_ctrl(codec, true); - schedule_delayed_work(&wsa881x->ocp_ctl_work, - msecs_to_jiffies(WSA881X_OCP_CTL_TIMER_SEC * 1000)); - break; - case SND_SOC_DAPM_PRE_PMD: - wsa881x_spkr_pa_ctrl(codec, false); - break; - case SND_SOC_DAPM_POST_PMD: - cancel_delayed_work_sync(&wsa881x->ocp_ctl_work); - snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0); - break; - default: - pr_err("%s: invalid event:%d\n", __func__, event); - return -EINVAL; - } - return 0; -} - - -static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = { - SND_SOC_DAPM_INPUT("WSA_IN"), - - SND_SOC_DAPM_DAC_E("RDAC Analog", NULL, SND_SOC_NOPM, 0, 0, - wsa881x_rdac_event, - SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | - SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_MUX("WSA_RDAC", SND_SOC_NOPM, 0, 0, - rdac_mux), - - SND_SOC_DAPM_PGA_S("WSA_SPKR PGA", 1, SND_SOC_NOPM, 0, 0, - wsa881x_spkr_pa_event, - SND_SOC_DAPM_PRE_PMU | - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | - SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_OUTPUT("WSA_SPKR"), -}; - -static const struct snd_soc_dapm_route wsa881x_audio_map[] = { - {"WSA_RDAC", "Switch", "WSA_IN"}, - {"RDAC Analog", NULL, "WSA_RDAC"}, - {"WSA_SPKR PGA", NULL, "RDAC Analog"}, - {"WSA_SPKR", NULL, "WSA_SPKR PGA"}, -}; - - -static int wsa881x_startup(struct wsa881x_pdata *pdata) -{ - int ret = 0; - struct snd_soc_codec *codec = pdata->codec; - struct snd_soc_card *card = codec->component.card; - - pr_debug("%s(): wsa startup, enable_cnt:%d\n", __func__, - pdata->enable_cnt); - - if (pdata->enable_cnt++ > 0) - return 0; - ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_clk"); - if (ret) { - pr_err("%s: gpio set cannot be activated %s\n", - __func__, "wsa_clk"); - return ret; - } - if (pdata->enable_mclk) { - ret = pdata->enable_mclk(card, true); - if (ret < 0) { - dev_err_ratelimited(codec->dev, - "%s: mclk enable failed %d\n", - __func__, ret); - return ret; - } - } - ret = wsa881x_reset(pdata, true); - return ret; -} - -static int wsa881x_shutdown(struct wsa881x_pdata *pdata) -{ - int ret = 0, reg; - struct snd_soc_codec *codec = pdata->codec; - struct snd_soc_card *card = codec->component.card; - - pr_debug("%s(): wsa shutdown, enable_cnt:%d\n", __func__, - pdata->enable_cnt); - if (--pdata->enable_cnt > 0) - return 0; - ret = wsa881x_reset(pdata, false); - if (ret) { - pr_err("%s: wsa reset failed suspend %d\n", - __func__, ret); - return ret; - } - - if (pdata->enable_mclk) { - ret = pdata->enable_mclk(card, false); - if (ret < 0) { - pr_err("%s: mclk disable failed %d\n", - __func__, ret); - return ret; - } - } - - ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_clk"); - if (ret) { - pr_err("%s: gpio set cannot be suspended %s\n", - __func__, "wsa_clk"); - return ret; - } - if (pdata->codec) { - /* restore defaults to cache */ - for (reg = 0; reg < ARRAY_SIZE(wsa881x_ana_reg_defaults); - reg++) { - if (wsa881x_ana_reg_readable[reg]) - snd_soc_cache_write(pdata->codec, - wsa881x_ana_reg_defaults[reg].reg, - wsa881x_ana_reg_defaults[reg].def); - } - } - return 0; -} - -static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec, - bool enable) -{ - int ret = 0; - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - if (enable) { - ret = wsa881x_startup(wsa881x); - if (ret < 0) { - dev_err_ratelimited(codec->dev, - "%s: failed to startup\n", __func__); - return ret; - } - } - wsa881x_clk_ctrl(codec, enable); - wsa881x_bandgap_ctrl(codec, enable); - if (!enable) { - ret = wsa881x_shutdown(wsa881x); - if (ret < 0) - dev_err_ratelimited(codec->dev, - "%s: failed to shutdown\n", __func__); - } - return ret; -} - -static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec, - struct wsa_temp_register *wsa_temp_reg) -{ - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - int ret = 0; - - if (!wsa881x) { - dev_err(codec->dev, "%s: wsa881x is NULL\n", __func__); - return -EINVAL; - } - ret = wsa881x_resource_acquire(codec, true); - if (ret) { - dev_err_ratelimited(codec->dev, - "%s: resource acquire fail\n", __func__); - return ret; - } - - if (WSA881X_IS_2_0(wsa881x->version)) { - snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00); - wsa_temp_reg->dmeas_msb = snd_soc_read(codec, WSA881X_TEMP_MSB); - wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, WSA881X_TEMP_LSB); - snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x01); - } else { - wsa_temp_reg->dmeas_msb = snd_soc_read(codec, - WSA881X_TEMP_DOUT_MSB); - wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, - WSA881X_TEMP_DOUT_LSB); - } - wsa_temp_reg->d1_msb = snd_soc_read(codec, WSA881X_OTP_REG_1); - wsa_temp_reg->d1_lsb = snd_soc_read(codec, WSA881X_OTP_REG_2); - wsa_temp_reg->d2_msb = snd_soc_read(codec, WSA881X_OTP_REG_3); - wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4); - - ret = wsa881x_resource_acquire(codec, false); - if (ret) - dev_err_ratelimited(codec->dev, - "%s: resource release fail\n", __func__); - - return ret; -} - -static int wsa881x_probe(struct snd_soc_codec *codec) -{ - struct i2c_client *client; - int ret = 0; - int wsa881x_index = 0; - struct snd_soc_dapm_context *dapm = &codec->dapm; - char *widget_name = NULL; - struct snd_soc_card *card = codec->component.card; - struct snd_soc_codec_conf *codec_conf = card->codec_conf; - - client = dev_get_drvdata(codec->dev); - ret = wsa881x_i2c_get_client_index(client, &wsa881x_index); - if (ret != 0) { - dev_err(&client->dev, "%s: I2C get codec I2C\n" - "client failed\n", __func__); - return ret; - } - mutex_init(&wsa_pdata[wsa881x_index].bg_lock); - mutex_init(&wsa_pdata[wsa881x_index].res_lock); - snprintf(wsa_pdata[wsa881x_index].tz_pdata.name, 100, "%s", - wsa_tz_names[wsa881x_index]); - wsa_pdata[wsa881x_index].codec = codec; - wsa_pdata[wsa881x_index].spk_pa_gain = SPK_GAIN_12DB; - wsa_pdata[wsa881x_index].codec = codec; - wsa_pdata[wsa881x_index].tz_pdata.codec = codec; - wsa_pdata[wsa881x_index].tz_pdata.wsa_temp_reg_read = - wsa881x_temp_reg_read; - snd_soc_codec_set_drvdata(codec, &wsa_pdata[wsa881x_index]); - wsa881x_init_thermal(&wsa_pdata[wsa881x_index].tz_pdata); - INIT_DELAYED_WORK(&wsa_pdata[wsa881x_index].ocp_ctl_work, - wsa881x_ocp_ctl_work); - - if (codec_conf->name_prefix) { - widget_name = kcalloc(WIDGET_NAME_MAX_SIZE, sizeof(char), - GFP_KERNEL); - if (!widget_name) - return -ENOMEM; - - snprintf(widget_name, WIDGET_NAME_MAX_SIZE, - "%s WSA_SPKR", codec_conf->name_prefix); - snd_soc_dapm_ignore_suspend(dapm, widget_name); - snprintf(widget_name, WIDGET_NAME_MAX_SIZE, - "%s WSA_IN", codec_conf->name_prefix); - snd_soc_dapm_ignore_suspend(dapm, widget_name); - kfree(widget_name); - } else { - snd_soc_dapm_ignore_suspend(dapm, "WSA_SPKR"); - snd_soc_dapm_ignore_suspend(dapm, "WSA_IN"); - } - - snd_soc_dapm_sync(dapm); - return 0; -} - -static int wsa881x_remove(struct snd_soc_codec *codec) -{ - struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec); - - if (wsa881x->tz_pdata.tz_dev) - wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev); - - mutex_destroy(&wsa881x->bg_lock); - mutex_destroy(&wsa881x->res_lock); - return 0; -} - -static struct snd_soc_codec_driver soc_codec_dev_wsa881x = { - .probe = wsa881x_probe, - .remove = wsa881x_remove, - - .read = wsa881x_i2c_read, - .write = wsa881x_i2c_write, - - .reg_cache_size = WSA881X_CACHE_SIZE, - .reg_cache_default = wsa881x_ana_reg_defaults, - .reg_word_size = 1, - - .component_driver = { - .controls = wsa881x_snd_controls, - .num_controls = ARRAY_SIZE(wsa881x_snd_controls), - .dapm_widgets = wsa881x_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets), - .dapm_routes = wsa881x_audio_map, - .num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map), - }, -}; - -static int wsa881x_reset(struct wsa881x_pdata *pdata, bool enable) -{ - int ret = 0; - - /* - * shutdown the GPIOs WSA_EN, WSA_MCLK, regulators - * and restore defaults in soc cache when shutdown. - * Enable regulators, GPIOs WSA_MCLK, WSA_EN when powerup. - */ - if (enable) { - if (pdata->wsa_active) - return 0; - ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_reset"); - if (ret) { - pr_err("%s: gpio set cannot be activated %s\n", - __func__, "wsa_reset"); - return ret; - } - ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_reset"); - if (ret) { - pr_err("%s: gpio set cannot be suspended(powerup) %s\n", - __func__, "wsa_reset"); - return ret; - } - ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_reset"); - if (ret) { - pr_err("%s: gpio set cannot be activated %s\n", - __func__, "wsa_reset"); - return ret; - } - pdata->wsa_active = true; - } else { - if (!pdata->wsa_active) - return 0; - ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_reset"); - if (ret) { - pr_err("%s: gpio set cannot be suspended %s\n", - __func__, "wsa_reset"); - return ret; - } - pdata->wsa_active = false; - } - return ret; -} - -int wsa881x_get_client_index(void) -{ - return wsa881x_i2c_addr; -} -EXPORT_SYMBOL(wsa881x_get_client_index); - -int wsa881x_get_probing_count(void) -{ - return wsa881x_probing_count; -} -EXPORT_SYMBOL(wsa881x_get_probing_count); - -int wsa881x_get_presence_count(void) -{ - return wsa881x_presence_count; -} -EXPORT_SYMBOL(wsa881x_get_presence_count); - -int wsa881x_set_mclk_callback( - int (*enable_mclk_callback)(struct snd_soc_card *, bool)) -{ - int i; - - for (i = 0; i < MAX_WSA881X_DEVICE; i++) { - if (wsa_pdata[i].status == WSA881X_STATUS_I2C) - wsa_pdata[i].enable_mclk = enable_mclk_callback; - } - return 0; -} -EXPORT_SYMBOL(wsa881x_set_mclk_callback); - -static int check_wsa881x_presence(struct i2c_client *client) -{ - int ret = 0; - int wsa881x_index = 0; - - ret = wsa881x_i2c_get_client_index(client, &wsa881x_index); - if (ret != 0) { - dev_err(&client->dev, "%s: I2C get codec I2C\n" - "client failed\n", __func__); - return ret; - } - ret = wsa881x_i2c_read_device(&wsa_pdata[wsa881x_index], - WSA881X_CDC_RST_CTL); - if (ret < 0) { - dev_err(&client->dev, "failed to read wsa881x with addr %x\n", - client->addr); - return ret; - } - ret = wsa881x_i2c_write_device(&wsa_pdata[wsa881x_index], - WSA881X_CDC_RST_CTL, 0x01); - if (ret < 0) { - dev_err(&client->dev, "failed write addr %x reg:0x5 val:0x1\n", - client->addr); - return ret; - } - /* allow 20ms before trigger next write to verify WSA881x presence */ - msleep(20); - ret = wsa881x_i2c_write_device(&wsa_pdata[wsa881x_index], - WSA881X_CDC_RST_CTL, 0x00); - if (ret < 0) { - dev_err(&client->dev, "failed write addr %x reg:0x5 val:0x0\n", - client->addr); - return ret; - } - return ret; -} - -static int wsa881x_populate_dt_pdata(struct device *dev) -{ - int ret = 0; - - /* reading the gpio configurations from dtsi file */ - if (!pinctrl_init) { - ret = msm_gpioset_initialize(CLIENT_WSA_BONGO_1, dev); - if (ret < 0) { - dev_err(dev, - "%s: error reading dtsi files%d\n", __func__, ret); - goto err; - } - pinctrl_init = true; - } -err: - return ret; -} - -static int wsa881x_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - int ret = 0; - int wsa881x_index = 0; - struct wsa881x_pdata *pdata = NULL; - - ret = wsa881x_i2c_get_client_index(client, &wsa881x_index); - if (ret != 0) { - dev_err(&client->dev, "%s: I2C get codec I2C\n" - "client failed\n", __func__); - return ret; - } - - pdata = &wsa_pdata[wsa881x_index]; - - if ((client->addr == WSA881X_I2C_SPK0_SLAVE1_ADDR || - client->addr == WSA881X_I2C_SPK1_SLAVE1_ADDR) && - (pdata->status == WSA881X_STATUS_PROBING)) - return ret; - - if (pdata->status == WSA881X_STATUS_I2C) { - dev_dbg(&client->dev, "%s:probe for other slaves\n" - "devices of codec I2C slave Addr = %x\n", - __func__, client->addr); - - dev_dbg(&client->dev, "%s:wsa_idx = %d SLAVE = %d\n", - __func__, wsa881x_index, WSA881X_ANALOG_SLAVE); - pdata->regmap[WSA881X_ANALOG_SLAVE] = - devm_regmap_init_i2c( - client, - &wsa881x_ana_regmap_config[WSA881X_ANALOG_SLAVE]); - regcache_cache_bypass(pdata->regmap[WSA881X_ANALOG_SLAVE], - true); - if (IS_ERR(pdata->regmap[WSA881X_ANALOG_SLAVE])) { - ret = PTR_ERR(pdata->regmap[WSA881X_ANALOG_SLAVE]); - dev_err(&client->dev, - "%s: regmap_init failed %d\n", - __func__, ret); - } - client->dev.platform_data = pdata; - i2c_set_clientdata(client, pdata); - pdata->client[WSA881X_ANALOG_SLAVE] = client; - if (pdata->version == WSA881X_2_0) - wsa881x_update_regmap_2_0( - pdata->regmap[WSA881X_ANALOG_SLAVE], - WSA881X_ANALOG_SLAVE); - - return ret; - } else if (pdata->status == WSA881X_STATUS_PROBING) { - pdata->index = wsa881x_index; - if (client->dev.of_node) { - dev_dbg(&client->dev, "%s:Platform data\n" - "from device tree\n", __func__); - ret = wsa881x_populate_dt_pdata(&client->dev); - if (ret < 0) { - dev_err(&client->dev, - "%s: Fail to obtain pdata from device tree\n", - __func__); - ret = -EINVAL; - goto err; - } - client->dev.platform_data = pdata; - } else { - dev_dbg(&client->dev, "%s:Platform data from\n" - "board file\n", __func__); - pdata = client->dev.platform_data; - } - if (!pdata) { - dev_dbg(&client->dev, "no platform data?\n"); - ret = -EINVAL; - goto err; - } - i2c_set_clientdata(client, pdata); - dev_set_drvdata(&client->dev, client); - - pdata->regmap[WSA881X_DIGITAL_SLAVE] = - devm_regmap_init_i2c( - client, - &wsa881x_ana_regmap_config[WSA881X_DIGITAL_SLAVE]); - regcache_cache_bypass(pdata->regmap[WSA881X_DIGITAL_SLAVE], - true); - if (IS_ERR(pdata->regmap[WSA881X_DIGITAL_SLAVE])) { - ret = PTR_ERR(pdata->regmap[WSA881X_DIGITAL_SLAVE]); - dev_err(&client->dev, "%s: regmap_init failed %d\n", - __func__, ret); - goto err; - } - /* bus reset sequence */ - ret = wsa881x_reset(pdata, true); - if (ret < 0) { - dev_err(&client->dev, "%s: WSA enable Failed %d\n", - __func__, ret); - goto err; - } - pdata->client[WSA881X_DIGITAL_SLAVE] = client; - pdata->regmap_flag = true; - ret = check_wsa881x_presence(client); - if (ret < 0) { - dev_err(&client->dev, - "failed to ping wsa with addr:%x, ret = %d\n", - client->addr, ret); - wsa881x_probing_count++; - goto err1; - } - pdata->version = wsa881x_i2c_read_device(pdata, - WSA881X_CHIP_ID1); - pr_debug("%s: wsa881x version: %d\n", __func__, pdata->version); - if (pdata->version == WSA881X_2_0) { - wsa881x_update_reg_defaults_2_0(); - wsa881x_update_regmap_2_0( - pdata->regmap[WSA881X_DIGITAL_SLAVE], - WSA881X_DIGITAL_SLAVE); - } - wsa881x_presence_count++; - wsa881x_probing_count++; - ret = snd_soc_register_codec(&client->dev, - &soc_codec_dev_wsa881x, - NULL, 0); - if (ret < 0) - goto err1; - pdata->status = WSA881X_STATUS_I2C; - } -err1: - wsa881x_reset(pdata, false); -err: - return 0; -} - -static int wsa881x_i2c_remove(struct i2c_client *client) -{ - struct wsa881x_pdata *wsa881x = i2c_get_clientdata(client); - - snd_soc_unregister_codec(&client->dev); - i2c_set_clientdata(client, NULL); - kfree(wsa881x); - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int wsa881x_i2c_suspend(struct device *dev) -{ - pr_debug("%s: system suspend\n", __func__); - return 0; -} - -static int wsa881x_i2c_resume(struct device *dev) -{ - pr_debug("%s: system resume\n", __func__); - return 0; -} - -static const struct dev_pm_ops wsa881x_i2c_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(wsa881x_i2c_suspend, wsa881x_i2c_resume) -}; -#endif /* CONFIG_PM_SLEEP */ - -static const struct i2c_device_id wsa881x_i2c_id[] = { - {"wsa881x-i2c-dev", WSA881X_I2C_SPK0_SLAVE0_ADDR}, - {"wsa881x-i2c-dev", WSA881X_I2C_SPK0_SLAVE1_ADDR}, - {"wsa881x-i2c-dev", WSA881X_I2C_SPK1_SLAVE0_ADDR}, - {"wsa881x-i2c-dev", WSA881X_I2C_SPK1_SLAVE1_ADDR}, - {} -}; - -MODULE_DEVICE_TABLE(i2c, wsa881x_i2c_id); - - -static const struct of_device_id msm_match_table[] = { - {.compatible = "qcom,wsa881x-i2c-codec"}, - {} -}; -MODULE_DEVICE_TABLE(of, msm_match_table); - -static struct i2c_driver wsa881x_codec_driver = { - .driver = { - .name = "wsa881x-i2c-codec", - .owner = THIS_MODULE, -#ifdef CONFIG_PM_SLEEP - .pm = &wsa881x_i2c_pm_ops, -#endif - .of_match_table = msm_match_table, - }, - .id_table = wsa881x_i2c_id, - .probe = wsa881x_i2c_probe, - .remove = wsa881x_i2c_remove, -}; - -static int __init wsa881x_codec_init(void) -{ - int i = 0; - - for (i = 0; i < MAX_WSA881X_DEVICE; i++) - wsa_pdata[i].status = WSA881X_STATUS_PROBING; - return i2c_add_driver(&wsa881x_codec_driver); -} -module_init(wsa881x_codec_init); - -static void __exit wsa881x_codec_exit(void) -{ - i2c_del_driver(&wsa881x_codec_driver); -} - -module_exit(wsa881x_codec_exit); - -MODULE_DESCRIPTION("WSA881x Codec driver"); -MODULE_LICENSE("GPL v2"); diff --git a/asoc/codecs/wsa881x-analog.h b/asoc/codecs/wsa881x-analog.h deleted file mode 100644 index a2ef2a284c23..000000000000 --- a/asoc/codecs/wsa881x-analog.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (c) 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 _WSA881X_H -#define _WSA881X_H - -#include -#include "wsa881x-registers-analog.h" -#include - -#define WSA881X_I2C_SPK0_SLAVE0_ADDR 0x0E -#define WSA881X_I2C_SPK0_SLAVE1_ADDR 0x44 -#define WSA881X_I2C_SPK1_SLAVE0_ADDR 0x0F -#define WSA881X_I2C_SPK1_SLAVE1_ADDR 0x45 - -#define WSA881X_I2C_SPK0_SLAVE0 0 -#define WSA881X_I2C_SPK1_SLAVE0 1 -#define MAX_WSA881X_DEVICE 2 -#define WSA881X_DIGITAL_SLAVE 0 -#define WSA881X_ANALOG_SLAVE 1 - -enum { - WSA881X_1_X = 0, - WSA881X_2_0, -}; - -#define WSA881X_IS_2_0(ver) \ - ((ver == WSA881X_2_0) ? 1 : 0) - -extern const u8 wsa881x_ana_reg_readable[WSA881X_CACHE_SIZE]; -extern struct reg_default wsa881x_ana_reg_defaults[WSA881X_CACHE_SIZE]; -extern struct regmap_config wsa881x_ana_regmap_config[2]; -int wsa881x_get_client_index(void); -int wsa881x_get_probing_count(void); -int wsa881x_get_presence_count(void); -int wsa881x_set_mclk_callback( - int (*enable_mclk_callback)(struct snd_soc_card *, bool)); -void wsa881x_update_reg_defaults_2_0(void); -void wsa881x_update_regmap_2_0(struct regmap *regmap, int flag); - -#endif /* _WSA881X_H */ diff --git a/asoc/codecs/wsa881x-irq.c b/asoc/codecs/wsa881x-irq.c deleted file mode 100644 index 9afbd92b8f72..000000000000 --- a/asoc/codecs/wsa881x-irq.c +++ /dev/null @@ -1,610 +0,0 @@ -/* Copyright (c) 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "wsa881x-irq.h" -#include "wsa881x-registers-analog.h" - -#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE)) -#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE) - - -#define WSA_MAX_NUM_IRQS 8 - -#ifndef NO_IRQ -#define NO_IRQ (-1) -#endif - -static int virq_to_phyirq( - struct wsa_resource *wsa_res, int virq); -static int phyirq_to_virq( - struct wsa_resource *wsa_res, int irq); -static unsigned int wsa_irq_get_upstream_irq( - struct wsa_resource *wsa_res); -static void wsa_irq_put_upstream_irq( - struct wsa_resource *wsa_res); -static int wsa_map_irq( - struct wsa_resource *wsa_res, int irq); - -static struct snd_soc_codec *ptr_codec; - -/** - * wsa_set_codec() - to update codec pointer - * @codec: codec pointer. - * - * To update the codec pointer, which is used to read/write - * wsa register. - * - * Return: void. - */ -void wsa_set_codec(struct snd_soc_codec *codec) -{ - if (codec == NULL) { - pr_err("%s: codec pointer is NULL\n", __func__); - ptr_codec = NULL; - return; - } - ptr_codec = codec; - /* Initialize interrupt mask and level registers */ - snd_soc_write(codec, WSA881X_INTR_LEVEL, 0x8F); - snd_soc_write(codec, WSA881X_INTR_MASK, 0x8F); -} - -static void wsa_irq_lock(struct irq_data *data) -{ - struct wsa_resource *wsa_res = - irq_data_get_irq_chip_data(data); - - if (wsa_res == NULL) { - pr_err("%s: wsa_res pointer is NULL\n", __func__); - return; - } - mutex_lock(&wsa_res->irq_lock); -} - -static void wsa_irq_sync_unlock(struct irq_data *data) -{ - struct wsa_resource *wsa_res = - irq_data_get_irq_chip_data(data); - - if (wsa_res == NULL) { - pr_err("%s: wsa_res pointer is NULL\n", __func__); - return; - } - if (wsa_res->codec == NULL) { - pr_err("%s: codec pointer not registered\n", __func__); - if (ptr_codec == NULL) { - pr_err("%s: did not receive valid codec pointer\n", - __func__); - goto unlock; - } else { - wsa_res->codec = ptr_codec; - } - } - - /* - * If there's been a change in the mask write it back - * to the hardware. - */ - if (wsa_res->irq_masks_cur != - wsa_res->irq_masks_cache) { - - wsa_res->irq_masks_cache = - wsa_res->irq_masks_cur; - snd_soc_write(wsa_res->codec, - WSA881X_INTR_MASK, - wsa_res->irq_masks_cur); - } -unlock: - mutex_unlock(&wsa_res->irq_lock); -} - -static void wsa_irq_enable(struct irq_data *data) -{ - struct wsa_resource *wsa_res = - irq_data_get_irq_chip_data(data); - int wsa_irq; - - if (wsa_res == NULL) { - pr_err("%s: wsa_res pointer is NULL\n", __func__); - return; - } - wsa_irq = virq_to_phyirq(wsa_res, data->irq); - pr_debug("%s: wsa_irq = %d\n", __func__, wsa_irq); - wsa_res->irq_masks_cur &= - ~(BYTE_BIT_MASK(wsa_irq)); -} - -static void wsa_irq_disable(struct irq_data *data) -{ - struct wsa_resource *wsa_res = - irq_data_get_irq_chip_data(data); - int wsa_irq; - - if (wsa_res == NULL) { - pr_err("%s: wsa_res pointer is NULL\n", __func__); - return; - } - wsa_irq = virq_to_phyirq(wsa_res, data->irq); - pr_debug("%s: wsa_irq = %d\n", __func__, wsa_irq); - wsa_res->irq_masks_cur - |= BYTE_BIT_MASK(wsa_irq); -} - -static void wsa_irq_ack(struct irq_data *data) -{ - int wsa_irq = 0; - struct wsa_resource *wsa_res = - irq_data_get_irq_chip_data(data); - - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return; - } - wsa_irq = virq_to_phyirq(wsa_res, data->irq); - pr_debug("%s: IRQ_ACK called for WCD9XXX IRQ: %d\n", - __func__, wsa_irq); -} - -static void wsa_irq_mask(struct irq_data *d) -{ - /* do nothing but required as linux calls irq_mask without NULL check */ -} - -static struct irq_chip wsa_irq_chip = { - .name = "wsa", - .irq_bus_lock = wsa_irq_lock, - .irq_bus_sync_unlock = wsa_irq_sync_unlock, - .irq_disable = wsa_irq_disable, - .irq_enable = wsa_irq_enable, - .irq_mask = wsa_irq_mask, - .irq_ack = wsa_irq_ack, -}; - -static irqreturn_t wsa_irq_thread(int irq, void *data) -{ - struct wsa_resource *wsa_res = data; - int i; - u8 status; - - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return IRQ_HANDLED; - } - if (wsa_res->codec == NULL) { - pr_err("%s: codec pointer not registered\n", __func__); - if (ptr_codec == NULL) { - pr_err("%s: did not receive valid codec pointer\n", - __func__); - return IRQ_HANDLED; - } - wsa_res->codec = ptr_codec; - } - status = snd_soc_read(wsa_res->codec, WSA881X_INTR_STATUS); - /* Apply masking */ - status &= ~wsa_res->irq_masks_cur; - - for (i = 0; i < wsa_res->num_irqs; i++) { - if (status & BYTE_BIT_MASK(i)) { - mutex_lock(&wsa_res->nested_irq_lock); - handle_nested_irq(phyirq_to_virq(wsa_res, i)); - mutex_unlock(&wsa_res->nested_irq_lock); - } - } - - return IRQ_HANDLED; -} - -/** - * wsa_free_irq() - to free an interrupt - * @irq: interrupt number. - * @data: pointer to wsa resource. - * - * To free already requested interrupt. - * - * Return: void. - */ -void wsa_free_irq(int irq, void *data) -{ - struct wsa_resource *wsa_res = data; - - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return; - } - free_irq(phyirq_to_virq(wsa_res, irq), data); -} - -/** - * wsa_enable_irq() - to enable an interrupt - * @wsa_res: pointer to wsa resource. - * @irq: interrupt number. - * - * This function is to enable an interrupt. - * - * Return: void. - */ -void wsa_enable_irq(struct wsa_resource *wsa_res, int irq) -{ - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return; - } - enable_irq(phyirq_to_virq(wsa_res, irq)); -} - -/** - * wsa_disable_irq() - to disable an interrupt - * @wsa_res: pointer to wsa resource. - * @irq: interrupt number. - * - * To disable an interrupt without waiting for executing - * handler to complete. - * - * Return: void. - */ -void wsa_disable_irq(struct wsa_resource *wsa_res, int irq) -{ - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return; - } - disable_irq_nosync(phyirq_to_virq(wsa_res, irq)); -} - -/** - * wsa_disable_irq_sync() - to disable an interrupt - * @wsa_res: pointer to wsa resource. - * @irq: interrupt number. - * - * To disable an interrupt, wait for executing IRQ - * handler to complete. - * - * Return: void. - */ -void wsa_disable_irq_sync( - struct wsa_resource *wsa_res, int irq) -{ - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return; - } - disable_irq(phyirq_to_virq(wsa_res, irq)); -} - -static int wsa_irq_setup_downstream_irq(struct wsa_resource *wsa_res) -{ - int irq, virq, ret; - - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return -EINVAL; - } - pr_debug("%s: enter\n", __func__); - - for (irq = 0; irq < wsa_res->num_irqs; irq++) { - /* Map OF irq */ - virq = wsa_map_irq(wsa_res, irq); - pr_debug("%s: irq %d -> %d\n", __func__, irq, virq); - if (virq == NO_IRQ) { - pr_err("%s, No interrupt specifier for irq %d\n", - __func__, irq); - return NO_IRQ; - } - - ret = irq_set_chip_data(virq, wsa_res); - if (ret) { - pr_err("%s: Failed to configure irq %d (%d)\n", - __func__, irq, ret); - return ret; - } - - if (wsa_res->irq_level_high[irq]) - irq_set_chip_and_handler(virq, &wsa_irq_chip, - handle_level_irq); - else - irq_set_chip_and_handler(virq, &wsa_irq_chip, - handle_edge_irq); - - irq_set_nested_thread(virq, 1); - } - - pr_debug("%s: leave\n", __func__); - - return 0; -} - -static int wsa_irq_init(struct wsa_resource *wsa_res) -{ - int i, ret; - - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return -EINVAL; - } - mutex_init(&wsa_res->irq_lock); - mutex_init(&wsa_res->nested_irq_lock); - - wsa_res->irq = wsa_irq_get_upstream_irq(wsa_res); - if (!wsa_res->irq) { - pr_warn("%s: irq driver is not yet initialized\n", __func__); - mutex_destroy(&wsa_res->irq_lock); - mutex_destroy(&wsa_res->nested_irq_lock); - return -EPROBE_DEFER; - } - pr_debug("%s: probed irq %d\n", __func__, wsa_res->irq); - - /* Setup downstream IRQs */ - ret = wsa_irq_setup_downstream_irq(wsa_res); - if (ret) { - pr_err("%s: Failed to setup downstream IRQ\n", __func__); - goto fail_irq_init; - } - - /* mask all the interrupts */ - for (i = 0; i < wsa_res->num_irqs; i++) { - wsa_res->irq_masks_cur |= BYTE_BIT_MASK(i); - wsa_res->irq_masks_cache |= BYTE_BIT_MASK(i); - } - - ret = request_threaded_irq(wsa_res->irq, NULL, wsa_irq_thread, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, - "wsa", wsa_res); - if (ret != 0) { - dev_err(wsa_res->dev, "Failed to request IRQ %d: %d\n", - wsa_res->irq, ret); - } else { - ret = enable_irq_wake(wsa_res->irq); - if (ret) { - dev_err(wsa_res->dev, - "Failed to set wake interrupt on IRQ %d: %d\n", - wsa_res->irq, ret); - free_irq(wsa_res->irq, wsa_res); - } - } - - if (ret) - goto fail_irq_init; - - return ret; - -fail_irq_init: - dev_err(wsa_res->dev, - "%s: Failed to init wsa irq\n", __func__); - wsa_irq_put_upstream_irq(wsa_res); - mutex_destroy(&wsa_res->irq_lock); - mutex_destroy(&wsa_res->nested_irq_lock); - return ret; -} - -/** - * wsa_request_irq() - to request/register an interrupt - * @wsa_res: pointer to wsa_resource. - * @irq: interrupt number. - * @handler: interrupt handler function pointer. - * @name: interrupt name. - * @data: device info. - * - * Convert physical irq to virtual irq and then - * reguest for threaded handler. - * - * Return: Retuns success/failure. - */ -int wsa_request_irq(struct wsa_resource *wsa_res, - int irq, irq_handler_t handler, - const char *name, void *data) -{ - int virq; - - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return -EINVAL; - } - virq = phyirq_to_virq(wsa_res, irq); - - /* - * ARM needs us to explicitly flag the IRQ as valid - * and will set them noprobe when we do so. - */ -#if defined(CONFIG_ARM) || defined(CONFIG_ARM64) - set_irq_flags(virq, IRQF_VALID); -#else - set_irq_noprobe(virq); -#endif - - return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING, - name, data); -} - -/** - * wsa_irq_exit() - to disable/clear interrupt/resources - * @wsa_res: pointer to wsa_resource - * - * Disable and free the interrupts and then release resources. - * - * Return: void. - */ -void wsa_irq_exit(struct wsa_resource *wsa_res) -{ - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return; - } - dev_dbg(wsa_res->dev, "%s: Cleaning up irq %d\n", __func__, - wsa_res->irq); - - if (wsa_res->irq) { - disable_irq_wake(wsa_res->irq); - free_irq(wsa_res->irq, wsa_res); - /* Release parent's of node */ - wsa_irq_put_upstream_irq(wsa_res); - } - mutex_destroy(&wsa_res->irq_lock); - mutex_destroy(&wsa_res->nested_irq_lock); -} - -static int phyirq_to_virq(struct wsa_resource *wsa_res, int offset) -{ - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return -EINVAL; - } - return irq_linear_revmap(wsa_res->domain, offset); -} - -static int virq_to_phyirq(struct wsa_resource *wsa_res, int virq) -{ - struct irq_data *irq_data = irq_get_irq_data(virq); - - if (unlikely(!irq_data)) { - pr_err("%s: irq_data is NULL\n", __func__); - return -EINVAL; - } - return irq_data->hwirq; -} - -static unsigned int wsa_irq_get_upstream_irq(struct wsa_resource *wsa_res) -{ - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return -EINVAL; - } - return wsa_res->irq; -} - -static void wsa_irq_put_upstream_irq(struct wsa_resource *wsa_res) -{ - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return; - } - /* Hold parent's of node */ - of_node_put(wsa_res->dev->of_node); -} - -static int wsa_map_irq(struct wsa_resource *wsa_res, int irq) -{ - if (wsa_res == NULL) { - pr_err("%s: wsa_res is NULL\n", __func__); - return -EINVAL; - } - return of_irq_to_resource(wsa_res->dev->of_node, irq, NULL); -} - -static int wsa_irq_probe(struct platform_device *pdev) -{ - int irq; - struct wsa_resource *wsa_res = NULL; - int ret = -EINVAL; - - irq = platform_get_irq_byname(pdev, "wsa-int"); - if (irq < 0) { - dev_err(&pdev->dev, "%s: Couldn't find wsa-int node(%d)\n", - __func__, irq); - return -EINVAL; - } - pr_debug("%s: node %s\n", __func__, pdev->name); - wsa_res = kzalloc(sizeof(*wsa_res), GFP_KERNEL); - if (!wsa_res) { - pr_err("%s: could not allocate memory\n", __func__); - return -ENOMEM; - } - /* - * wsa interrupt controller supports N to N irq mapping with - * single cell binding with irq numbers(offsets) only. - * Use irq_domain_simple_ops that has irq_domain_simple_map and - * irq_domain_xlate_onetwocell. - */ - wsa_res->dev = &pdev->dev; - wsa_res->domain = irq_domain_add_linear(wsa_res->dev->of_node, - WSA_MAX_NUM_IRQS, &irq_domain_simple_ops, - wsa_res); - if (!wsa_res->domain) { - dev_err(&pdev->dev, "%s: domain is NULL\n", __func__); - ret = -ENOMEM; - goto err; - } - wsa_res->dev = &pdev->dev; - - dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq); - wsa_res->irq = irq; - wsa_res->num_irq_regs = 1; - wsa_res->num_irqs = WSA_NUM_IRQS; - ret = wsa_irq_init(wsa_res); - if (ret < 0) { - dev_err(&pdev->dev, "%s: failed to do irq init %d\n", - __func__, ret); - goto err; - } - - return ret; -err: - kfree(wsa_res); - return ret; -} - -static int wsa_irq_remove(struct platform_device *pdev) -{ - struct irq_domain *domain; - struct wsa_resource *data; - - domain = irq_find_host(pdev->dev.of_node); - if (unlikely(!domain)) { - pr_err("%s: domain is NULL\n", __func__); - return -EINVAL; - } - data = (struct wsa_resource *)domain->host_data; - data->irq = 0; - - return 0; -} - -static const struct of_device_id of_match[] = { - { .compatible = "qcom,wsa-irq" }, - { } -}; - -static struct platform_driver wsa_irq_driver = { - .probe = wsa_irq_probe, - .remove = wsa_irq_remove, - .driver = { - .name = "wsa_intc", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(of_match), - }, -}; - -static int wsa_irq_drv_init(void) -{ - return platform_driver_register(&wsa_irq_driver); -} -subsys_initcall(wsa_irq_drv_init); - -static void wsa_irq_drv_exit(void) -{ - platform_driver_unregister(&wsa_irq_driver); -} -module_exit(wsa_irq_drv_exit); - -MODULE_DESCRIPTION("WSA881x IRQ driver"); -MODULE_LICENSE("GPL v2"); diff --git a/asoc/codecs/wsa881x-irq.h b/asoc/codecs/wsa881x-irq.h deleted file mode 100644 index 270eb917a666..000000000000 --- a/asoc/codecs/wsa881x-irq.h +++ /dev/null @@ -1,82 +0,0 @@ -/* Copyright (c) 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 __WSA881X_IRQ_H__ -#define __WSA881X_IRQ_H__ - -#include -#include -#include - -/** - * enum wsa_interrupts - wsa interrupt number - * @WSA_INT_SAF2WAR: Temp irq interrupt, from safe state to warning state. - * @WSA_INT_WAR2SAF: Temp irq interrupt, from warning state to safe state. - * @WSA_INT_DISABLE: Disable Temp sensor interrupts. - * @WSA_INT_OCP: OCP interrupt. - * @WSA_INT_CLIP: CLIP detect interrupt. - * @WSA_NUM_IRQS: MAX Interrupt number. - * - * WSA IRQ Interrupt numbers. - */ -enum wsa_interrupts { - WSA_INT_SAF2WAR = 0, - WSA_INT_WAR2SAF, - WSA_INT_DISABLE, - WSA_INT_OCP, - WSA_INT_CLIP, - WSA_NUM_IRQS, -}; - -/** - * struct wsa_resource - the basic wsa_resource structure - * @irq_lock: lock used by irq_chip functions. - * @nested_irq_lock: lock used while handling nested interrupts. - * @irq: interrupt number. - * @irq_masks_cur: current mask value to be written to mask registers. - * @irq_masks_cache: cached mask value. - * @num_irqs: number of supported interrupts. - * @num_irq_regs: number of irq registers. - * @parent: parent pointer. - * @dev: device pointer. - * @domain: irq domain pointer. - * codec: codec pointer. - * - * Contains required members used in wsa irq driver. - */ - -struct wsa_resource { - struct mutex irq_lock; - struct mutex nested_irq_lock; - unsigned int irq; - u8 irq_masks_cur; - u8 irq_masks_cache; - bool irq_level_high[8]; - int num_irqs; - int num_irq_regs; - void *parent; - struct device *dev; - struct irq_domain *domain; - struct snd_soc_codec *codec; -}; - -void wsa_set_codec(struct snd_soc_codec *codec); -void wsa_free_irq(int irq, void *data); -void wsa_enable_irq(struct wsa_resource *wsa_res, int irq); -void wsa_disable_irq(struct wsa_resource *wsa_res, int irq); -void wsa_disable_irq_sync(struct wsa_resource *wsa_res, int irq); -int wsa_request_irq(struct wsa_resource *wsa_res, - int irq, irq_handler_t handler, - const char *name, void *data); - -void wsa_irq_exit(struct wsa_resource *wsa_res); - -#endif /* __WSA881X_IRQ_H__ */ diff --git a/asoc/codecs/wsa881x-registers-analog.h b/asoc/codecs/wsa881x-registers-analog.h deleted file mode 100644 index a5ebf8e1aab4..000000000000 --- a/asoc/codecs/wsa881x-registers-analog.h +++ /dev/null @@ -1,206 +0,0 @@ -/* Copyright (c) 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 WSA881X_REGISTERS_H -#define WSA881X_REGISTERS_H - -#define WSA881X_DIGITAL_BASE 0x0000 -#define WSA881X_ANALOG_BASE 0x0100 - -#define WSA881X_CHIP_ID0 (WSA881X_DIGITAL_BASE+0x0000) -#define WSA881X_CHIP_ID1 (WSA881X_DIGITAL_BASE+0x0001) -#define WSA881X_CHIP_ID2 (WSA881X_DIGITAL_BASE+0x0002) -#define WSA881X_CHIP_ID3 (WSA881X_DIGITAL_BASE+0x0003) -#define WSA881X_BUS_ID (WSA881X_DIGITAL_BASE+0x0004) -#define WSA881X_CDC_RST_CTL (WSA881X_DIGITAL_BASE+0x0005) -#define WSA881X_CDC_TOP_CLK_CTL (WSA881X_DIGITAL_BASE+0x0006) -#define WSA881X_CDC_ANA_CLK_CTL (WSA881X_DIGITAL_BASE+0x0007) -#define WSA881X_CDC_DIG_CLK_CTL (WSA881X_DIGITAL_BASE+0x0008) -#define WSA881X_CLOCK_CONFIG (WSA881X_DIGITAL_BASE+0x0009) -#define WSA881X_ANA_CTL (WSA881X_DIGITAL_BASE+0x000A) -#define WSA881X_SWR_RESET_EN (WSA881X_DIGITAL_BASE+0x000B) -#define WSA881X_RESET_CTL (WSA881X_DIGITAL_BASE+0x000C) -#define WSA881X_TADC_VALUE_CTL (WSA881X_DIGITAL_BASE+0x000F) -#define WSA881X_TEMP_DETECT_CTL (WSA881X_DIGITAL_BASE+0x0010) -#define WSA881X_TEMP_MSB (WSA881X_DIGITAL_BASE+0x0011) -#define WSA881X_TEMP_LSB (WSA881X_DIGITAL_BASE+0x0012) -#define WSA881X_TEMP_CONFIG0 (WSA881X_DIGITAL_BASE+0x0013) -#define WSA881X_TEMP_CONFIG1 (WSA881X_DIGITAL_BASE+0x0014) -#define WSA881X_CDC_CLIP_CTL (WSA881X_DIGITAL_BASE+0x0015) -#define WSA881X_SDM_PDM9_LSB (WSA881X_DIGITAL_BASE+0x0016) -#define WSA881X_SDM_PDM9_MSB (WSA881X_DIGITAL_BASE+0x0017) -#define WSA881X_CDC_RX_CTL (WSA881X_DIGITAL_BASE+0x0018) -#define WSA881X_DEM_BYPASS_DATA0 (WSA881X_DIGITAL_BASE+0x0019) -#define WSA881X_DEM_BYPASS_DATA1 (WSA881X_DIGITAL_BASE+0x001A) -#define WSA881X_DEM_BYPASS_DATA2 (WSA881X_DIGITAL_BASE+0x001B) -#define WSA881X_DEM_BYPASS_DATA3 (WSA881X_DIGITAL_BASE+0x001C) -#define WSA881X_OTP_CTRL0 (WSA881X_DIGITAL_BASE+0x001D) -#define WSA881X_OTP_CTRL1 (WSA881X_DIGITAL_BASE+0x001E) -#define WSA881X_HDRIVE_CTL_GROUP1 (WSA881X_DIGITAL_BASE+0x001F) -#define WSA881X_INTR_MODE (WSA881X_DIGITAL_BASE+0x0020) -#define WSA881X_INTR_MASK (WSA881X_DIGITAL_BASE+0x0021) -#define WSA881X_INTR_STATUS (WSA881X_DIGITAL_BASE+0x0022) -#define WSA881X_INTR_CLEAR (WSA881X_DIGITAL_BASE+0x0023) -#define WSA881X_INTR_LEVEL (WSA881X_DIGITAL_BASE+0x0024) -#define WSA881X_INTR_SET (WSA881X_DIGITAL_BASE+0x0025) -#define WSA881X_INTR_TEST (WSA881X_DIGITAL_BASE+0x0026) -#define WSA881X_PDM_TEST_MODE (WSA881X_DIGITAL_BASE+0x0030) -#define WSA881X_ATE_TEST_MODE (WSA881X_DIGITAL_BASE+0x0031) -#define WSA881X_PIN_CTL_MODE (WSA881X_DIGITAL_BASE+0x0032) -#define WSA881X_PIN_CTL_OE (WSA881X_DIGITAL_BASE+0x0033) -#define WSA881X_PIN_WDATA_IOPAD (WSA881X_DIGITAL_BASE+0x0034) -#define WSA881X_PIN_STATUS (WSA881X_DIGITAL_BASE+0x0035) -#define WSA881X_DIG_DEBUG_MODE (WSA881X_DIGITAL_BASE+0x0037) -#define WSA881X_DIG_DEBUG_SEL (WSA881X_DIGITAL_BASE+0x0038) -#define WSA881X_DIG_DEBUG_EN (WSA881X_DIGITAL_BASE+0x0039) -#define WSA881X_SWR_HM_TEST1 (WSA881X_DIGITAL_BASE+0x003B) -#define WSA881X_SWR_HM_TEST2 (WSA881X_DIGITAL_BASE+0x003C) -#define WSA881X_TEMP_DETECT_DBG_CTL (WSA881X_DIGITAL_BASE+0x003D) -#define WSA881X_TEMP_DEBUG_MSB (WSA881X_DIGITAL_BASE+0x003E) -#define WSA881X_TEMP_DEBUG_LSB (WSA881X_DIGITAL_BASE+0x003F) -#define WSA881X_SAMPLE_EDGE_SEL (WSA881X_DIGITAL_BASE+0x0044) -#define WSA881X_IOPAD_CTL (WSA881X_DIGITAL_BASE+0x0045) -#define WSA881X_SPARE_0 (WSA881X_DIGITAL_BASE+0x0050) -#define WSA881X_SPARE_1 (WSA881X_DIGITAL_BASE+0x0051) -#define WSA881X_SPARE_2 (WSA881X_DIGITAL_BASE+0x0052) -#define WSA881X_OTP_REG_0 (WSA881X_DIGITAL_BASE+0x0080) -#define WSA881X_OTP_REG_1 (WSA881X_DIGITAL_BASE+0x0081) -#define WSA881X_OTP_REG_2 (WSA881X_DIGITAL_BASE+0x0082) -#define WSA881X_OTP_REG_3 (WSA881X_DIGITAL_BASE+0x0083) -#define WSA881X_OTP_REG_4 (WSA881X_DIGITAL_BASE+0x0084) -#define WSA881X_OTP_REG_5 (WSA881X_DIGITAL_BASE+0x0085) -#define WSA881X_OTP_REG_6 (WSA881X_DIGITAL_BASE+0x0086) -#define WSA881X_OTP_REG_7 (WSA881X_DIGITAL_BASE+0x0087) -#define WSA881X_OTP_REG_8 (WSA881X_DIGITAL_BASE+0x0088) -#define WSA881X_OTP_REG_9 (WSA881X_DIGITAL_BASE+0x0089) -#define WSA881X_OTP_REG_10 (WSA881X_DIGITAL_BASE+0x008A) -#define WSA881X_OTP_REG_11 (WSA881X_DIGITAL_BASE+0x008B) -#define WSA881X_OTP_REG_12 (WSA881X_DIGITAL_BASE+0x008C) -#define WSA881X_OTP_REG_13 (WSA881X_DIGITAL_BASE+0x008D) -#define WSA881X_OTP_REG_14 (WSA881X_DIGITAL_BASE+0x008E) -#define WSA881X_OTP_REG_15 (WSA881X_DIGITAL_BASE+0x008F) -#define WSA881X_OTP_REG_16 (WSA881X_DIGITAL_BASE+0x0090) -#define WSA881X_OTP_REG_17 (WSA881X_DIGITAL_BASE+0x0091) -#define WSA881X_OTP_REG_18 (WSA881X_DIGITAL_BASE+0x0092) -#define WSA881X_OTP_REG_19 (WSA881X_DIGITAL_BASE+0x0093) -#define WSA881X_OTP_REG_20 (WSA881X_DIGITAL_BASE+0x0094) -#define WSA881X_OTP_REG_21 (WSA881X_DIGITAL_BASE+0x0095) -#define WSA881X_OTP_REG_22 (WSA881X_DIGITAL_BASE+0x0096) -#define WSA881X_OTP_REG_23 (WSA881X_DIGITAL_BASE+0x0097) -#define WSA881X_OTP_REG_24 (WSA881X_DIGITAL_BASE+0x0098) -#define WSA881X_OTP_REG_25 (WSA881X_DIGITAL_BASE+0x0099) -#define WSA881X_OTP_REG_26 (WSA881X_DIGITAL_BASE+0x009A) -#define WSA881X_OTP_REG_27 (WSA881X_DIGITAL_BASE+0x009B) -#define WSA881X_OTP_REG_28 (WSA881X_DIGITAL_BASE+0x009C) -#define WSA881X_OTP_REG_29 (WSA881X_DIGITAL_BASE+0x009D) -#define WSA881X_OTP_REG_30 (WSA881X_DIGITAL_BASE+0x009E) -#define WSA881X_OTP_REG_31 (WSA881X_DIGITAL_BASE+0x009F) -#define WSA881X_OTP_REG_32 (WSA881X_DIGITAL_BASE+0x00A0) -#define WSA881X_OTP_REG_33 (WSA881X_DIGITAL_BASE+0x00A1) -#define WSA881X_OTP_REG_34 (WSA881X_DIGITAL_BASE+0x00A2) -#define WSA881X_OTP_REG_35 (WSA881X_DIGITAL_BASE+0x00A3) -#define WSA881X_OTP_REG_36 (WSA881X_DIGITAL_BASE+0x00A4) -#define WSA881X_OTP_REG_37 (WSA881X_DIGITAL_BASE+0x00A5) -#define WSA881X_OTP_REG_38 (WSA881X_DIGITAL_BASE+0x00A6) -#define WSA881X_OTP_REG_39 (WSA881X_DIGITAL_BASE+0x00A7) -#define WSA881X_OTP_REG_40 (WSA881X_DIGITAL_BASE+0x00A8) -#define WSA881X_OTP_REG_41 (WSA881X_DIGITAL_BASE+0x00A9) -#define WSA881X_OTP_REG_42 (WSA881X_DIGITAL_BASE+0x00AA) -#define WSA881X_OTP_REG_43 (WSA881X_DIGITAL_BASE+0x00AB) -#define WSA881X_OTP_REG_44 (WSA881X_DIGITAL_BASE+0x00AC) -#define WSA881X_OTP_REG_45 (WSA881X_DIGITAL_BASE+0x00AD) -#define WSA881X_OTP_REG_46 (WSA881X_DIGITAL_BASE+0x00AE) -#define WSA881X_OTP_REG_47 (WSA881X_DIGITAL_BASE+0x00AF) -#define WSA881X_OTP_REG_48 (WSA881X_DIGITAL_BASE+0x00B0) -#define WSA881X_OTP_REG_49 (WSA881X_DIGITAL_BASE+0x00B1) -#define WSA881X_OTP_REG_50 (WSA881X_DIGITAL_BASE+0x00B2) -#define WSA881X_OTP_REG_51 (WSA881X_DIGITAL_BASE+0x00B3) -#define WSA881X_OTP_REG_52 (WSA881X_DIGITAL_BASE+0x00B4) -#define WSA881X_OTP_REG_53 (WSA881X_DIGITAL_BASE+0x00B5) -#define WSA881X_OTP_REG_54 (WSA881X_DIGITAL_BASE+0x00B6) -#define WSA881X_OTP_REG_55 (WSA881X_DIGITAL_BASE+0x00B7) -#define WSA881X_OTP_REG_56 (WSA881X_DIGITAL_BASE+0x00B8) -#define WSA881X_OTP_REG_57 (WSA881X_DIGITAL_BASE+0x00B9) -#define WSA881X_OTP_REG_58 (WSA881X_DIGITAL_BASE+0x00BA) -#define WSA881X_OTP_REG_59 (WSA881X_DIGITAL_BASE+0x00BB) -#define WSA881X_OTP_REG_60 (WSA881X_DIGITAL_BASE+0x00BC) -#define WSA881X_OTP_REG_61 (WSA881X_DIGITAL_BASE+0x00BD) -#define WSA881X_OTP_REG_62 (WSA881X_DIGITAL_BASE+0x00BE) -#define WSA881X_OTP_REG_63 (WSA881X_DIGITAL_BASE+0x00BF) -/* Analog Register address space */ -#define WSA881X_BIAS_REF_CTRL (WSA881X_ANALOG_BASE+0x0000) -#define WSA881X_BIAS_TEST (WSA881X_ANALOG_BASE+0x0001) -#define WSA881X_BIAS_BIAS (WSA881X_ANALOG_BASE+0x0002) -#define WSA881X_TEMP_OP (WSA881X_ANALOG_BASE+0x0003) -#define WSA881X_TEMP_IREF_CTRL (WSA881X_ANALOG_BASE+0x0004) -#define WSA881X_TEMP_ISENS_CTRL (WSA881X_ANALOG_BASE+0x0005) -#define WSA881X_TEMP_CLK_CTRL (WSA881X_ANALOG_BASE+0x0006) -#define WSA881X_TEMP_TEST (WSA881X_ANALOG_BASE+0x0007) -#define WSA881X_TEMP_BIAS (WSA881X_ANALOG_BASE+0x0008) -#define WSA881X_TEMP_ADC_CTRL (WSA881X_ANALOG_BASE+0x0009) -#define WSA881X_TEMP_DOUT_MSB (WSA881X_ANALOG_BASE+0x000A) -#define WSA881X_TEMP_DOUT_LSB (WSA881X_ANALOG_BASE+0x000B) -#define WSA881X_ADC_EN_MODU_V (WSA881X_ANALOG_BASE+0x0010) -#define WSA881X_ADC_EN_MODU_I (WSA881X_ANALOG_BASE+0x0011) -#define WSA881X_ADC_EN_DET_TEST_V (WSA881X_ANALOG_BASE+0x0012) -#define WSA881X_ADC_EN_DET_TEST_I (WSA881X_ANALOG_BASE+0x0013) -#define WSA881X_ADC_SEL_IBIAS (WSA881X_ANALOG_BASE+0x0014) -#define WSA881X_ADC_EN_SEL_IBIAS (WSA881X_ANALOG_BASE+0x0015) -#define WSA881X_SPKR_DRV_EN (WSA881X_ANALOG_BASE+0x001A) -#define WSA881X_SPKR_DRV_GAIN (WSA881X_ANALOG_BASE+0x001B) -#define WSA881X_SPKR_DAC_CTL (WSA881X_ANALOG_BASE+0x001C) -#define WSA881X_SPKR_DRV_DBG (WSA881X_ANALOG_BASE+0x001D) -#define WSA881X_SPKR_PWRSTG_DBG (WSA881X_ANALOG_BASE+0x001E) -#define WSA881X_SPKR_OCP_CTL (WSA881X_ANALOG_BASE+0x001F) -#define WSA881X_SPKR_CLIP_CTL (WSA881X_ANALOG_BASE+0x0020) -#define WSA881X_SPKR_BBM_CTL (WSA881X_ANALOG_BASE+0x0021) -#define WSA881X_SPKR_MISC_CTL1 (WSA881X_ANALOG_BASE+0x0022) -#define WSA881X_SPKR_MISC_CTL2 (WSA881X_ANALOG_BASE+0x0023) -#define WSA881X_SPKR_BIAS_INT (WSA881X_ANALOG_BASE+0x0024) -#define WSA881X_SPKR_PA_INT (WSA881X_ANALOG_BASE+0x0025) -#define WSA881X_SPKR_BIAS_CAL (WSA881X_ANALOG_BASE+0x0026) -#define WSA881X_SPKR_BIAS_PSRR (WSA881X_ANALOG_BASE+0x0027) -#define WSA881X_SPKR_STATUS1 (WSA881X_ANALOG_BASE+0x0028) -#define WSA881X_SPKR_STATUS2 (WSA881X_ANALOG_BASE+0x0029) -#define WSA881X_BOOST_EN_CTL (WSA881X_ANALOG_BASE+0x002A) -#define WSA881X_BOOST_CURRENT_LIMIT (WSA881X_ANALOG_BASE+0x002B) -#define WSA881X_BOOST_PS_CTL (WSA881X_ANALOG_BASE+0x002C) -#define WSA881X_BOOST_PRESET_OUT1 (WSA881X_ANALOG_BASE+0x002D) -#define WSA881X_BOOST_PRESET_OUT2 (WSA881X_ANALOG_BASE+0x002E) -#define WSA881X_BOOST_FORCE_OUT (WSA881X_ANALOG_BASE+0x002F) -#define WSA881X_BOOST_LDO_PROG (WSA881X_ANALOG_BASE+0x0030) -#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB (WSA881X_ANALOG_BASE+0x0031) -#define WSA881X_BOOST_RON_CTL (WSA881X_ANALOG_BASE+0x0032) -#define WSA881X_BOOST_LOOP_STABILITY (WSA881X_ANALOG_BASE+0x0033) -#define WSA881X_BOOST_ZX_CTL (WSA881X_ANALOG_BASE+0x0034) -#define WSA881X_BOOST_START_CTL (WSA881X_ANALOG_BASE+0x0035) -#define WSA881X_BOOST_MISC1_CTL (WSA881X_ANALOG_BASE+0x0036) -#define WSA881X_BOOST_MISC2_CTL (WSA881X_ANALOG_BASE+0x0037) -#define WSA881X_BOOST_MISC3_CTL (WSA881X_ANALOG_BASE+0x0038) -#define WSA881X_BOOST_ATEST_CTL (WSA881X_ANALOG_BASE+0x0039) -#define WSA881X_SPKR_PROT_FE_GAIN (WSA881X_ANALOG_BASE+0x003A) -#define WSA881X_SPKR_PROT_FE_CM_LDO_SET (WSA881X_ANALOG_BASE+0x003B) -#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x003C) -#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 (WSA881X_ANALOG_BASE+0x003D) -#define WSA881X_SPKR_PROT_ATEST1 (WSA881X_ANALOG_BASE+0x003E) -#define WSA881X_SPKR_PROT_ATEST2 (WSA881X_ANALOG_BASE+0x003F) -#define WSA881X_SPKR_PROT_FE_VSENSE_VCM (WSA881X_ANALOG_BASE+0x0040) -#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 (WSA881X_ANALOG_BASE+0x0041) -#define WSA881X_BONGO_RESRV_REG1 (WSA881X_ANALOG_BASE+0x0042) -#define WSA881X_BONGO_RESRV_REG2 (WSA881X_ANALOG_BASE+0x0043) -#define WSA881X_SPKR_PROT_SAR (WSA881X_ANALOG_BASE+0x0044) -#define WSA881X_SPKR_STATUS3 (WSA881X_ANALOG_BASE+0x0045) - -#define WSA881X_NUM_REGISTERS (WSA881X_SPKR_STATUS3+1) -#define WSA881X_MAX_REGISTER (WSA881X_NUM_REGISTERS-1) -#define WSA881X_CACHE_SIZE WSA881X_NUM_REGISTERS -#endif /* WSA881X_REGISTERS_H */ diff --git a/asoc/codecs/wsa881x-regmap-analog.c b/asoc/codecs/wsa881x-regmap-analog.c deleted file mode 100644 index 2bc3c9eb7061..000000000000 --- a/asoc/codecs/wsa881x-regmap-analog.c +++ /dev/null @@ -1,499 +0,0 @@ -/* - * 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 "wsa881x-registers-analog.h" -#include "wsa881x-analog.h" - -struct reg_default wsa881x_ana_reg_defaults[] = { - {WSA881X_CHIP_ID0, 0x00}, - {WSA881X_CHIP_ID1, 0x00}, - {WSA881X_CHIP_ID2, 0x00}, - {WSA881X_CHIP_ID3, 0x02}, - {WSA881X_BUS_ID, 0x00}, - {WSA881X_CDC_RST_CTL, 0x00}, - {WSA881X_CDC_TOP_CLK_CTL, 0x03}, - {WSA881X_CDC_ANA_CLK_CTL, 0x00}, - {WSA881X_CDC_DIG_CLK_CTL, 0x00}, - {WSA881X_CLOCK_CONFIG, 0x00}, - {WSA881X_ANA_CTL, 0x08}, - {WSA881X_SWR_RESET_EN, 0x00}, - {WSA881X_TEMP_DETECT_CTL, 0x01}, - {WSA881X_TEMP_MSB, 0x00}, - {WSA881X_TEMP_LSB, 0x00}, - {WSA881X_TEMP_CONFIG0, 0x00}, - {WSA881X_TEMP_CONFIG1, 0x00}, - {WSA881X_CDC_CLIP_CTL, 0x03}, - {WSA881X_SDM_PDM9_LSB, 0x00}, - {WSA881X_SDM_PDM9_MSB, 0x00}, - {WSA881X_CDC_RX_CTL, 0x7E}, - {WSA881X_DEM_BYPASS_DATA0, 0x00}, - {WSA881X_DEM_BYPASS_DATA1, 0x00}, - {WSA881X_DEM_BYPASS_DATA2, 0x00}, - {WSA881X_DEM_BYPASS_DATA3, 0x00}, - {WSA881X_OTP_CTRL0, 0x00}, - {WSA881X_OTP_CTRL1, 0x00}, - {WSA881X_HDRIVE_CTL_GROUP1, 0x00}, - {WSA881X_INTR_MODE, 0x00}, - {WSA881X_INTR_MASK, 0x1F}, - {WSA881X_INTR_STATUS, 0x00}, - {WSA881X_INTR_CLEAR, 0x00}, - {WSA881X_INTR_LEVEL, 0x00}, - {WSA881X_INTR_SET, 0x00}, - {WSA881X_INTR_TEST, 0x00}, - {WSA881X_PDM_TEST_MODE, 0x00}, - {WSA881X_ATE_TEST_MODE, 0x00}, - {WSA881X_PIN_CTL_MODE, 0x00}, - {WSA881X_PIN_CTL_OE, 0x00}, - {WSA881X_PIN_WDATA_IOPAD, 0x00}, - {WSA881X_PIN_STATUS, 0x00}, - {WSA881X_DIG_DEBUG_MODE, 0x00}, - {WSA881X_DIG_DEBUG_SEL, 0x00}, - {WSA881X_DIG_DEBUG_EN, 0x00}, - {WSA881X_SWR_HM_TEST1, 0x08}, - {WSA881X_SWR_HM_TEST2, 0x00}, - {WSA881X_TEMP_DETECT_DBG_CTL, 0x00}, - {WSA881X_TEMP_DEBUG_MSB, 0x00}, - {WSA881X_TEMP_DEBUG_LSB, 0x00}, - {WSA881X_SAMPLE_EDGE_SEL, 0x0C}, - {WSA881X_SPARE_0, 0x00}, - {WSA881X_SPARE_1, 0x00}, - {WSA881X_SPARE_2, 0x00}, - {WSA881X_OTP_REG_0, 0x01}, - {WSA881X_OTP_REG_1, 0xFF}, - {WSA881X_OTP_REG_2, 0xC0}, - {WSA881X_OTP_REG_3, 0xFF}, - {WSA881X_OTP_REG_4, 0xC0}, - {WSA881X_OTP_REG_5, 0xFF}, - {WSA881X_OTP_REG_6, 0xFF}, - {WSA881X_OTP_REG_7, 0xFF}, - {WSA881X_OTP_REG_8, 0xFF}, - {WSA881X_OTP_REG_9, 0xFF}, - {WSA881X_OTP_REG_10, 0xFF}, - {WSA881X_OTP_REG_11, 0xFF}, - {WSA881X_OTP_REG_12, 0xFF}, - {WSA881X_OTP_REG_13, 0xFF}, - {WSA881X_OTP_REG_14, 0xFF}, - {WSA881X_OTP_REG_15, 0xFF}, - {WSA881X_OTP_REG_16, 0xFF}, - {WSA881X_OTP_REG_17, 0xFF}, - {WSA881X_OTP_REG_18, 0xFF}, - {WSA881X_OTP_REG_19, 0xFF}, - {WSA881X_OTP_REG_20, 0xFF}, - {WSA881X_OTP_REG_21, 0xFF}, - {WSA881X_OTP_REG_22, 0xFF}, - {WSA881X_OTP_REG_23, 0xFF}, - {WSA881X_OTP_REG_24, 0x03}, - {WSA881X_OTP_REG_25, 0x01}, - {WSA881X_OTP_REG_26, 0x03}, - {WSA881X_OTP_REG_27, 0x11}, - {WSA881X_OTP_REG_28, 0xFF}, - {WSA881X_OTP_REG_29, 0xFF}, - {WSA881X_OTP_REG_30, 0xFF}, - {WSA881X_OTP_REG_31, 0xFF}, - {WSA881X_OTP_REG_63, 0x40}, - /* WSA881x Analog registers */ - {WSA881X_BIAS_REF_CTRL, 0x6C}, - {WSA881X_BIAS_TEST, 0x16}, - {WSA881X_BIAS_BIAS, 0xF0}, - {WSA881X_TEMP_OP, 0x00}, - {WSA881X_TEMP_IREF_CTRL, 0x56}, - {WSA881X_TEMP_ISENS_CTRL, 0x47}, - {WSA881X_TEMP_CLK_CTRL, 0x87}, - {WSA881X_TEMP_TEST, 0x00}, - {WSA881X_TEMP_BIAS, 0x51}, - {WSA881X_TEMP_ADC_CTRL, 0x00}, - {WSA881X_TEMP_DOUT_MSB, 0x00}, - {WSA881X_TEMP_DOUT_LSB, 0x00}, - {WSA881X_ADC_EN_MODU_V, 0x00}, - {WSA881X_ADC_EN_MODU_I, 0x00}, - {WSA881X_ADC_EN_DET_TEST_V, 0x00}, - {WSA881X_ADC_EN_DET_TEST_I, 0x00}, - {WSA881X_ADC_SEL_IBIAS, 0x25}, - {WSA881X_ADC_EN_SEL_IBIAS, 0x10}, - {WSA881X_SPKR_DRV_EN, 0x74}, - {WSA881X_SPKR_DRV_GAIN, 0x01}, - {WSA881X_SPKR_DAC_CTL, 0x40}, - {WSA881X_SPKR_DRV_DBG, 0x15}, - {WSA881X_SPKR_PWRSTG_DBG, 0x00}, - {WSA881X_SPKR_OCP_CTL, 0xD4}, - {WSA881X_SPKR_CLIP_CTL, 0x90}, - {WSA881X_SPKR_BBM_CTL, 0x00}, - {WSA881X_SPKR_MISC_CTL1, 0x80}, - {WSA881X_SPKR_MISC_CTL2, 0x00}, - {WSA881X_SPKR_BIAS_INT, 0x56}, - {WSA881X_SPKR_PA_INT, 0x54}, - {WSA881X_SPKR_BIAS_CAL, 0xAC}, - {WSA881X_SPKR_BIAS_PSRR, 0x54}, - {WSA881X_SPKR_STATUS1, 0x00}, - {WSA881X_SPKR_STATUS2, 0x00}, - {WSA881X_BOOST_EN_CTL, 0x18}, - {WSA881X_BOOST_CURRENT_LIMIT, 0x7A}, - {WSA881X_BOOST_PS_CTL, 0xC0}, - {WSA881X_BOOST_PRESET_OUT1, 0x77}, - {WSA881X_BOOST_PRESET_OUT2, 0x70}, - {WSA881X_BOOST_FORCE_OUT, 0x0E}, - {WSA881X_BOOST_LDO_PROG, 0x16}, - {WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71}, - {WSA881X_BOOST_RON_CTL, 0x0F}, - {WSA881X_BOOST_LOOP_STABILITY, 0xAD}, - {WSA881X_BOOST_ZX_CTL, 0x34}, - {WSA881X_BOOST_START_CTL, 0x23}, - {WSA881X_BOOST_MISC1_CTL, 0x80}, - {WSA881X_BOOST_MISC2_CTL, 0x00}, - {WSA881X_BOOST_MISC3_CTL, 0x00}, - {WSA881X_BOOST_ATEST_CTL, 0x00}, - {WSA881X_SPKR_PROT_FE_GAIN, 0x46}, - {WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B}, - {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D}, - {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D}, - {WSA881X_SPKR_PROT_ATEST1, 0x01}, - {WSA881X_SPKR_PROT_ATEST2, 0x00}, - {WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D}, - {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D}, - {WSA881X_BONGO_RESRV_REG1, 0x00}, - {WSA881X_BONGO_RESRV_REG2, 0x00}, - {WSA881X_SPKR_PROT_SAR, 0x00}, - {WSA881X_SPKR_STATUS3, 0x00}, -}; - -struct reg_default wsa881x_ana_reg_defaults_0[] = { - {WSA881X_CHIP_ID0, 0x00}, - {WSA881X_CHIP_ID1, 0x00}, - {WSA881X_CHIP_ID2, 0x00}, - {WSA881X_CHIP_ID3, 0x02}, - {WSA881X_BUS_ID, 0x00}, - {WSA881X_CDC_RST_CTL, 0x00}, - {WSA881X_CDC_TOP_CLK_CTL, 0x03}, - {WSA881X_CDC_ANA_CLK_CTL, 0x00}, - {WSA881X_CDC_DIG_CLK_CTL, 0x00}, - {WSA881X_CLOCK_CONFIG, 0x00}, - {WSA881X_ANA_CTL, 0x08}, - {WSA881X_SWR_RESET_EN, 0x00}, - {WSA881X_TEMP_DETECT_CTL, 0x01}, - {WSA881X_TEMP_MSB, 0x00}, - {WSA881X_TEMP_LSB, 0x00}, - {WSA881X_TEMP_CONFIG0, 0x00}, - {WSA881X_TEMP_CONFIG1, 0x00}, - {WSA881X_CDC_CLIP_CTL, 0x03}, - {WSA881X_SDM_PDM9_LSB, 0x00}, - {WSA881X_SDM_PDM9_MSB, 0x00}, - {WSA881X_CDC_RX_CTL, 0x7E}, - {WSA881X_DEM_BYPASS_DATA0, 0x00}, - {WSA881X_DEM_BYPASS_DATA1, 0x00}, - {WSA881X_DEM_BYPASS_DATA2, 0x00}, - {WSA881X_DEM_BYPASS_DATA3, 0x00}, - {WSA881X_OTP_CTRL0, 0x00}, - {WSA881X_OTP_CTRL1, 0x00}, - {WSA881X_HDRIVE_CTL_GROUP1, 0x00}, - {WSA881X_INTR_MODE, 0x00}, - {WSA881X_INTR_MASK, 0x1F}, - {WSA881X_INTR_STATUS, 0x00}, - {WSA881X_INTR_CLEAR, 0x00}, - {WSA881X_INTR_LEVEL, 0x00}, - {WSA881X_INTR_SET, 0x00}, - {WSA881X_INTR_TEST, 0x00}, - {WSA881X_PDM_TEST_MODE, 0x00}, - {WSA881X_ATE_TEST_MODE, 0x00}, - {WSA881X_PIN_CTL_MODE, 0x00}, - {WSA881X_PIN_CTL_OE, 0x00}, - {WSA881X_PIN_WDATA_IOPAD, 0x00}, - {WSA881X_PIN_STATUS, 0x00}, - {WSA881X_DIG_DEBUG_MODE, 0x00}, - {WSA881X_DIG_DEBUG_SEL, 0x00}, - {WSA881X_DIG_DEBUG_EN, 0x00}, - {WSA881X_SWR_HM_TEST1, 0x08}, - {WSA881X_SWR_HM_TEST2, 0x00}, - {WSA881X_TEMP_DETECT_DBG_CTL, 0x00}, - {WSA881X_TEMP_DEBUG_MSB, 0x00}, - {WSA881X_TEMP_DEBUG_LSB, 0x00}, - {WSA881X_SAMPLE_EDGE_SEL, 0x0C}, - {WSA881X_SPARE_0, 0x00}, - {WSA881X_SPARE_1, 0x00}, - {WSA881X_SPARE_2, 0x00}, - {WSA881X_OTP_REG_0, 0x01}, - {WSA881X_OTP_REG_1, 0xFF}, - {WSA881X_OTP_REG_2, 0xC0}, - {WSA881X_OTP_REG_3, 0xFF}, - {WSA881X_OTP_REG_4, 0xC0}, - {WSA881X_OTP_REG_5, 0xFF}, - {WSA881X_OTP_REG_6, 0xFF}, - {WSA881X_OTP_REG_7, 0xFF}, - {WSA881X_OTP_REG_8, 0xFF}, - {WSA881X_OTP_REG_9, 0xFF}, - {WSA881X_OTP_REG_10, 0xFF}, - {WSA881X_OTP_REG_11, 0xFF}, - {WSA881X_OTP_REG_12, 0xFF}, - {WSA881X_OTP_REG_13, 0xFF}, - {WSA881X_OTP_REG_14, 0xFF}, - {WSA881X_OTP_REG_15, 0xFF}, - {WSA881X_OTP_REG_16, 0xFF}, - {WSA881X_OTP_REG_17, 0xFF}, - {WSA881X_OTP_REG_18, 0xFF}, - {WSA881X_OTP_REG_19, 0xFF}, - {WSA881X_OTP_REG_20, 0xFF}, - {WSA881X_OTP_REG_21, 0xFF}, - {WSA881X_OTP_REG_22, 0xFF}, - {WSA881X_OTP_REG_23, 0xFF}, - {WSA881X_OTP_REG_24, 0x03}, - {WSA881X_OTP_REG_25, 0x01}, - {WSA881X_OTP_REG_26, 0x03}, - {WSA881X_OTP_REG_27, 0x11}, - {WSA881X_OTP_REG_28, 0xFF}, - {WSA881X_OTP_REG_29, 0xFF}, - {WSA881X_OTP_REG_30, 0xFF}, - {WSA881X_OTP_REG_31, 0xFF}, - {WSA881X_OTP_REG_63, 0x40}, -}; - -struct reg_default wsa881x_ana_reg_defaults_1[] = { - {WSA881X_BIAS_REF_CTRL - WSA881X_ANALOG_BASE, 0x6C}, - {WSA881X_BIAS_TEST - WSA881X_ANALOG_BASE, 0x16}, - {WSA881X_BIAS_BIAS - WSA881X_ANALOG_BASE, 0xF0}, - {WSA881X_TEMP_OP - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_TEMP_IREF_CTRL - WSA881X_ANALOG_BASE, 0x56}, - {WSA881X_TEMP_ISENS_CTRL - WSA881X_ANALOG_BASE, 0x47}, - {WSA881X_TEMP_CLK_CTRL - WSA881X_ANALOG_BASE, 0x87}, - {WSA881X_TEMP_TEST - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_TEMP_BIAS - WSA881X_ANALOG_BASE, 0x51}, - {WSA881X_TEMP_ADC_CTRL - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_TEMP_DOUT_MSB - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_TEMP_DOUT_LSB - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_ADC_EN_MODU_V - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_ADC_EN_MODU_I - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_ADC_EN_DET_TEST_V - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_ADC_EN_DET_TEST_I - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_ADC_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x25}, - {WSA881X_ADC_EN_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x10}, - {WSA881X_SPKR_DRV_EN - WSA881X_ANALOG_BASE, 0x74}, - {WSA881X_SPKR_DRV_GAIN - WSA881X_ANALOG_BASE, 0x01}, - {WSA881X_SPKR_DAC_CTL - WSA881X_ANALOG_BASE, 0x40}, - {WSA881X_SPKR_DRV_DBG - WSA881X_ANALOG_BASE, 0x15}, - {WSA881X_SPKR_PWRSTG_DBG - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_SPKR_OCP_CTL - WSA881X_ANALOG_BASE, 0xD4}, - {WSA881X_SPKR_CLIP_CTL - WSA881X_ANALOG_BASE, 0x90}, - {WSA881X_SPKR_BBM_CTL - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_SPKR_MISC_CTL1 - WSA881X_ANALOG_BASE, 0x80}, - {WSA881X_SPKR_MISC_CTL2 - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_SPKR_BIAS_INT - WSA881X_ANALOG_BASE, 0x56}, - {WSA881X_SPKR_PA_INT - WSA881X_ANALOG_BASE, 0x54}, - {WSA881X_SPKR_BIAS_CAL - WSA881X_ANALOG_BASE, 0xAC}, - {WSA881X_SPKR_BIAS_PSRR - WSA881X_ANALOG_BASE, 0x54}, - {WSA881X_SPKR_STATUS1 - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_SPKR_STATUS2 - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_BOOST_EN_CTL - WSA881X_ANALOG_BASE, 0x18}, - {WSA881X_BOOST_CURRENT_LIMIT - WSA881X_ANALOG_BASE, 0x7A}, - {WSA881X_BOOST_PS_CTL - WSA881X_ANALOG_BASE, 0xC0}, - {WSA881X_BOOST_PRESET_OUT1 - WSA881X_ANALOG_BASE, 0x77}, - {WSA881X_BOOST_PRESET_OUT2 - WSA881X_ANALOG_BASE, 0x70}, - {WSA881X_BOOST_FORCE_OUT - WSA881X_ANALOG_BASE, 0x0E}, - {WSA881X_BOOST_LDO_PROG - WSA881X_ANALOG_BASE, 0x16}, - {WSA881X_BOOST_SLOPE_COMP_ISENSE_FB - WSA881X_ANALOG_BASE, 0x71}, - {WSA881X_BOOST_RON_CTL - WSA881X_ANALOG_BASE, 0x0F}, - {WSA881X_BOOST_LOOP_STABILITY - WSA881X_ANALOG_BASE, 0xAD}, - {WSA881X_BOOST_ZX_CTL - WSA881X_ANALOG_BASE, 0x34}, - {WSA881X_BOOST_START_CTL - WSA881X_ANALOG_BASE, 0x23}, - {WSA881X_BOOST_MISC1_CTL - WSA881X_ANALOG_BASE, 0x80}, - {WSA881X_BOOST_MISC2_CTL - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_BOOST_MISC3_CTL - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_BOOST_ATEST_CTL - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_SPKR_PROT_FE_GAIN - WSA881X_ANALOG_BASE, 0x46}, - {WSA881X_SPKR_PROT_FE_CM_LDO_SET - WSA881X_ANALOG_BASE, 0x3B}, - {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 - WSA881X_ANALOG_BASE, 0x8D}, - {WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 - WSA881X_ANALOG_BASE, 0x8D}, - {WSA881X_SPKR_PROT_ATEST1 - WSA881X_ANALOG_BASE, 0x01}, - {WSA881X_SPKR_PROT_ATEST2 - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_SPKR_PROT_FE_VSENSE_VCM - WSA881X_ANALOG_BASE, 0x8D}, - {WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 - WSA881X_ANALOG_BASE, 0x4D}, - {WSA881X_BONGO_RESRV_REG1 - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_BONGO_RESRV_REG2 - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_SPKR_PROT_SAR - WSA881X_ANALOG_BASE, 0x00}, - {WSA881X_SPKR_STATUS3 - WSA881X_ANALOG_BASE, 0x00}, -}; - -struct reg_default wsa881x_rev_2_0_dig[] = { - {WSA881X_RESET_CTL, 0x00}, - {WSA881X_TADC_VALUE_CTL, 0x01}, - {WSA881X_INTR_MASK, 0x1B}, - {WSA881X_IOPAD_CTL, 0x00}, - {WSA881X_OTP_REG_28, 0x3F}, - {WSA881X_OTP_REG_29, 0x3F}, - {WSA881X_OTP_REG_30, 0x01}, - {WSA881X_OTP_REG_31, 0x01}, -}; - -struct reg_default wsa881x_rev_2_0_ana[] = { - {WSA881X_TEMP_ADC_CTRL, 0x03}, - {WSA881X_ADC_SEL_IBIAS, 0x45}, - {WSA881X_SPKR_DRV_GAIN, 0xC1}, - {WSA881X_SPKR_DAC_CTL, 0x42}, - {WSA881X_SPKR_BBM_CTL, 0x02}, - {WSA881X_SPKR_MISC_CTL1, 0x40}, - {WSA881X_SPKR_MISC_CTL2, 0x07}, - {WSA881X_SPKR_BIAS_INT, 0x5F}, - {WSA881X_SPKR_BIAS_PSRR, 0x44}, - {WSA881X_BOOST_PS_CTL, 0xA0}, - {WSA881X_BOOST_PRESET_OUT1, 0xB7}, - {WSA881X_BOOST_LOOP_STABILITY, 0x8D}, - {WSA881X_SPKR_PROT_ATEST2, 0x02}, - {WSA881X_BONGO_RESRV_REG1, 0x5E}, - {WSA881X_BONGO_RESRV_REG2, 0x07}, -}; - -struct reg_default wsa881x_rev_2_0_regmap_ana[] = { - {WSA881X_TEMP_ADC_CTRL - WSA881X_ANALOG_BASE, 0x03}, - {WSA881X_ADC_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x45}, - {WSA881X_SPKR_DRV_GAIN - WSA881X_ANALOG_BASE, 0xC1}, - {WSA881X_SPKR_DAC_CTL - WSA881X_ANALOG_BASE, 0x42}, - {WSA881X_SPKR_BBM_CTL - WSA881X_ANALOG_BASE, 0x02}, - {WSA881X_SPKR_MISC_CTL1 - WSA881X_ANALOG_BASE, 0x40}, - {WSA881X_SPKR_MISC_CTL2 - WSA881X_ANALOG_BASE, 0x07}, - {WSA881X_SPKR_BIAS_INT - WSA881X_ANALOG_BASE, 0x5F}, - {WSA881X_SPKR_BIAS_PSRR - WSA881X_ANALOG_BASE, 0x44}, - {WSA881X_BOOST_PS_CTL - WSA881X_ANALOG_BASE, 0xA0}, - {WSA881X_BOOST_PRESET_OUT1 - WSA881X_ANALOG_BASE, 0xB7}, - {WSA881X_BOOST_LOOP_STABILITY - WSA881X_ANALOG_BASE, 0x8D}, - {WSA881X_SPKR_PROT_ATEST2 - WSA881X_ANALOG_BASE, 0x02}, - {WSA881X_BONGO_RESRV_REG1 - WSA881X_ANALOG_BASE, 0x5E}, - {WSA881X_BONGO_RESRV_REG2 - WSA881X_ANALOG_BASE, 0x07}, -}; - -/** - * wsa881x_update_reg_defaults_2_0 - update default values of regs for v2.0 - * - * WSA881x v2.0 has different default values for certain analog and digital - * registers compared to v1.x. Therefore, update the values of these registers - * with the values from tables defined above for v2.0. - */ -void wsa881x_update_reg_defaults_2_0(void) -{ - int i, j; - - for (i = 0; i < ARRAY_SIZE(wsa881x_rev_2_0_dig); i++) { - for (j = 0; j < ARRAY_SIZE(wsa881x_ana_reg_defaults); j++) - if (wsa881x_ana_reg_defaults[j].reg == - wsa881x_rev_2_0_dig[i].reg) - wsa881x_ana_reg_defaults[j].def = - wsa881x_rev_2_0_dig[i].def; - } - for (i = 0; i < ARRAY_SIZE(wsa881x_rev_2_0_ana); i++) { - for (j = 0; j < ARRAY_SIZE(wsa881x_ana_reg_defaults); j++) - if (wsa881x_ana_reg_defaults[j].reg == - wsa881x_rev_2_0_ana[i].reg) - wsa881x_ana_reg_defaults[j].def = - wsa881x_rev_2_0_ana[i].def; - } -} -EXPORT_SYMBOL(wsa881x_update_reg_defaults_2_0); - -/** - * wsa881x_update_regmap_2_0 - update regmap framework with new tables - * @regmap: pointer to WSA881x regmap structure - * @flag: indicates digital or analog WSA881x slave - * - * WSA881x v2.0 has some new registers for both analog and digital slaves. - * Update the regmap framework with all the new registers. - */ -void wsa881x_update_regmap_2_0(struct regmap *regmap, int flag) -{ - u16 ret = 0; - - switch (flag) { - case WSA881X_DIGITAL_SLAVE: - ret = regmap_register_patch(regmap, wsa881x_rev_2_0_dig, - ARRAY_SIZE(wsa881x_rev_2_0_dig)); - break; - case WSA881X_ANALOG_SLAVE: - ret = regmap_register_patch(regmap, wsa881x_rev_2_0_ana, - ARRAY_SIZE(wsa881x_rev_2_0_ana)); - break; - default: - pr_debug("%s: unknown version", __func__); - ret = -EINVAL; - break; - } - if (ret) - pr_err("%s: Failed to update regmap defaults ret= %d\n", - __func__, ret); -} -EXPORT_SYMBOL(wsa881x_update_regmap_2_0); - -static bool wsa881x_readable_register(struct device *dev, unsigned int reg) -{ - return wsa881x_ana_reg_readable[reg]; -} - -static bool wsa881x_volatile_register(struct device *dev, unsigned int reg) -{ - switch (reg) { - case WSA881X_CHIP_ID0: - case WSA881X_CHIP_ID1: - case WSA881X_CHIP_ID2: - case WSA881X_CHIP_ID3: - case WSA881X_BUS_ID: - case WSA881X_TEMP_MSB: - case WSA881X_TEMP_LSB: - case WSA881X_SDM_PDM9_LSB: - case WSA881X_SDM_PDM9_MSB: - case WSA881X_OTP_REG_0: - case WSA881X_OTP_REG_1: - case WSA881X_OTP_REG_2: - case WSA881X_OTP_REG_3: - case WSA881X_OTP_REG_4: - case WSA881X_OTP_REG_5: - case WSA881X_OTP_REG_31: - case WSA881X_TEMP_DOUT_MSB: - case WSA881X_TEMP_DOUT_LSB: - case WSA881X_TEMP_OP: - case WSA881X_OTP_CTRL1: - case WSA881X_INTR_STATUS: - case WSA881X_ATE_TEST_MODE: - case WSA881X_PIN_STATUS: - case WSA881X_SWR_HM_TEST2: - case WSA881X_SPKR_STATUS1: - case WSA881X_SPKR_STATUS2: - case WSA881X_SPKR_STATUS3: - case WSA881X_SPKR_PROT_SAR: - return true; - default: - return false; - } -} - -struct regmap_config wsa881x_ana_regmap_config[] = { -{ - .reg_bits = 8, - .val_bits = 8, - .cache_type = REGCACHE_NONE, - .reg_defaults = wsa881x_ana_reg_defaults_0, - .num_reg_defaults = ARRAY_SIZE(wsa881x_ana_reg_defaults_0), - .max_register = WSA881X_MAX_REGISTER, - .volatile_reg = wsa881x_volatile_register, - .readable_reg = wsa881x_readable_register, - .reg_format_endian = REGMAP_ENDIAN_NATIVE, - .val_format_endian = REGMAP_ENDIAN_NATIVE, -}, -{ - .reg_bits = 8, - .val_bits = 8, - .cache_type = REGCACHE_NONE, - .reg_defaults = wsa881x_ana_reg_defaults_1, - .num_reg_defaults = ARRAY_SIZE(wsa881x_ana_reg_defaults_1), - .max_register = WSA881X_MAX_REGISTER, - .volatile_reg = wsa881x_volatile_register, - .readable_reg = wsa881x_readable_register, - .reg_format_endian = REGMAP_ENDIAN_NATIVE, - .val_format_endian = REGMAP_ENDIAN_NATIVE, -} -}; diff --git a/asoc/codecs/wsa881x-tables-analog.c b/asoc/codecs/wsa881x-tables-analog.c deleted file mode 100644 index 061ed6fff798..000000000000 --- a/asoc/codecs/wsa881x-tables-analog.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 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. - */ - -#include -#include -#include "wsa881x-registers-analog.h" - -const u8 wsa881x_ana_reg_readable[WSA881X_CACHE_SIZE] = { - [WSA881X_CHIP_ID0] = 1, - [WSA881X_CHIP_ID1] = 1, - [WSA881X_CHIP_ID2] = 1, - [WSA881X_CHIP_ID3] = 1, - [WSA881X_BUS_ID] = 1, - [WSA881X_CDC_RST_CTL] = 1, - [WSA881X_CDC_TOP_CLK_CTL] = 1, - [WSA881X_CDC_ANA_CLK_CTL] = 1, - [WSA881X_CDC_DIG_CLK_CTL] = 1, - [WSA881X_CLOCK_CONFIG] = 1, - [WSA881X_ANA_CTL] = 1, - [WSA881X_SWR_RESET_EN] = 1, - [WSA881X_RESET_CTL] = 1, - [WSA881X_TADC_VALUE_CTL] = 1, - [WSA881X_TEMP_DETECT_CTL] = 1, - [WSA881X_TEMP_MSB] = 1, - [WSA881X_TEMP_LSB] = 1, - [WSA881X_TEMP_CONFIG0] = 1, - [WSA881X_TEMP_CONFIG1] = 1, - [WSA881X_CDC_CLIP_CTL] = 1, - [WSA881X_SDM_PDM9_LSB] = 1, - [WSA881X_SDM_PDM9_MSB] = 1, - [WSA881X_CDC_RX_CTL] = 1, - [WSA881X_DEM_BYPASS_DATA0] = 1, - [WSA881X_DEM_BYPASS_DATA1] = 1, - [WSA881X_DEM_BYPASS_DATA2] = 1, - [WSA881X_DEM_BYPASS_DATA3] = 1, - [WSA881X_OTP_CTRL0] = 1, - [WSA881X_OTP_CTRL1] = 1, - [WSA881X_HDRIVE_CTL_GROUP1] = 1, - [WSA881X_INTR_MODE] = 1, - [WSA881X_INTR_MASK] = 1, - [WSA881X_INTR_STATUS] = 1, - [WSA881X_INTR_CLEAR] = 1, - [WSA881X_INTR_LEVEL] = 1, - [WSA881X_INTR_SET] = 1, - [WSA881X_INTR_TEST] = 1, - [WSA881X_PDM_TEST_MODE] = 1, - [WSA881X_ATE_TEST_MODE] = 1, - [WSA881X_PIN_CTL_MODE] = 1, - [WSA881X_PIN_CTL_OE] = 1, - [WSA881X_PIN_WDATA_IOPAD] = 1, - [WSA881X_PIN_STATUS] = 1, - [WSA881X_DIG_DEBUG_MODE] = 1, - [WSA881X_DIG_DEBUG_SEL] = 1, - [WSA881X_DIG_DEBUG_EN] = 1, - [WSA881X_SWR_HM_TEST1] = 1, - [WSA881X_SWR_HM_TEST2] = 1, - [WSA881X_TEMP_DETECT_DBG_CTL] = 1, - [WSA881X_TEMP_DEBUG_MSB] = 1, - [WSA881X_TEMP_DEBUG_LSB] = 1, - [WSA881X_SAMPLE_EDGE_SEL] = 1, - [WSA881X_IOPAD_CTL] = 1, - [WSA881X_SPARE_0] = 1, - [WSA881X_SPARE_1] = 1, - [WSA881X_SPARE_2] = 1, - [WSA881X_OTP_REG_0] = 1, - [WSA881X_OTP_REG_1] = 1, - [WSA881X_OTP_REG_2] = 1, - [WSA881X_OTP_REG_3] = 1, - [WSA881X_OTP_REG_4] = 1, - [WSA881X_OTP_REG_5] = 1, - [WSA881X_OTP_REG_6] = 1, - [WSA881X_OTP_REG_7] = 1, - [WSA881X_OTP_REG_8] = 1, - [WSA881X_OTP_REG_9] = 1, - [WSA881X_OTP_REG_10] = 1, - [WSA881X_OTP_REG_11] = 1, - [WSA881X_OTP_REG_12] = 1, - [WSA881X_OTP_REG_13] = 1, - [WSA881X_OTP_REG_14] = 1, - [WSA881X_OTP_REG_15] = 1, - [WSA881X_OTP_REG_16] = 1, - [WSA881X_OTP_REG_17] = 1, - [WSA881X_OTP_REG_18] = 1, - [WSA881X_OTP_REG_19] = 1, - [WSA881X_OTP_REG_20] = 1, - [WSA881X_OTP_REG_21] = 1, - [WSA881X_OTP_REG_22] = 1, - [WSA881X_OTP_REG_23] = 1, - [WSA881X_OTP_REG_24] = 1, - [WSA881X_OTP_REG_25] = 1, - [WSA881X_OTP_REG_26] = 1, - [WSA881X_OTP_REG_27] = 1, - [WSA881X_OTP_REG_28] = 1, - [WSA881X_OTP_REG_29] = 1, - [WSA881X_OTP_REG_30] = 1, - [WSA881X_OTP_REG_31] = 1, - [WSA881X_OTP_REG_63] = 1, - /* Analog Registers */ - [WSA881X_BIAS_REF_CTRL] = 1, - [WSA881X_BIAS_TEST] = 1, - [WSA881X_BIAS_BIAS] = 1, - [WSA881X_TEMP_OP] = 1, - [WSA881X_TEMP_IREF_CTRL] = 1, - [WSA881X_TEMP_ISENS_CTRL] = 1, - [WSA881X_TEMP_CLK_CTRL] = 1, - [WSA881X_TEMP_TEST] = 1, - [WSA881X_TEMP_BIAS] = 1, - [WSA881X_TEMP_ADC_CTRL] = 1, - [WSA881X_TEMP_DOUT_MSB] = 1, - [WSA881X_TEMP_DOUT_LSB] = 1, - [WSA881X_ADC_EN_MODU_V] = 1, - [WSA881X_ADC_EN_MODU_I] = 1, - [WSA881X_ADC_EN_DET_TEST_V] = 1, - [WSA881X_ADC_EN_DET_TEST_I] = 1, - [WSA881X_ADC_SEL_IBIAS] = 1, - [WSA881X_ADC_EN_SEL_IBIAS] = 1, - [WSA881X_SPKR_DRV_EN] = 1, - [WSA881X_SPKR_DRV_GAIN] = 1, - [WSA881X_SPKR_DAC_CTL] = 1, - [WSA881X_SPKR_DRV_DBG] = 1, - [WSA881X_SPKR_PWRSTG_DBG] = 1, - [WSA881X_SPKR_OCP_CTL] = 1, - [WSA881X_SPKR_CLIP_CTL] = 1, - [WSA881X_SPKR_BBM_CTL] = 1, - [WSA881X_SPKR_MISC_CTL1] = 1, - [WSA881X_SPKR_MISC_CTL2] = 1, - [WSA881X_SPKR_BIAS_INT] = 1, - [WSA881X_SPKR_PA_INT] = 1, - [WSA881X_SPKR_BIAS_CAL] = 1, - [WSA881X_SPKR_BIAS_PSRR] = 1, - [WSA881X_SPKR_STATUS1] = 1, - [WSA881X_SPKR_STATUS2] = 1, - [WSA881X_BOOST_EN_CTL] = 1, - [WSA881X_BOOST_CURRENT_LIMIT] = 1, - [WSA881X_BOOST_PS_CTL] = 1, - [WSA881X_BOOST_PRESET_OUT1] = 1, - [WSA881X_BOOST_PRESET_OUT2] = 1, - [WSA881X_BOOST_FORCE_OUT] = 1, - [WSA881X_BOOST_LDO_PROG] = 1, - [WSA881X_BOOST_SLOPE_COMP_ISENSE_FB] = 1, - [WSA881X_BOOST_RON_CTL] = 1, - [WSA881X_BOOST_LOOP_STABILITY] = 1, - [WSA881X_BOOST_ZX_CTL] = 1, - [WSA881X_BOOST_START_CTL] = 1, - [WSA881X_BOOST_MISC1_CTL] = 1, - [WSA881X_BOOST_MISC2_CTL] = 1, - [WSA881X_BOOST_MISC3_CTL] = 1, - [WSA881X_BOOST_ATEST_CTL] = 1, - [WSA881X_SPKR_PROT_FE_GAIN] = 1, - [WSA881X_SPKR_PROT_FE_CM_LDO_SET] = 1, - [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1] = 1, - [WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2] = 1, - [WSA881X_SPKR_PROT_ATEST1] = 1, - [WSA881X_SPKR_PROT_ATEST2] = 1, - [WSA881X_SPKR_PROT_FE_VSENSE_VCM] = 1, - [WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1] = 1, - [WSA881X_BONGO_RESRV_REG1] = 1, - [WSA881X_BONGO_RESRV_REG2] = 1, - [WSA881X_SPKR_PROT_SAR] = 1, - [WSA881X_SPKR_STATUS3] = 1, -}; diff --git a/asoc/msm-audio-pinctrl.c b/asoc/msm-audio-pinctrl.c deleted file mode 100644 index f0fba840eb2d..000000000000 --- a/asoc/msm-audio-pinctrl.c +++ /dev/null @@ -1,316 +0,0 @@ -/* 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/asoc/msm-audio-pinctrl.h b/asoc/msm-audio-pinctrl.h deleted file mode 100644 index ec7c6aafdaea..000000000000 --- a/asoc/msm-audio-pinctrl.h +++ /dev/null @@ -1,43 +0,0 @@ -/* 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/asoc/msm8996.c b/asoc/msm8996.c deleted file mode 100644 index 08900372a2c2..000000000000 --- a/asoc/msm8996.c +++ /dev/null @@ -1,4007 +0,0 @@ -/* - * 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_soc_kcontrol_codec(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_component *aux_comp; - 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 (!list_empty(&rtd->card->aux_comp_list)) { - aux_comp = list_first_entry(&rtd->card->aux_comp_list, - struct snd_soc_component, list_aux); - if (!strcmp(aux_comp->name, WSA8810_NAME_1) || - !strcmp(aux_comp->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->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->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->id == MSM_BACKEND_DAI_SLIMBUS_0_TX) - user_set_tx_ch = msm_slim_0_tx_ch; - /* For _tx3 case */ - else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX) - user_set_tx_ch = msm_slim_1_tx_ch; - else if (dai_link->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->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->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->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, - .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, - .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", - .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, - .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, - .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 */ - .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", - .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, - .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", - .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 */ - .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 */ - .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 */ - .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", - .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", - .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, - .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", - .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", - .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", - .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", - .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", - .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", - .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", - .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, - .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", - .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 */ - .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 */ - .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 */ - .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 */ - .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 */ - .id = MSM_FRONTEND_DAI_MULTIMEDIA15, - }, - { - .name = "MSM8996 ULL NOIRQ_2", - .stream_name = "MM_NOIRQ_2", - .cpu_dai_name = "MultiMedia16", - .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 */ - .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, - .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", - .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", - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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, - .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/asoc/sdm660-external.c b/asoc/sdm660-external.c index 0358ad8ad9b4..1c12bfd732cd 100644 --- a/asoc/sdm660-external.c +++ b/asoc/sdm660-external.c @@ -20,7 +20,6 @@ #include #include #include "msm-pcm-routing-v2.h" -#include "msm-audio-pinctrl.h" #include "sdm660-common.h" #include "sdm660-external.h" #include "codecs/wcd9335.h" From 0713e26a7d146cc3eda5f9dfee06a7cce802a876 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Wed, 30 Aug 2017 22:28:49 +0530 Subject: [PATCH 023/276] autoconf: update config to support DLKM audio for WCD/WSA To support DLKM audio for WCD/WSA drivers on SDM845, update conf file to install as module(.ko) for required configs. CRs-Fixed: 2082310 Change-Id: I359799ac8c8ac050fce1eac8741837004f9f6a0b Signed-off-by: Laxminath Kasam --- config/sdm845auto.conf | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/config/sdm845auto.conf b/config/sdm845auto.conf index 64ca29f9da4a..5234e2b3a618 100644 --- a/config/sdm845auto.conf +++ b/config/sdm845auto.conf @@ -1,14 +1,16 @@ -CONFIG_PINCTRL_WCD=y -CONFIG_SND_SOC_WCD934X=y -CONFIG_AUDIO_EXT_CLK=y -CONFIG_SND_SOC_WCD9XXX_V2=y -CONFIG_SND_SOC_WCD_MBHC=y -CONFIG_SND_SOC_WSA881X=y -CONFIG_SND_SOC_WCD_DSP_MGR=y -CONFIG_SND_SOC_WCD_SPI=y -CONFIG_SND_SOC_WCD934X=y -CONFIG_SND_SOC_WCD934X_MBHC=y -CONFIG_SND_SOC_WCD934X_DSD=y +CONFIG_PINCTRL_WCD=m +CONFIG_SND_SOC_WCD9XXX_V2=m +CONFIG_SND_SOC_WCD_MBHC=m +CONFIG_SND_SOC_WSA881X=m +CONFIG_SND_SOC_WCD_SPI=m +CONFIG_SND_SOC_WCD934X=m +CONFIG_SOUNDWIRE_WCD_CTRL=m +CONFIG_WCD9XXX_CODEC_CORE=m +CONFIG_MSM_CDC_PINCTRL=m +CONFIG_SND_SOC_WCD934X_MBHC=m +CONFIG_SND_SOC_WCD934X_DSD=m +CONFIG_SND_SOC_MACHINE_SDM845=m +CONFIG_WCD_DSP_GLINK=m CONFIG_MSM_QDSP6V2_CODECS=y CONFIG_MSM_ULTRASOUND=y CONFIG_MSM_QDSP6_APRV2_GLINK=y @@ -19,15 +21,10 @@ CONFIG_MSM_QDSP6_SSR=y CONFIG_MSM_QDSP6_PDR=y CONFIG_MSM_QDSP6_NOTIFIER=y CONFIG_SND_SOC_MSM_HOSTLESS_PCM=y -CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y CONFIG_SND_SOC_SDM845=y CONFIG_MSM_GLINK_SPI_XPRT=y CONFIG_SOUNDWIRE=y -CONFIG_SOUNDWIRE_WCD_CTRL=y CONFIG_SND_SOC_QDSP6V2=y -CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y -CONFIG_MSM_CDC_PINCTRL=y -CONFIG_WCD9XXX_CODEC_CORE=y CONFIG_SND_SOC_WCD_MBHC_ADC=y CONFIG_QTI_PP=y CONFIG_SND_HWDEP=y @@ -35,8 +32,6 @@ CONFIG_DTS_EAGLE=y CONFIG_DOLBY_DS2=y CONFIG_DOLBY_LICENSE=y CONFIG_DTS_SRS_TM=y -CONFIG_SND_SOC_MACHINE_SDM845=y CONFIG_SND_SOC_MSM_STUB=y -CONFIG_WCD_DSP_GLINK=y CONFIG_MSM_AVTIMER=y CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y From 73f4dfff2075eac9e064450156ddf44bae6a7d61 Mon Sep 17 00:00:00 2001 From: Asish Bhattacharya Date: Sun, 10 Sep 2017 09:58:32 +0530 Subject: [PATCH 024/276] autoconf: enabled HDMI for sdm670 Fix compilation of kernel for sdm670. Change-Id: Ice7768e280a924043b7aafb4d8f7e9fdc051134b Signed-off-by: Asish Bhattacharya --- config/sdm670auto.conf | 1 + config/sdm670autoconf.h | 1 + 2 files changed, 2 insertions(+) diff --git a/config/sdm670auto.conf b/config/sdm670auto.conf index 59e27dc313f5..cbeb2dee9ae8 100644 --- a/config/sdm670auto.conf +++ b/config/sdm670auto.conf @@ -44,3 +44,4 @@ CONFIG_WCD_DSP_GLINK=y CONFIG_MSM_AVTIMER=y CONFIG_SND_SOC_SDM660_CDC=y CONFIG_SND_SOC_MSM_SDW=y +CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y diff --git a/config/sdm670autoconf.h b/config/sdm670autoconf.h index 7b3a3515da36..1cec92c8c7a8 100644 --- a/config/sdm670autoconf.h +++ b/config/sdm670autoconf.h @@ -57,3 +57,4 @@ #define CONFIG_SND_SOC_INT_CODEC 1 #define CONFIG_SND_SOC_SDM660_CDC 1 #define CONFIG_SND_SOC_MSM_SDW 1 +#define CONFIG_SND_SOC_MSM_HDMI_CODEC_RX 1 From f0cb90b1f715e02d8eb0cdb7ac01931643d0f290 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Thu, 7 Sep 2017 11:40:08 +0530 Subject: [PATCH 025/276] dsp: assign channel mapping for Quad Mic use case Assign channel mapping for Quad Mic voice use case in channel info command during voice setup as DSP requires channel mapping to be sent with default values. CRs-Fixed: 2097341 Change-Id: I661f845c26f8639e5e8dffa430ecc8cb3b3e8780 Signed-off-by: Aditya Bavanari --- dsp/q6voice.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dsp/q6voice.c b/dsp/q6voice.c index 7cd340f96177..d7d19b7af0f3 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -36,6 +36,7 @@ #define CMD_STATUS_FAIL 1 #define NUM_CHANNELS_MONO 1 #define NUM_CHANNELS_STEREO 2 +#define NUM_CHANNELS_QUAD 4 #define CVP_VERSION_2 2 enum { @@ -3889,11 +3890,15 @@ static int voice_send_cvp_channel_info_v2(struct voice_data *v, } else if (channel_info->num_channels == NUM_CHANNELS_STEREO) { channel_info->channel_mapping[0] = PCM_CHANNEL_FL; channel_info->channel_mapping[1] = PCM_CHANNEL_FR; + } else if (channel_info->num_channels == NUM_CHANNELS_QUAD && + param_type == TX_PATH) { + channel_info->channel_mapping[0] = PCM_CHANNEL_FL; + channel_info->channel_mapping[1] = PCM_CHANNEL_FR; + channel_info->channel_mapping[2] = PCM_CHANNEL_LS; + channel_info->channel_mapping[3] = PCM_CHANNEL_RS; } else { - pr_err("%s: Unsupported num channels: %d\n", - __func__, channel_info->num_channels); - ret = -EINVAL; - goto done; + pr_warn("%s: Unsupported num channels: %d for path: %d\n", + __func__, channel_info->num_channels, param_type); } v->cvp_state = CMD_STATUS_FAIL; @@ -4014,10 +4019,8 @@ static int voice_send_cvp_mfc_config_v2(struct voice_data *v) mfc_config_info->channel_type[0] = PCM_CHANNEL_FL; mfc_config_info->channel_type[1] = PCM_CHANNEL_FR; } else { - pr_err("%s: Unsupported num channels: %d\n", - __func__, mfc_config_info->num_channels); - ret = -EINVAL; - goto done; + pr_warn("%s: Unsupported num channels: %d\n", + __func__, mfc_config_info->num_channels); } v->cvp_state = CMD_STATUS_FAIL; From 245361df3830a3bf7263e90a1e373d5bd6409577 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Thu, 7 Sep 2017 12:11:30 +0530 Subject: [PATCH 026/276] dsp: fix 2nd channel mute in VPRx out for Stereo EC ref In Stereo EC reference, mute is observed in VPRx output of 2nd channel when only speaker device uses custom MFC topology. When handset and speaker use the same custom topology, issue is not observed. Because when DSP VPRx module creates the custom topology with handset configuration (1 channel and center channel mapping) and device switch to speaker occurs MFC conversion from center to left and right channels is successful. But in case where handset uses default topology, when device switch to speaker occurs, VPRx creates the custom topology with speaker configuration(2 channels and left mapping). So, when MFC config is received, only left->left mapping is done which leads to mute in right channel. Fix is to send the channel mixer configuration to MFC module so that both left->left and left->right mapping is done. CRs-Fixed: 2102374 Change-Id: If9be09866615e6623e78f48b9841eb068c825992 Signed-off-by: Aditya Bavanari --- dsp/q6voice.c | 106 +++++++++++++++++++++++++++++++++++++ include/dsp/apr_audio-v2.h | 3 ++ include/dsp/q6voice.h | 57 ++++++++++++++++++++ 3 files changed, 166 insertions(+) diff --git a/dsp/q6voice.c b/dsp/q6voice.c index d7d19b7af0f3..6f70f57dc400 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -38,6 +38,7 @@ #define NUM_CHANNELS_STEREO 2 #define NUM_CHANNELS_QUAD 4 #define CVP_VERSION_2 2 +#define GAIN_Q14_FORMAT(a) (a << 14) enum { VOC_TOKEN_NONE, @@ -3958,6 +3959,103 @@ static int voice_send_cvp_channel_info_cmd(struct voice_data *v) return ret; } +static int voice_send_cvp_ch_mixer_info_v2(struct voice_data *v) +{ + int ret; + struct cvp_set_channel_mixer_info_cmd_v2 cvp_set_ch_mixer_info_cmd; + void *apr_cvp; + u16 cvp_handle; + struct vss_icommon_param_data_ch_mixer_v2_t *cvp_config_param_data = + &cvp_set_ch_mixer_info_cmd. + cvp_set_ch_mixer_param_v2.param_data; + struct vss_param_channel_mixer_info_t *ch_mixer_info = + &cvp_config_param_data->ch_mixer_info; + + 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); + memset(&cvp_set_ch_mixer_info_cmd, 0, + sizeof(cvp_set_ch_mixer_info_cmd)); + + cvp_set_ch_mixer_info_cmd.hdr.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + cvp_set_ch_mixer_info_cmd.hdr.pkt_size = + APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(cvp_set_ch_mixer_info_cmd) - APR_HDR_SIZE); + cvp_set_ch_mixer_info_cmd.hdr.src_svc = 0; + cvp_set_ch_mixer_info_cmd.hdr.src_domain = APR_DOMAIN_APPS; + cvp_set_ch_mixer_info_cmd.hdr.src_port = + voice_get_idx_for_session(v->session_id); + cvp_set_ch_mixer_info_cmd.hdr.dest_svc = 0; + cvp_set_ch_mixer_info_cmd.hdr.dest_domain = APR_DOMAIN_ADSP; + cvp_set_ch_mixer_info_cmd.hdr.dest_port = cvp_handle; + cvp_set_ch_mixer_info_cmd.hdr.token = VOC_GENERIC_SET_PARAM_TOKEN; + cvp_set_ch_mixer_info_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_PARAM_V2; + cvp_set_ch_mixer_info_cmd.cvp_set_ch_mixer_param_v2.mem_size = + sizeof(struct vss_icommon_param_data_ch_mixer_v2_t); + + cvp_config_param_data->module_id = AUDPROC_MODULE_ID_MFC; + cvp_config_param_data->param_id = + AUDPROC_CHMIXER_PARAM_ID_COEFF; + cvp_config_param_data->param_size = + sizeof(struct vss_param_channel_mixer_info_t); + + ch_mixer_info->index = 0; + ch_mixer_info->num_output_channels = v->dev_rx.no_of_channels; + /* + * Configure Rx input to be mono for channel mixer as the DSP + * configures vocproc input as mono. + */ + ch_mixer_info->num_input_channels = NUM_CHANNELS_MONO; + ch_mixer_info->out_channel_map[0] = PCM_CHANNEL_L; + ch_mixer_info->out_channel_map[1] = PCM_CHANNEL_R; + ch_mixer_info->in_channel_map[0] = PCM_CHANNEL_L; + ch_mixer_info->channel_weight_coeff[0][0] = GAIN_Q14_FORMAT(1); + ch_mixer_info->channel_weight_coeff[1][0] = GAIN_Q14_FORMAT(1); + ch_mixer_info->reserved = 0; + + v->cvp_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_set_ch_mixer_info_cmd); + if (ret < 0) { + pr_err("%s: Failed to send VSS_ICOMMON_CMD_SET_PARAM_V2 %d\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: wait_event timeout\n", __func__); + ret = -ETIMEDOUT; + goto done; + } + + if (v->async_err > 0) { + pr_err("%s: DSP returned error[%s] handle = %d\n", __func__, + adsp_err_get_err_str(v->async_err), cvp_handle); + ret = adsp_err_get_lnx_err_code(v->async_err); + goto done; + } + ret = 0; +done: + return ret; +} + static int voice_send_cvp_mfc_config_v2(struct voice_data *v) { int ret; @@ -4057,7 +4155,15 @@ static int voice_send_cvp_mfc_config_cmd(struct voice_data *v) int ret = 0; if (common.cvp_version >= CVP_VERSION_2) { + ret = voice_send_cvp_ch_mixer_info_v2(v); + if (ret < 0) + pr_warn("%s: Set channel mixer config failed err:%d", + __func__, ret); + ret = voice_send_cvp_mfc_config_v2(v); + if (ret < 0) + pr_warn("%s: Set MFC config failed err:%d", + __func__, ret); } else { pr_warn("%s: CVP Version not supported\n", __func__); ret = -EINVAL; diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index 2256b7b4d9b8..8e9b6294156b 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -623,6 +623,9 @@ struct audproc_softvolume_params { */ #define AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT 0x00010913 +/* Param ID of Channel Mixer used by AUDPROC_MODULE_ID_MFC */ +#define AUDPROC_CHMIXER_PARAM_ID_COEFF 0x00010342 + struct audproc_mfc_output_media_fmt { struct adm_cmd_set_pp_params_v5 params; diff --git a/include/dsp/q6voice.h b/include/dsp/q6voice.h index 7818707b5aa9..af7995a01cd9 100644 --- a/include/dsp/q6voice.h +++ b/include/dsp/q6voice.h @@ -245,6 +245,16 @@ struct vss_param_vocproc_dev_channel_info_t { uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; } __packed; +struct vss_param_channel_mixer_info_t { + uint32_t index; + uint16_t num_output_channels; + uint16_t num_input_channels; + uint16_t out_channel_map[2]; + uint16_t in_channel_map[1]; + uint16_t channel_weight_coeff[2][1]; + uint16_t reserved; +} __packed; + struct vss_param_mfc_config_info_t { uint32_t sample_rate; uint16_t bits_per_sample; @@ -313,6 +323,47 @@ struct vss_icommon_cmd_set_param_channel_info_v2_t { struct vss_icommon_param_data_channel_info_v2_t param_data; } __packed; +struct vss_icommon_param_data_ch_mixer_v2_t { + /* Valid ID of the module. */ + uint32_t module_id; + /* Valid ID of the parameter. */ + uint32_t param_id; + /* + * Data size of the structure relating to the param_id/module_id + * combination in uint8_t bytes. + */ + uint16_t param_size; + /* This field must be set to zero. */ + uint16_t reserved; + struct vss_param_channel_mixer_info_t ch_mixer_info; +} __packed; + +struct vss_icommon_cmd_set_param_ch_mixer_v2_t { + /* + * Pointer to the unique identifier for an address (physical/virtual). + * + * If the parameter data payload is within the message payload + * (in-band), set this field to 0. The parameter data begins at the + * specified data payload address. + * + * If the parameter data is out-of-band, this field is the handle to + * the physical address in the shared memory that holds the parameter + * data. + */ + uint32_t mem_handle; + /* + * Location of the parameter data payload. + * + * The payload is an array of vss_icommon_param_data_t. If the + * mem_handle is 0, this field is ignored. + */ + uint64_t mem_address; + /* Size of the parameter data payload in bytes. */ + uint32_t mem_size; + + struct vss_icommon_param_data_ch_mixer_v2_t param_data; +} __packed; + struct vss_icommon_param_data_mfc_config_v2_t { /* Valid ID of the module. */ uint32_t module_id; @@ -1595,6 +1646,12 @@ struct cvp_set_channel_info_cmd_v2 { cvp_set_ch_info_param_v2; } __packed; +struct cvp_set_channel_mixer_info_cmd_v2 { + struct apr_hdr hdr; + struct vss_icommon_cmd_set_param_ch_mixer_v2_t + cvp_set_ch_mixer_param_v2; +} __packed; + struct cvp_set_mfc_config_cmd_v2 { struct apr_hdr hdr; struct vss_icommon_cmd_set_param_mfc_config_v2_t cvp_set_mfc_param_v2; From 60871bde94822da700156d1e47139d7bb26a823b Mon Sep 17 00:00:00 2001 From: Vidyakumar Athota Date: Fri, 8 Sep 2017 11:26:51 -0700 Subject: [PATCH 027/276] ipc: initialize glink link state There is a chance that glink channel memory pointer is used after free if WDSP_REG_PKT and WDSP_CMD_PKT are received at the same time from different threads. Fix this issue by initializing glink link state to GLINK_LINK_STATE_DOWN. Also limit error logs to avoid watchdog timeout issues. Change-Id: I07c4e6f12eb057405eb59f1c0d04b890fa964ce8 Signed-off-by: Vidyakumar Athota --- ipc/wcd-dsp-glink.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/ipc/wcd-dsp-glink.c b/ipc/wcd-dsp-glink.c index 870b9f7455d3..095205c52c18 100644 --- a/ipc/wcd-dsp-glink.c +++ b/ipc/wcd-dsp-glink.c @@ -570,7 +570,7 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, mutex_lock(&wpriv->glink_mutex); if (wpriv->ch) { - dev_err(wpriv->dev, "%s: glink ch memory is already allocated\n", + dev_err_ratelimited(wpriv->dev, "%s: glink ch memory is already allocated\n", __func__); ret = -EINVAL; goto done; @@ -579,7 +579,7 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, no_of_channels = pkt->no_of_channels; if (no_of_channels > WDSP_MAX_NO_OF_CHANNELS) { - dev_err(wpriv->dev, "%s: no_of_channels: %d but max allowed are %d\n", + dev_err_ratelimited(wpriv->dev, "%s: no_of_channels: %d but max allowed are %d\n", __func__, no_of_channels, WDSP_MAX_NO_OF_CHANNELS); ret = -EINVAL; goto done; @@ -598,20 +598,20 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, size += WDSP_CH_CFG_SIZE; if (size > pkt_size) { - dev_err(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", __func__, size, pkt_size); ret = -EINVAL; goto err_ch_mem; } if (ch_cfg->no_of_intents > WDSP_MAX_NO_OF_INTENTS) { - dev_err(wpriv->dev, "%s: Invalid no_of_intents = %d\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid no_of_intents = %d\n", __func__, ch_cfg->no_of_intents); ret = -EINVAL; goto err_ch_mem; } size += (sizeof(u32) * ch_cfg->no_of_intents); if (size > pkt_size) { - dev_err(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid size = %zd, pkt_size = %zd\n", __func__, size, pkt_size); ret = -EINVAL; goto err_ch_mem; @@ -746,7 +746,7 @@ static ssize_t wdsp_glink_read(struct file *file, char __user *buf, } if (count > WDSP_MAX_READ_SIZE) { - dev_info(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n", + dev_info_ratelimited(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n", __func__, count); count = WDSP_MAX_READ_SIZE; } @@ -778,7 +778,7 @@ static ssize_t wdsp_glink_read(struct file *file, char __user *buf, if (ret1) { mutex_unlock(&wpriv->rsp_mutex); - dev_err(wpriv->dev, "%s: copy_to_user failed %d\n", + dev_err_ratelimited(wpriv->dev, "%s: copy_to_user failed %d\n", __func__, ret); ret = -EFAULT; goto done; @@ -824,7 +824,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, if ((count < WDSP_WRITE_PKT_SIZE) || (count > WDSP_MAX_WRITE_SIZE)) { - dev_err(wpriv->dev, "%s: Invalid count = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid count = %zd\n", __func__, count); ret = -EINVAL; goto done; @@ -841,7 +841,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, ret = copy_from_user(tx_buf->buf, buf, count); if (ret) { - dev_err(wpriv->dev, "%s: copy_from_user failed %d\n", + dev_err_ratelimited(wpriv->dev, "%s: copy_from_user failed %d\n", __func__, ret); ret = -EFAULT; goto free_buf; @@ -852,7 +852,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, case WDSP_REG_PKT: if (count < (WDSP_WRITE_PKT_SIZE + WDSP_REG_PKT_SIZE + WDSP_CH_CFG_SIZE)) { - dev_err(wpriv->dev, "%s: Invalid reg pkt size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid reg pkt size = %zd\n", __func__, count); ret = -EINVAL; goto free_buf; @@ -861,7 +861,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, (struct wdsp_reg_pkt *)wpkt->payload, count); if (ret < 0) - dev_err(wpriv->dev, "%s: glink register failed, ret = %d\n", + dev_err_ratelimited(wpriv->dev, "%s: glink register failed, ret = %d\n", __func__, ret); vfree(tx_buf); break; @@ -871,7 +871,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, GLINK_LINK_STATE_UP), msecs_to_jiffies(TIMEOUT_MS)); if (!ret) { - dev_err(wpriv->dev, "%s: Link state wait timeout\n", + dev_err_ratelimited(wpriv->dev, "%s: Link state wait timeout\n", __func__); ret = -ETIMEDOUT; goto free_buf; @@ -881,7 +881,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, break; case WDSP_CMD_PKT: if (count <= (WDSP_WRITE_PKT_SIZE + WDSP_CMD_PKT_SIZE)) { - dev_err(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt size = %zd\n", __func__, count); ret = -EINVAL; goto free_buf; @@ -889,7 +889,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, mutex_lock(&wpriv->glink_mutex); if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) { mutex_unlock(&wpriv->glink_mutex); - dev_err(wpriv->dev, "%s: Link state is Down\n", + dev_err_ratelimited(wpriv->dev, "%s: Link state is Down\n", __func__); ret = -ENETRESET; @@ -901,7 +901,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, sizeof(struct wdsp_cmd_pkt) + cpkt->payload_size; if (count < pkt_max_size) { - dev_err(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n", + dev_err_ratelimited(wpriv->dev, "%s: Invalid cmd pkt count = %zd, pkt_size = %zd\n", __func__, count, pkt_max_size); ret = -EINVAL; goto free_buf; @@ -917,7 +917,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, } } if (!tx_buf->ch) { - dev_err(wpriv->dev, "%s: Failed to get glink channel\n", + dev_err_ratelimited(wpriv->dev, "%s: Failed to get glink channel\n", __func__); ret = -EINVAL; goto free_buf; @@ -928,7 +928,7 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, GLINK_CONNECTED), msecs_to_jiffies(TIMEOUT_MS)); if (!ret) { - dev_err(wpriv->dev, "%s: glink channel %s is not in connected state %d\n", + dev_err_ratelimited(wpriv->dev, "%s: glink channel %s is not in connected state %d\n", __func__, tx_buf->ch->ch_cfg.name, tx_buf->ch->channel_state); ret = -ETIMEDOUT; @@ -940,7 +940,8 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, queue_work(wpriv->work_queue, &tx_buf->tx_work); break; default: - dev_err(wpriv->dev, "%s: Invalid packet type\n", __func__); + dev_err_ratelimited(wpriv->dev, "%s: Invalid packet type\n", + __func__); ret = -EINVAL; vfree(tx_buf); break; @@ -986,6 +987,7 @@ static int wdsp_glink_open(struct inode *inode, struct file *file) goto err_wq; } + wpriv->glink_state.link_state = GLINK_LINK_STATE_DOWN; init_completion(&wpriv->rsp_complete); init_waitqueue_head(&wpriv->link_state_wait); mutex_init(&wpriv->rsp_mutex); From 861a8fa60c95fb9f7fa1784c00173754ddb45a11 Mon Sep 17 00:00:00 2001 From: Tanya Dixit Date: Tue, 12 Sep 2017 15:38:21 +0530 Subject: [PATCH 028/276] dsp: fix no recovery after SSR/PDR Add apr_reset for q6core service to enable glink channel to close and open properly after SSR. CRs-Fixed: 2099312 Change-Id: Ia023cdb19a6a9dc7fae3e5c28ab51ddd92d37527 Signed-off-by: Tanya Dixit --- dsp/q6core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dsp/q6core.c b/dsp/q6core.c index 8d82704055fc..95b203a74b07 100644 --- a/dsp/q6core.c +++ b/dsp/q6core.c @@ -210,7 +210,11 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) case RESET_EVENTS:{ pr_debug("%s: Reset event received in Core service\n", __func__); - /* no reset done as the data will not change after SSR*/ + /* + * no reset for q6core_avcs_ver_info done as + * the data will not change after SSR + */ + apr_reset(q6core_lcl.core_handle_q); q6core_lcl.core_handle_q = NULL; break; } From a6b2a5317309c757a16fb4f40bce616bc9d1a4f9 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Thu, 24 Aug 2017 16:50:06 -0700 Subject: [PATCH 029/276] ASoC: sdm845: add support to enable QUAT TDM TX path Add support to update QUAT TDM TX HW parameters and back end DAI link ops to enable QUAT TDM recording feature on SDM845. Change-Id: If0fccafaf563f9453559a45a59300a5bec75f449 Signed-off-by: Xiaoyu Ye --- asoc/sdm845.c | 52 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index a3654d8ecdb2..ce5373cda341 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -4495,21 +4495,22 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, unsigned int slot_mask; unsigned int slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; - pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); - - slots = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; - /*2 slot config - bits 0 and 1 set for the first two slots */ - slot_mask = 0x0000FFFF >> (16-slots); slot_width = 32; - channels = slots; + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); - pr_debug("%s: slot_width %d slots %d\n", __func__, slot_width, slots); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - pr_debug("%s: slot_width %d\n", __func__, slot_width); + slots = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + channels = slots; + + pr_debug("%s: tdm rx slot_width %d slots %d\n", + __func__, slot_width, slots); + 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", + pr_err("%s: failed to set tdm rx slot, err:%d\n", __func__, ret); goto end; } @@ -4517,11 +4518,36 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, 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", + pr_err("%s: failed to set tdm rx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + slots = tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + channels = slots; + + pr_debug("%s: tdm tx slot_width %d slots %d\n", + __func__, slot_width, slots); + + 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 tx 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 tdm tx channel map, err:%d\n", __func__, ret); goto end; } } else { + ret = -EINVAL; pr_err("%s: invalid use case, err:%d\n", __func__, ret); } @@ -4540,7 +4566,7 @@ static int sdm845_tdm_snd_startup(struct snd_pcm_substream *substream) ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); if (ret) - pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", __func__, ret); return ret; @@ -4556,7 +4582,7 @@ static void sdm845_tdm_snd_shutdown(struct snd_pcm_substream *substream) ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); if (ret) - pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", __func__, ret); } @@ -5734,7 +5760,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .dpcm_capture = 1, .id = MSM_BACKEND_DAI_QUAT_TDM_TX_0, .be_hw_params_fixup = msm_be_hw_params_fixup, - .ops = &msm_tdm_be_ops, + .ops = &sdm845_tdm_be_ops, .ignore_suspend = 1, }, }; From 1cb5ce084c7853c77397d94622c5b3d30208dd46 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Tue, 12 Sep 2017 18:19:08 -0700 Subject: [PATCH 030/276] ASoC: msm: qdsp6v2: add support to enable 4-channel QUAT TDM recording Add the corresponding 'cap_mask' to support 4 slots configuration in CPU DAI driver to enable 4-channel QUAT TDM recording feature on SDM845. Change-Id: I137d874b00fadde7513ab6c843f61becc74feac1 Signed-off-by: Xiaoyu Ye --- asoc/msm-dai-q6-v2.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 6b620a5c8ed0..1d01026f1cf2 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -6104,11 +6104,14 @@ static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; } - /* HW only supports 16 and 8 slots configuration */ + /* HW supports 1-32 slots configuration. Typical: 1, 2, 4, 8, 16, 32 */ switch (slots) { case 2: cap_mask = 0x03; break; + case 4: + cap_mask = 0x0F; + break; case 8: cap_mask = 0xFF; break; From 2d0102dd44b605c9ae24dd93e49b9de7811fffe4 Mon Sep 17 00:00:00 2001 From: Siena Richard Date: Tue, 5 Sep 2017 11:15:45 -0700 Subject: [PATCH 031/276] dsp: allocate contiguous memory for version information Allocate contiguous memory for version information instead of splitting it across the stack and heap to centralize memory and improve readability. CRs-Fixed: 2104576 Signed-off-by: Siena Richard Change-Id: Id7b5942522da6312da57bfef3d3c0b55934fe1d7 --- dsp/q6core.c | 126 +++++++++++++++++-------------------- dsp/q6voice.c | 17 ++--- include/dsp/apr_audio-v2.h | 2 +- include/dsp/q6core.h | 2 +- 4 files changed, 69 insertions(+), 78 deletions(-) diff --git a/dsp/q6core.c b/dsp/q6core.c index 8d82704055fc..72c146b0230d 100644 --- a/dsp/q6core.c +++ b/dsp/q6core.c @@ -46,7 +46,7 @@ enum ver_query_status { struct q6core_avcs_ver_info { enum ver_query_status status; - struct avcs_fwk_ver_info ver_info; + struct avcs_fwk_ver_info *ver_info; }; struct q6core_str { @@ -84,53 +84,40 @@ static struct generic_get_data_ *generic_get_data; static int parse_fwk_version_info(uint32_t *payload) { - size_t fwk_ver_size; - size_t svc_size; + size_t ver_size; int num_services; - int ret = 0; pr_debug("%s: Payload info num services %d\n", __func__, payload[4]); + /* * payload1[4] is the number of services running on DSP * Based on this info, we copy the payload into core * avcs version info structure. */ num_services = payload[4]; - q6core_lcl.q6core_avcs_ver_info.ver_info.avcs_fwk_version. - num_services = num_services; if (num_services > VSS_MAX_AVCS_NUM_SERVICES) { pr_err("%s: num_services: %d greater than max services: %d\n", __func__, num_services, VSS_MAX_AVCS_NUM_SERVICES); - ret = -EINVAL; - goto done; + return -EINVAL; } - fwk_ver_size = sizeof(struct avcs_get_fwk_version); - svc_size = num_services * sizeof(struct avs_svc_api_info); + /* * Dynamically allocate memory for all * the services based on num_services */ - q6core_lcl.q6core_avcs_ver_info.ver_info.services = NULL; - q6core_lcl.q6core_avcs_ver_info.ver_info.services = - kzalloc(svc_size, GFP_ATOMIC); - if (q6core_lcl.q6core_avcs_ver_info.ver_info.services == NULL) { - ret = -ENOMEM; - goto done; - } - /* - * memcpy is done twice because the memory allocated for - * q6core_lcl.q6core_avcs_ver_info.ver_info is not - * contiguous. - */ - memcpy(&q6core_lcl.q6core_avcs_ver_info.ver_info, - (uint8_t *)payload, fwk_ver_size); - memcpy(q6core_lcl.q6core_avcs_ver_info.ver_info.services, - (uint8_t *)&payload[sizeof(struct avcs_get_fwk_version)/ - sizeof(uint32_t)], svc_size); - ret = 0; -done: - return ret; + ver_size = sizeof(struct avcs_get_fwk_version) + + num_services * sizeof(struct avs_svc_api_info); + if (q6core_lcl.q6core_avcs_ver_info.ver_info != NULL) + pr_warn("%s: Version info is not NULL\n", __func__); + q6core_lcl.q6core_avcs_ver_info.ver_info = + kzalloc(ver_size, GFP_ATOMIC); + if (q6core_lcl.q6core_avcs_ver_info.ver_info == NULL) + return -ENOMEM; + + memcpy(q6core_lcl.q6core_avcs_ver_info.ver_info, (uint8_t *) payload, + ver_size); + return 0; } static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) @@ -367,58 +354,56 @@ int q6core_get_service_version(uint32_t service_id, struct avcs_fwk_ver_info *ver_info, size_t size) { + struct avcs_fwk_ver_info *cached_ver_info = NULL; int i; uint32_t num_services; - size_t svc_size; + size_t ver_size; + int ret; - svc_size = q6core_get_avcs_service_size(service_id); - if (svc_size != size) { - pr_err("%s: Expected size: %zu, Provided size: %zu\n", - __func__, svc_size, size); + if (ver_info == NULL) { + pr_err("%s: ver_info is NULL\n", __func__); return -EINVAL; } - num_services = - q6core_lcl.q6core_avcs_ver_info.ver_info. - avcs_fwk_version.num_services; + ret = q6core_get_fwk_version_size(service_id); + if (ret < 0) { + pr_err("%s: Failed to get service size for service id %d with error %d\n", + __func__, service_id, ret); + return ret; + } - if (ver_info == NULL) { - pr_err("%s: NULL parameter ver_info\n", __func__); + ver_size = ret; + if (ver_size != size) { + pr_err("%s: Expected size %zu and provided size %zu do not match\n", + __func__, ver_size, size); return -EINVAL; } - memcpy(ver_info, &q6core_lcl.q6core_avcs_ver_info. - ver_info.avcs_fwk_version, sizeof(struct avcs_get_fwk_version)); + cached_ver_info = q6core_lcl.q6core_avcs_ver_info.ver_info; + num_services = cached_ver_info->avcs_fwk_version.num_services; if (service_id == AVCS_SERVICE_ID_ALL) { - memcpy(&ver_info->services[0], &q6core_lcl. - q6core_avcs_ver_info.ver_info.services[0], - (num_services * sizeof(struct avs_svc_api_info))); - } else { - for (i = 0; i < num_services; i++) { - if (q6core_lcl.q6core_avcs_ver_info. - ver_info.services[i].service_id == service_id) { - memcpy(&ver_info->services[0], - &q6core_lcl.q6core_avcs_ver_info. - ver_info.services[i], size); - break; - } - } + memcpy(ver_info, cached_ver_info, ver_size); + return 0; } - return 0; + ver_info->avcs_fwk_version = cached_ver_info->avcs_fwk_version; + for (i = 0; i < num_services; i++) { + if (cached_ver_info->services[i].service_id == service_id) { + ver_info->services[0] = cached_ver_info->services[i]; + return 0; + } + } + pr_err("%s: No service matching service ID %d\n", __func__, service_id); + return -EINVAL; } EXPORT_SYMBOL(q6core_get_service_version); -size_t q6core_get_avcs_service_size(uint32_t service_id) +size_t q6core_get_fwk_version_size(uint32_t service_id) { int ret = 0; uint32_t num_services; - num_services = - q6core_lcl.q6core_avcs_ver_info.ver_info. - avcs_fwk_version.num_services; - mutex_lock(&(q6core_lcl.ver_lock)); pr_debug("%s: q6core_avcs_ver_info.status(%d)\n", __func__, q6core_lcl.q6core_avcs_ver_info.status); @@ -427,7 +412,6 @@ size_t q6core_get_avcs_service_size(uint32_t service_id) case VER_QUERY_SUPPORTED: pr_debug("%s: AVCS FWK version query already attempted\n", __func__); - ret = num_services * sizeof(struct avs_svc_api_info); break; case VER_QUERY_UNSUPPORTED: ret = -EOPNOTSUPP; @@ -436,9 +420,6 @@ size_t q6core_get_avcs_service_size(uint32_t service_id) pr_debug("%s: Attempting AVCS FWK version query\n", __func__); if (q6core_is_adsp_ready()) { ret = q6core_send_get_avcs_fwk_ver_cmd(); - if (ret == 0) - ret = num_services * - sizeof(struct avs_svc_api_info); } else { pr_err("%s: ADSP is not ready to query version\n", __func__); @@ -453,12 +434,21 @@ size_t q6core_get_avcs_service_size(uint32_t service_id) } mutex_unlock(&(q6core_lcl.ver_lock)); - if (service_id != AVCS_SERVICE_ID_ALL) - return sizeof(struct avs_svc_api_info); + if (ret) + goto done; + + num_services = q6core_lcl.q6core_avcs_ver_info.ver_info + ->avcs_fwk_version.num_services; + ret = sizeof(struct avcs_get_fwk_version); + if (service_id == AVCS_SERVICE_ID_ALL) + ret += num_services * sizeof(struct avs_svc_api_info); + else + ret += sizeof(struct avs_svc_api_info); +done: return ret; } -EXPORT_SYMBOL(q6core_get_avcs_service_size); +EXPORT_SYMBOL(q6core_get_fwk_version_size); int32_t core_set_license(uint32_t key, uint32_t module_id) { diff --git a/dsp/q6voice.c b/dsp/q6voice.c index 7cd340f96177..fa338b2282d7 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -4066,8 +4066,8 @@ static int voice_send_cvp_mfc_config_cmd(struct voice_data *v) static int voice_get_avcs_version_per_service(uint32_t service_id) { int ret = 0; - size_t svc_size; - struct avcs_fwk_ver_info ver_info = {{0}, NULL}; + size_t ver_size; + struct avcs_fwk_ver_info *ver_info = NULL; if (service_id == AVCS_SERVICE_ID_ALL) { pr_err("%s: Invalid service id: %d", __func__, @@ -4075,19 +4075,20 @@ static int voice_get_avcs_version_per_service(uint32_t service_id) return -EINVAL; } - svc_size = sizeof(struct avs_svc_api_info); - ver_info.services = kzalloc(svc_size, GFP_KERNEL); - if (ver_info.services == NULL) + ver_size = sizeof(struct avcs_get_fwk_version) + + sizeof(struct avs_svc_api_info); + ver_info = kzalloc(ver_size, GFP_KERNEL); + if (ver_info == NULL) return -ENOMEM; - ret = q6core_get_service_version(service_id, &ver_info, svc_size); + ret = q6core_get_service_version(service_id, ver_info, ver_size); if (ret < 0) goto done; - ret = ver_info.services[0].api_version; + ret = ver_info->services[0].api_version; common.is_avcs_version_queried = true; done: - kfree(ver_info.services); + kfree(ver_info); return ret; } diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index 2256b7b4d9b8..121efc432c59 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -9264,7 +9264,7 @@ struct avs_svc_api_info { struct avcs_fwk_ver_info { struct avcs_get_fwk_version avcs_fwk_version; - struct avs_svc_api_info *services; + struct avs_svc_api_info services[0]; } __packed; /* LSM Specific */ diff --git a/include/dsp/q6core.h b/include/dsp/q6core.h index 1f1adad031e3..f6240a33e6fd 100644 --- a/include/dsp/q6core.h +++ b/include/dsp/q6core.h @@ -25,7 +25,7 @@ bool q6core_is_adsp_ready(void); int q6core_get_service_version(uint32_t service_id, struct avcs_fwk_ver_info *ver_info, size_t size); -size_t q6core_get_avcs_service_size(uint32_t service_id); +size_t q6core_get_fwk_version_size(uint32_t service_id); #define ADSP_CMD_SET_DTS_EAGLE_DATA_ID 0x00012919 #define DTS_EAGLE_LICENSE_ID 0x00028346 From 2b6adc8ba19b57d3c58f02c2c154e022d9d9f4d9 Mon Sep 17 00:00:00 2001 From: Cong Tang Date: Fri, 26 May 2017 12:06:50 +0800 Subject: [PATCH 032/276] ASoC: add support to configure TDM dai clk attribute and clk freq TDM dai driver supports optional clk attribute configuration and clk freq update. To configure TDM interface as PCM mode, the clk attribute should be Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO. Implement set_sysclk callback function to update Quaternary TDM RX and TX clk freq. Change-Id: I41edaa8d99325e9582e04ddb81a6ad5b5e4435bc Signed-off-by: Cong Tang Signed-off-by: Xiaoyu Ye --- asoc/msm-dai-q6-v2.c | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 6b620a5c8ed0..189a417ebb0e 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -5127,6 +5127,25 @@ static int msm_dai_tdm_q6_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s: Clk Rate from DT file %d\n", __func__, tdm_clk_set.clk_freq_in_hz); + /* initialize static tdm clk attribute to default value */ + tdm_clk_set.clk_attri = Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO; + + /* extract tdm clk attribute into static */ + if (of_find_property(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-clk-attribute", NULL)) { + rc = of_property_read_u16(pdev->dev.of_node, + "qcom,msm-cpudai-tdm-clk-attribute", + &tdm_clk_set.clk_attri); + if (rc) { + dev_err(&pdev->dev, "%s: value for clk attribute not found %s\n", + __func__, "qcom,msm-cpudai-tdm-clk-attribute"); + goto rtn; + } + dev_dbg(&pdev->dev, "%s: clk attribute from DT file %d\n", + __func__, tdm_clk_set.clk_attri); + } else + dev_dbg(&pdev->dev, "%s: clk attribute not found\n", __func__); + /* extract tdm clk src master/slave info into static */ rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-cpudai-tdm-clk-internal", @@ -6203,6 +6222,40 @@ static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai, return rc; } +static int msm_dai_q6_tdm_set_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + struct msm_dai_q6_tdm_dai_data *dai_data = + dev_get_drvdata(dai->dev); + + switch (dai->id) { + 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_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: + dai_data->clk_set.clk_freq_in_hz = freq; + break; + default: + return -EINVAL; + } + + dev_dbg(dai->dev, "%s: dai id = 0x%x, group clk_freq = %d\n", + __func__, dai->id, freq); + return 0; +} + 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) @@ -6632,6 +6685,7 @@ static struct snd_soc_dai_ops msm_dai_q6_tdm_ops = { .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, + .set_sysclk = msm_dai_q6_tdm_set_sysclk, .shutdown = msm_dai_q6_tdm_shutdown, }; From f48af4ff1f4b67c172833c68ccf1bba12075d89c Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Fri, 1 Sep 2017 14:57:51 -0700 Subject: [PATCH 033/276] ASoC: sdm845: add support to update TDM CLK frequency at runtime TDM CLK frequency needs to be updated at runtime to use the correct CLK setup. Change-Id: Ib42fedca3cbf41b85ca121ca04cc994bf8ad4d39 Signed-off-by: Xiaoyu Ye --- asoc/sdm845.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index a3654d8ecdb2..2d26aac09a4a 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -4492,7 +4492,7 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai = rtd->cpu_dai; int ret = 0; int channels, slot_width, slots; - unsigned int slot_mask; + unsigned int slot_mask, rate, clk_freq; unsigned int slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); @@ -4524,8 +4524,16 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, } else { pr_err("%s: invalid use case, err:%d\n", __func__, ret); + goto end; } + rate = params_rate(params); + clk_freq = rate * slot_width * slots; + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT); + if (ret < 0) + pr_err("%s: failed to set tdm clk, err:%d\n", + __func__, ret); + end: return ret; } From a8aea17dddb8030a3bf89cb1e63779c269b4c02f Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Wed, 13 Sep 2017 11:37:53 +0530 Subject: [PATCH 034/276] dsp: fix dangling pointer access Assign the circular buffer address to port buffer only after ION allocation is succesful to fix dangling pointer access. Also, lock the circular buffer memory allocation in order to avoid multiple allocations for a port. CRs-Fixed: 2096407 Change-Id: I22c1d55ea611ac59cdca51924787f6831bad8c2b Signed-off-by: Aditya Bavanari --- dsp/q6asm.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 28b3f733bcd9..0a4216a0e28c 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -3350,6 +3350,15 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac, int bytes_to_alloc, rc; size_t len; + mutex_lock(&ac->cmd_lock); + + if (ac->port[dir].buf) { + pr_err("%s: Buffer already allocated\n", __func__); + rc = -EINVAL; + mutex_unlock(&ac->cmd_lock); + goto done; + } + buf_circ = kzalloc(sizeof(struct audio_buffer), GFP_KERNEL); if (!buf_circ) { @@ -3357,10 +3366,6 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac, 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); @@ -3372,11 +3377,12 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac, if (rc) { pr_err("%s: Audio ION alloc is failed, rc = %d\n", __func__, rc); - mutex_unlock(&ac->cmd_lock); kfree(buf_circ); + mutex_unlock(&ac->cmd_lock); goto done; } + ac->port[dir].buf = buf_circ; buf_circ->used = dir ^ 1; buf_circ->size = bytes_to_alloc; buf_circ->actual_size = bytes_to_alloc; @@ -3541,12 +3547,6 @@ int q6asm_open_shared_io(struct audio_client *ac, 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) From 016c49fa463b54e0e729a4c275750116a47b1502 Mon Sep 17 00:00:00 2001 From: Vikram Panduranga Date: Mon, 11 Sep 2017 15:15:56 -0700 Subject: [PATCH 035/276] ASoC: add routing entries for Multimedia10 and Multimedia16 Add missing FE to BE links for Multimedia10 and Multimedia16 which can resolve FE to BE routing failures. Change-Id: I9f889d88fe520f7efa83c2e2058995fa955c82cb Signed-off-by: Vikram Panduranga --- asoc/msm-pcm-routing-v2.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 29dfade94f0f..788946ad8ea1 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -7311,6 +7311,30 @@ static const struct snd_kcontrol_new mmul10_mixer_controls[] = { SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX, MSM_FRONTEND_DAI_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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_MULTIMEDIA10, 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, @@ -14202,6 +14226,12 @@ static const struct snd_soc_dapm_route intercon[] = { {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_RX_1", "QUAT_TDM_RX_1"}, {"AUDIO_REF_EC_UL10 MUX", "QUAT_TDM_RX_2", "QUAT_TDM_RX_2"}, {"AUDIO_REF_EC_UL10 MUX", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + + {"AUDIO_REF_EC_UL16 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL16 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL16 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL16 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"}, From a63f8be985e508d3bacba7bb1df80fee2dd4eb58 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Tue, 12 Sep 2017 16:51:26 -0700 Subject: [PATCH 036/276] ASoC: msm: fix compilation errors when CONFIG_QTI_PP is disabled Add dummy static inline functions for feature QTI_PP to fix compilation erros for undefined function when flag 'CONFIG_QTI_PP' is not defined. Change-Id: I24c29636755f44a80b2a0267e0dbb8f72d6a3c9d Signed-off-by: Xiaoyu Ye --- asoc/msm-qti-pp-config.h | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/asoc/msm-qti-pp-config.h b/asoc/msm-qti-pp-config.h index ade2f5e507ec..3bf97b479e84 100644 --- a/asoc/msm-qti-pp-config.h +++ b/asoc/msm-qti-pp-config.h @@ -15,6 +15,7 @@ #include #define DSP_BIT_WIDTH_MIXER_CTL "ASM Bit Width" +#ifdef CONFIG_QTI_PP int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, uint32_t *payload); int msm_adsp_init_mixer_ctl_pp_event_queue(struct snd_soc_pcm_runtime *rtd); @@ -25,7 +26,6 @@ int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); -#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, @@ -35,6 +35,42 @@ int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx, uint16_t op_FR_ip_FR_weight); void msm_qti_pp_add_controls(struct snd_soc_platform *platform); #else /* CONFIG_QTI_PP */ +static inline int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, + uint32_t *payload) +{ + return 0; +} + +static inline int msm_adsp_init_mixer_ctl_pp_event_queue( + struct snd_soc_pcm_runtime *rtd) +{ + return 0; +} + +static inline int msm_adsp_clean_mixer_ctl_pp_event_queue( + struct snd_soc_pcm_runtime *rtd) +{ + return 0; +} + +static inline int msm_adsp_stream_cmd_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return 0; +} + +static inline int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static inline int msm_adsp_stream_callback_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return 0; +} + #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, \ From ee9c03a81e6f5fc7d8b12fde367820a1e0d3e229 Mon Sep 17 00:00:00 2001 From: Vikram Panduranga Date: Thu, 7 Sep 2017 15:06:17 -0700 Subject: [PATCH 037/276] asoc: send effect config based on apptype Add support to configure a specific module and its parameter on adm based on apptype. This can be used to dynamically configure a module provided by application. Change-Id: Iccfee192b0dec505bbb5f65a251e0dd12432fe59 Signed-off-by: Vikram Panduranga --- asoc/msm-pcm-routing-v2.c | 88 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 8e3fe895d98d..7a2dc54753d5 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -10816,6 +10816,91 @@ static const struct snd_kcontrol_new app_type_cfg_controls[] = { 0x2000, 0, 4, NULL, msm_routing_put_app_type_gain_control) }; +static int msm_routing_put_module_cfg_control(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int copp_idx, fe_id, be_id, port_type; + int ret = 0; + unsigned long copp; + struct msm_pcm_routing_bdai_data *bedai; + char *param_data = NULL; + uint32_t *update_param_data = NULL; + uint32_t param_size = sizeof(uint32_t) + + sizeof(struct adm_param_data_v5); + int dir = ucontrol->value.integer.value[0] ? SESSION_TYPE_TX : + SESSION_TYPE_RX; + int app_type = ucontrol->value.integer.value[1]; + int module_id = ucontrol->value.integer.value[2]; + int instance_id = ucontrol->value.integer.value[3]; + int param_id = ucontrol->value.integer.value[4]; + int param_value = ucontrol->value.integer.value[5]; + + port_type = (dir == SESSION_TYPE_RX) ? MSM_AFE_PORT_TYPE_RX : + MSM_AFE_PORT_TYPE_TX; + pr_debug("%s app_type:%d mod_id:%d instance_id:%d param_id:%d value:%d\n", + __func__, app_type, module_id, + instance_id, param_id, param_value); + + param_data = kzalloc(param_size, GFP_KERNEL); + if (!param_data) + return -ENOMEM; + + update_param_data = (uint32_t *)param_data; + *update_param_data++ = module_id; + *update_param_data++ = param_id; + *update_param_data++ = sizeof(uint32_t); + *update_param_data++ = param_value; + + mutex_lock(&routing_lock); + for (be_id = 0; be_id < MSM_BACKEND_DAI_MAX; be_id++) { + if (is_be_dai_extproc(be_id)) + continue; + + bedai = &msm_bedais[be_id]; + if (afe_get_port_type(bedai->port_id) != port_type) + continue; + + if (!bedai->active) + continue; + + for (fe_id = 0; fe_id < MSM_FRONTEND_DAI_MAX; fe_id++) { + if (!test_bit(fe_id, &bedai->fe_sessions[0])) + continue; + + if (app_type != + fe_dai_app_type_cfg[fe_id][dir][be_id].app_type) + continue; + + copp = session_copp_map[fe_id][dir][be_id]; + for (copp_idx = 0; copp_idx < MAX_COPPS_PER_PORT; + copp_idx++) { + if (!test_bit(copp_idx, &copp)) + continue; + + ret = adm_send_params_v5(bedai->port_id, + copp_idx, + param_data, + param_size); + if (ret) { + pr_err("%s: Setting param failed with err=%d\n", + __func__, ret); + ret = -EINVAL; + goto done; + } + } + } + } +done: + mutex_unlock(&routing_lock); + kfree(param_data); + return ret; +} + +static const struct snd_kcontrol_new module_cfg_controls[] = { + SOC_SINGLE_MULTI_EXT("Audio Effect", SND_SOC_NOPM, 0, + 0x2000, 0, 6, NULL, msm_routing_put_module_cfg_control) +}; + static int msm_routing_get_lsm_app_type_cfg_control( struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -15810,6 +15895,9 @@ static int msm_routing_probe(struct snd_soc_platform *platform) snd_soc_add_platform_controls(platform, lsm_app_type_cfg_controls, ARRAY_SIZE(lsm_app_type_cfg_controls)); + snd_soc_add_platform_controls(platform, module_cfg_controls, + ARRAY_SIZE(module_cfg_controls)); + snd_soc_add_platform_controls(platform, stereo_to_custom_stereo_controls, ARRAY_SIZE(stereo_to_custom_stereo_controls)); From 4d6a6bef3249f0af49793413f0237e091c2c5aa6 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Fri, 15 Sep 2017 10:35:44 +0800 Subject: [PATCH 038/276] ASoC: wcd-mbhc: add support to read new headset detection parameters To config different headset detection parameters for different hardware, add two new headset detection parameters in device tree and read it in mbhc driver. Change-Id: I10457b7d5eae54eba8a4cd273885a63ebad3ccb3 Signed-off-by: Meng Wang --- asoc/codecs/wcd-mbhc-adc.c | 46 +++++++++++++++++++++++++++----------- asoc/codecs/wcd-mbhc-v2.c | 14 ++++++++++++ asoc/codecs/wcd-mbhc-v2.h | 2 ++ 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/asoc/codecs/wcd-mbhc-adc.c b/asoc/codecs/wcd-mbhc-adc.c index e44eec9fa5c7..097dda9e4242 100644 --- a/asoc/codecs/wcd-mbhc-adc.c +++ b/asoc/codecs/wcd-mbhc-adc.c @@ -370,11 +370,18 @@ static bool wcd_mbhc_adc_check_for_spl_headset(struct wcd_mbhc *mbhc, * btn press/relesae for HEADSET type during correct work. */ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); - adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * + if (mbhc->hs_thr) + adc_threshold = mbhc->hs_thr; + else + adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * wcd_mbhc_get_micbias(mbhc))/WCD_MBHC_ADC_MICBIAS_MV); - adc_hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV * - wcd_mbhc_get_micbias(mbhc))/ - WCD_MBHC_ADC_MICBIAS_MV); + + if (mbhc->hph_thr) + adc_hph_threshold = mbhc->hph_thr; + else + adc_hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV * + wcd_mbhc_get_micbias(mbhc))/ + WCD_MBHC_ADC_MICBIAS_MV); if (output_mv > adc_threshold || output_mv < adc_hph_threshold) { spl_hs = false; @@ -426,8 +433,10 @@ static bool wcd_is_special_headset(struct wcd_mbhc *mbhc) return false; } } - - adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * + if (mbhc->hs_thr) + adc_threshold = mbhc->hs_thr; + else + adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * wcd_mbhc_get_micbias(mbhc)) / WCD_MBHC_ADC_MICBIAS_MV); @@ -556,14 +565,25 @@ static void wcd_micbias_disable(struct wcd_mbhc *mbhc) } } -static int wcd_mbhc_get_plug_from_adc(int adc_result) +static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result) { enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; + u32 hph_thr = 0, hs_thr = 0; + + if (mbhc->hs_thr) + hs_thr = mbhc->hs_thr; + else + hs_thr = WCD_MBHC_ADC_HS_THRESHOLD_MV; + + if (mbhc->hph_thr) + hph_thr = mbhc->hph_thr; + else + hph_thr = WCD_MBHC_ADC_HPH_THRESHOLD_MV; - if (adc_result < WCD_MBHC_ADC_HPH_THRESHOLD_MV) + if (adc_result < hph_thr) plug_type = MBHC_PLUG_TYPE_HEADPHONE; - else if (adc_result > WCD_MBHC_ADC_HS_THRESHOLD_MV) + else if (adc_result > hs_thr) plug_type = MBHC_PLUG_TYPE_HIGH_HPH; else plug_type = MBHC_PLUG_TYPE_HEADSET; @@ -612,7 +632,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) } /* Find plug type */ output_mv = wcd_measure_adc_continuous(mbhc); - plug_type = wcd_mbhc_get_plug_from_adc(output_mv); + plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); /* * Report plug type if it is either headset or headphone @@ -667,7 +687,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) * instead of hogging system by contineous polling, wait for * sometime and re-check stop request again. */ - plug_type = wcd_mbhc_get_plug_from_adc(output_mv); + plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); if ((output_mv > WCD_MBHC_ADC_HS_THRESHOLD_MV) && (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) { @@ -713,7 +733,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) no_gnd_mic_swap_cnt++; pt_gnd_mic_swap_cnt = 0; plug_type = wcd_mbhc_get_plug_from_adc( - output_mv); + mbhc, output_mv); if ((no_gnd_mic_swap_cnt < GND_MIC_SWAP_THRESHOLD) && (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) { @@ -747,7 +767,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) plug_type); if (plug_type != MBHC_PLUG_TYPE_GND_MIC_SWAP) { plug_type = wcd_mbhc_get_plug_from_adc( - output_mv); + mbhc, output_mv); if (!spl_hs_reported && spl_hs_count == WCD_MBHC_SPL_HS_CNT) { spl_hs_reported = true; diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index 6b3dd8626da5..7e91a6e3c121 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -1838,6 +1838,8 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, struct snd_soc_card *card = codec->component.card; const char *hph_switch = "qcom,msm-mbhc-hphl-swh"; const char *gnd_switch = "qcom,msm-mbhc-gnd-swh"; + const char *hs_thre = "qcom,msm-mbhc-hs-mic-max-threshold-mv"; + const char *hph_thre = "qcom,msm-mbhc-hs-mic-min-threshold-mv"; pr_debug("%s: enter\n", __func__); @@ -1855,6 +1857,18 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, goto err; } + ret = of_property_read_u32(card->dev->of_node, hs_thre, + &(mbhc->hs_thr)); + if (ret) + dev_dbg(card->dev, + "%s: missing %s in dt node\n", __func__, hs_thre); + + ret = of_property_read_u32(card->dev->of_node, hph_thre, + &(mbhc->hph_thr)); + if (ret) + dev_dbg(card->dev, + "%s: missing %s in dt node\n", __func__, hph_thre); + ret = of_property_read_u32_array(card->dev->of_node, "qcom,msm-mbhc-moist-cfg", hph_moist_config, 3); diff --git a/asoc/codecs/wcd-mbhc-v2.h b/asoc/codecs/wcd-mbhc-v2.h index c8714fc3abbc..d180cde53413 100644 --- a/asoc/codecs/wcd-mbhc-v2.h +++ b/asoc/codecs/wcd-mbhc-v2.h @@ -518,6 +518,8 @@ struct wcd_mbhc { bool in_swch_irq_handler; bool hphl_swh; /*track HPHL switch NC / NO */ bool gnd_swh; /*track GND switch NC / NO */ + u32 hs_thr; + u32 hph_thr; u32 moist_vref; u32 moist_iref; u32 moist_rref; From 87c83b72af7e8718918a6dd4fce63af4072e6774 Mon Sep 17 00:00:00 2001 From: Vidyakumar Athota Date: Tue, 19 Sep 2017 12:06:46 -0700 Subject: [PATCH 039/276] ipc: fix out of bounds read issue If userspace sends non-null terminated channel name then out of bounds read is possible while printing channel name. Fix this issue by validating channel name before use. Change-Id: Ide270a54be14b280e87360836a0fad1302075660 Signed-off-by: Vidyakumar Athota --- ipc/wcd-dsp-glink.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/ipc/wcd-dsp-glink.c b/ipc/wcd-dsp-glink.c index 095205c52c18..cab7c3b7c4a2 100644 --- a/ipc/wcd-dsp-glink.c +++ b/ipc/wcd-dsp-glink.c @@ -634,6 +634,21 @@ static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv, memcpy(&ch[i]->ch_cfg, payload, ch_cfg_size); payload += ch_cfg_size; + /* check ch name is valid string or not */ + for (j = 0; j < WDSP_CH_NAME_MAX_LEN; j++) { + if (ch[i]->ch_cfg.name[j] == '\0') + break; + } + + if (j == WDSP_CH_NAME_MAX_LEN) { + dev_err_ratelimited(wpriv->dev, "%s: Wrong channel name\n", + __func__); + kfree(ch[i]); + ch[i] = NULL; + ret = -EINVAL; + goto err_ch_mem; + } + mutex_init(&ch[i]->mutex); ch[i]->wpriv = wpriv; INIT_WORK(&ch[i]->lcl_ch_open_wrk, wdsp_glink_lcl_ch_open_wrk); @@ -906,8 +921,6 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, ret = -EINVAL; goto free_buf; } - dev_dbg(wpriv->dev, "%s: requested ch_name: %s, pkt_size: %zd\n", - __func__, cpkt->ch_name, pkt_max_size); for (i = 0; i < wpriv->no_of_channels; i++) { if (wpriv->ch && wpriv->ch[i] && (!strcmp(cpkt->ch_name, @@ -922,6 +935,8 @@ static ssize_t wdsp_glink_write(struct file *file, const char __user *buf, ret = -EINVAL; goto free_buf; } + dev_dbg(wpriv->dev, "%s: requested ch_name: %s, pkt_size: %zd\n", + __func__, cpkt->ch_name, pkt_max_size); ret = wait_event_timeout(tx_buf->ch->ch_connect_wait, (tx_buf->ch->channel_state == From 83716b9a69a414977da6075e1470b54e85d91752 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Thu, 14 Sep 2017 12:13:13 +0530 Subject: [PATCH 040/276] ASoC: wsa881x: fix wsa881x speaker damage wsa881x speaker damage has been reported due to DC at wsa output. Add a register setting which will reset the soundwire when there are any DC or synchronization issues. CRs-Fixed: 2086505 Change-Id: Id2bfb876a5bace891befa613153a88ee54033d76 Signed-off-by: Vatsal Bucha --- asoc/codecs/wsa881x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/asoc/codecs/wsa881x.c b/asoc/codecs/wsa881x.c index 456f68c81954..bff0ad539792 100644 --- a/asoc/codecs/wsa881x.c +++ b/asoc/codecs/wsa881x.c @@ -968,6 +968,8 @@ static void wsa881x_init(struct snd_soc_codec *codec) wsa881x->version = snd_soc_read(codec, WSA881X_CHIP_ID1); wsa881x_regmap_defaults(wsa881x->regmap, wsa881x->version); + /* Enable software reset output from soundwire slave */ + snd_soc_update_bits(codec, WSA881X_SWR_RESET_EN, 0x07, 0x07); /* Bring out of analog reset */ snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x02, 0x02); /* Bring out of digital reset */ From 39603aff7b1f33bac2fb7571b47b633118a35d27 Mon Sep 17 00:00:00 2001 From: Rohit Kumar Date: Mon, 11 Sep 2017 00:00:37 +0530 Subject: [PATCH 041/276] ipc: apr_v2: export symbols used by WCD driver Add EXPORT_SYMBOL to symbols used by codec driver. Codec driver is built as separate module and requires symbols called to be exported. Change-Id: I3d1bc6c8cbfc1b92f790b700ecfeaff211ddcff0 Signed-off-by: Rohit Kumar --- ipc/apr_v2.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ipc/apr_v2.c b/ipc/apr_v2.c index d330571f3908..7e6331009458 100644 --- a/ipc/apr_v2.c +++ b/ipc/apr_v2.c @@ -19,10 +19,16 @@ #include #include +/** + * apr_get_subsys_state - get Q6 subsys status + * + * Returns apr_subsys_state + */ enum apr_subsys_state apr_get_subsys_state(void) { return apr_get_q6_state(); } +EXPORT_SYMBOL(apr_get_subsys_state); void apr_set_subsys_state(void) { From e169e990eabe69efc1d43b1cea17caec61d99268 Mon Sep 17 00:00:00 2001 From: Satya Krishna Pindiproli Date: Fri, 22 Sep 2017 11:34:35 +0530 Subject: [PATCH 042/276] audio-lnx: dsp: codecs: modify API to configure output PCM block Pass valid channel count and sample rate information to configure AAC decoder component and modify the API used to configure the output PCM block when multi AAC decoder component is used in non-tunnelled mode. Change-Id: I5938f316a39b722924416b6910e6c8b835e7855e Signed-off-by: Satya Krishna Pindiproli --- dsp/codecs/audio_aac.c | 4 +++- dsp/codecs/audio_multi_aac.c | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/dsp/codecs/audio_aac.c b/dsp/codecs/audio_aac.c index 0f371fd1aa48..006b21c6793a 100644 --- a/dsp/codecs/audio_aac.c +++ b/dsp/codecs/audio_aac.c @@ -48,7 +48,9 @@ static long audio_ioctl_shared(struct file *file, unsigned int cmd, audio->ac->session); if (audio->feedback == NON_TUNNEL_MODE) { /* Configure PCM output block */ - rc = q6asm_enc_cfg_blk_pcm(audio->ac, 0, 0); + 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; diff --git a/dsp/codecs/audio_multi_aac.c b/dsp/codecs/audio_multi_aac.c index 01c3dc5ada58..5d407e0c5e61 100644 --- a/dsp/codecs/audio_multi_aac.c +++ b/dsp/codecs/audio_multi_aac.c @@ -59,9 +59,9 @@ static long audio_ioctl_shared(struct file *file, unsigned int cmd, 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); + 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; From 13da482a7024da66730566a1f47d83781a535166 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 18 Sep 2017 17:46:30 -0700 Subject: [PATCH 043/276] dsp: fix logic to send AFE calibration for TDM AFE ports AFE calibration should be sent even when the number of group ports is 1. Remove this condition from API to send AFE calibration for any number of group ports. Change-Id: I7f0c8ab739da2831e17e5dbbaf013cf99f2e6d88 Signed-off-by: Xiaoyu Ye --- dsp/q6afe.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index a6041b377525..0289144006fe 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -2644,10 +2644,9 @@ int afe_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port, this_afe.dev_acdb_id[index] = this_afe.rt_cb(port_id); } - /* Also send the topology id here if multiple ports: */ + /* 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) && - num_groups > 1) { + 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); @@ -2709,14 +2708,12 @@ int afe_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port, ret = -EINVAL; goto fail_cmd; } - /* slot mapping is not need if there is only one group */ - if (num_groups > 1) { - 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; - } + + 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) { From a507793028a15cf331f560c11d84f9e5f10b318b Mon Sep 17 00:00:00 2001 From: Rohit Kumar Date: Sun, 10 Sep 2017 22:05:05 +0530 Subject: [PATCH 044/276] asoc: msm: add support for quinary interfaces in dai and routing driver Add support for dais and routing for Quinary MI2S, AUXPCM and TDM interfaces. Change-Id: I50c3099a658998bab805c4c6a5aa447e85f3e7c6 Signed-off-by: Rohit Kumar --- asoc/msm-dai-q6-v2.c | 629 ++++++++++++ asoc/msm-pcm-routing-v2.c | 1960 +++++++++++++++++++++++++++++++++--- asoc/msm-pcm-routing-v2.h | 36 + include/dsp/apr_audio-v2.h | 60 +- 4 files changed, 2552 insertions(+), 133 deletions(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 898cb1b0f674..2802a4fa170a 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -31,6 +31,7 @@ #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 MSM_DAI_QUIN_AUXPCM_DT_DEV_ID 5 #define spdif_clock_value(rate) (2*rate*32*2) @@ -155,6 +156,22 @@ enum { IDX_QUATERNARY_TDM_TX_5, IDX_QUATERNARY_TDM_TX_6, IDX_QUATERNARY_TDM_TX_7, + IDX_QUINARY_TDM_RX_0, + IDX_QUINARY_TDM_RX_1, + IDX_QUINARY_TDM_RX_2, + IDX_QUINARY_TDM_RX_3, + IDX_QUINARY_TDM_RX_4, + IDX_QUINARY_TDM_RX_5, + IDX_QUINARY_TDM_RX_6, + IDX_QUINARY_TDM_RX_7, + IDX_QUINARY_TDM_TX_0, + IDX_QUINARY_TDM_TX_1, + IDX_QUINARY_TDM_TX_2, + IDX_QUINARY_TDM_TX_3, + IDX_QUINARY_TDM_TX_4, + IDX_QUINARY_TDM_TX_5, + IDX_QUINARY_TDM_TX_6, + IDX_QUINARY_TDM_TX_7, IDX_TDM_MAX, }; @@ -167,6 +184,8 @@ enum { IDX_GROUP_TERTIARY_TDM_TX, IDX_GROUP_QUATERNARY_TDM_RX, IDX_GROUP_QUATERNARY_TDM_TX, + IDX_GROUP_QUINARY_TDM_RX, + IDX_GROUP_QUINARY_TDM_TX, IDX_GROUP_TDM_MAX, }; @@ -394,6 +413,26 @@ int msm_dai_q6_get_group_idx(u16 id) case AFE_PORT_ID_QUATERNARY_TDM_TX_6: case AFE_PORT_ID_QUATERNARY_TDM_TX_7: return IDX_GROUP_QUATERNARY_TDM_TX; + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: + return IDX_GROUP_QUINARY_TDM_RX; + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_7: + return IDX_GROUP_QUINARY_TDM_TX; default: return -EINVAL; } } @@ -529,6 +568,38 @@ int msm_dai_q6_get_port_idx(u16 id) return IDX_QUATERNARY_TDM_RX_7; case AFE_PORT_ID_QUATERNARY_TDM_TX_7: return IDX_QUATERNARY_TDM_TX_7; + case AFE_PORT_ID_QUINARY_TDM_RX: + return IDX_QUINARY_TDM_RX_0; + case AFE_PORT_ID_QUINARY_TDM_TX: + return IDX_QUINARY_TDM_TX_0; + case AFE_PORT_ID_QUINARY_TDM_RX_1: + return IDX_QUINARY_TDM_RX_1; + case AFE_PORT_ID_QUINARY_TDM_TX_1: + return IDX_QUINARY_TDM_TX_1; + case AFE_PORT_ID_QUINARY_TDM_RX_2: + return IDX_QUINARY_TDM_RX_2; + case AFE_PORT_ID_QUINARY_TDM_TX_2: + return IDX_QUINARY_TDM_TX_2; + case AFE_PORT_ID_QUINARY_TDM_RX_3: + return IDX_QUINARY_TDM_RX_3; + case AFE_PORT_ID_QUINARY_TDM_TX_3: + return IDX_QUINARY_TDM_TX_3; + case AFE_PORT_ID_QUINARY_TDM_RX_4: + return IDX_QUINARY_TDM_RX_4; + case AFE_PORT_ID_QUINARY_TDM_TX_4: + return IDX_QUINARY_TDM_TX_4; + case AFE_PORT_ID_QUINARY_TDM_RX_5: + return IDX_QUINARY_TDM_RX_5; + case AFE_PORT_ID_QUINARY_TDM_TX_5: + return IDX_QUINARY_TDM_TX_5; + case AFE_PORT_ID_QUINARY_TDM_RX_6: + return IDX_QUINARY_TDM_RX_6; + case AFE_PORT_ID_QUINARY_TDM_TX_6: + return IDX_QUINARY_TDM_TX_6; + case AFE_PORT_ID_QUINARY_TDM_RX_7: + return IDX_QUINARY_TDM_RX_7; + case AFE_PORT_ID_QUINARY_TDM_TX_7: + return IDX_QUINARY_TDM_TX_7; default: return -EINVAL; } } @@ -908,6 +979,14 @@ static int msm_dai_q6_auxpcm_prepare(struct snd_pcm_substream *substream, aux_dai_data->clk_set.clk_id = Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT; break; + case MSM_DAI_QUIN_AUXPCM_DT_DEV_ID: + if (pcm_clk_rate) + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_QUIN_PCM_IBIT; + else + aux_dai_data->clk_set.clk_id = + Q6AFE_LPASS_CLK_ID_QUIN_PCM_EBIT; + break; default: dev_err(dai->dev, "%s: AUXPCM id: %d not supported\n", __func__, dai->id); @@ -1146,6 +1225,32 @@ static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_dai[] = { .probe = msm_dai_q6_aux_pcm_probe, .remove = msm_dai_q6_dai_auxpcm_remove, }, + { + .playback = { + .stream_name = "Quin AUX PCM Playback", + .aif_name = "QUIN_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 = "Quin AUX PCM Capture", + .aif_name = "QUIN_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_QUIN_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, @@ -3044,6 +3149,11 @@ static int msm_auxpcm_dev_probe(struct platform_device *pdev) dai_data->tx_pid = AFE_PORT_ID_QUATERNARY_PCM_TX; pdev->id = MSM_DAI_QUAT_AUXPCM_DT_DEV_ID; i = 3; + } else if (!strcmp(intf_name, "quinary")) { + dai_data->rx_pid = AFE_PORT_ID_QUINARY_PCM_RX; + dai_data->tx_pid = AFE_PORT_ID_QUINARY_PCM_TX; + pdev->id = MSM_DAI_QUIN_AUXPCM_DT_DEV_ID; + i = 4; } else { dev_err(&pdev->dev, "%s: invalid DT intf name %s\n", __func__, intf_name); @@ -5052,6 +5162,13 @@ static int msm_dai_q6_tdm_set_clk_param(u32 group_id, else clk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT; break; + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_TX: + if (mode) + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUIN_TDM_IBIT; + else + clk_set->clk_id = Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT; + break; default: return -EINVAL; } @@ -5510,6 +5627,54 @@ static const struct snd_kcontrol_new tdm_config_controls_data_format[] = { 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), + SOC_ENUM_EXT("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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[] = { @@ -5705,6 +5870,54 @@ static const struct snd_kcontrol_new tdm_config_controls_header_type[] = { 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), + SOC_ENUM_EXT("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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[] = { @@ -5964,6 +6177,70 @@ static const struct snd_kcontrol_new tdm_config_controls_header[] = { SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8, msm_dai_q6_tdm_header_get, msm_dai_q6_tdm_header_put), + SOC_SINGLE_MULTI_EXT("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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("QUIN_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( @@ -6187,6 +6464,14 @@ static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai, 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_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: tdm_group->nslots_per_frame = slots; tdm_group->slot_width = slot_width; tdm_group->slot_mask = rx_mask & cap_mask; @@ -6223,6 +6508,14 @@ static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai, 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_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_7: tdm_group->nslots_per_frame = slots; tdm_group->slot_width = slot_width; tdm_group->slot_mask = tx_mask & cap_mask; @@ -6316,6 +6609,14 @@ static int msm_dai_q6_tdm_set_channel_map(struct snd_soc_dai *dai, 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_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: if (!rx_slot) { dev_err(dai->dev, "%s: rx slot not found\n", __func__); return -EINVAL; @@ -6366,6 +6667,14 @@ static int msm_dai_q6_tdm_set_channel_map(struct snd_soc_dai *dai, 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_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_7: if (!tx_slot) { dev_err(dai->dev, "%s: tx slot not found\n", __func__); return -EINVAL; @@ -7984,6 +8293,326 @@ static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = { .probe = msm_dai_q6_dai_tdm_probe, .remove = msm_dai_q6_dai_tdm_remove, }, + { + .playback = { + .stream_name = "Quinary TDM0 Playback", + .aif_name = "QUIN_TDM_RX_0", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM1 Playback", + .aif_name = "QUIN_TDM_RX_1", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM2 Playback", + .aif_name = "QUIN_TDM_RX_2", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM3 Playback", + .aif_name = "QUIN_TDM_RX_3", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM4 Playback", + .aif_name = "QUIN_TDM_RX_4", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM5 Playback", + .aif_name = "QUIN_TDM_RX_5", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM6 Playback", + .aif_name = "QUIN_TDM_RX_6", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .playback = { + .stream_name = "Quinary TDM7 Playback", + .aif_name = "QUIN_TDM_RX_7", + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_RX_7, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM0 Capture", + .aif_name = "QUIN_TDM_TX_0", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM1 Capture", + .aif_name = "QUIN_TDM_TX_1", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_1, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM2 Capture", + .aif_name = "QUIN_TDM_TX_2", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_2, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM3 Capture", + .aif_name = "QUIN_TDM_TX_3", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_3, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM4 Capture", + .aif_name = "QUIN_TDM_TX_4", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_4, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM5 Capture", + .aif_name = "QUIN_TDM_TX_5", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_5, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM6 Capture", + .aif_name = "QUIN_TDM_TX_6", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_TDM_TX_6, + .probe = msm_dai_q6_dai_tdm_probe, + .remove = msm_dai_q6_dai_tdm_remove, + }, + { + .capture = { + .stream_name = "Quinary TDM7 Capture", + .aif_name = "QUIN_TDM_TX_7", + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800, + .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 = 352800, + }, + .ops = &msm_dai_q6_tdm_ops, + .id = AFE_PORT_ID_QUINARY_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 = { diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 788946ad8ea1..2af966454726 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -67,6 +67,7 @@ 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 quin_mi2s_switch_enable; static int fm_pcmrx_switch_enable; static int usb_switch_enable; static int lsm_port_index; @@ -497,6 +498,38 @@ struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = { 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}, + { AFE_PORT_ID_QUINARY_TDM_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_RX_0}, + { AFE_PORT_ID_QUINARY_TDM_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_TX_0}, + { AFE_PORT_ID_QUINARY_TDM_RX_1, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_RX_1}, + { AFE_PORT_ID_QUINARY_TDM_TX_1, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_TX_1}, + { AFE_PORT_ID_QUINARY_TDM_RX_2, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_RX_2}, + { AFE_PORT_ID_QUINARY_TDM_TX_2, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_TX_2}, + { AFE_PORT_ID_QUINARY_TDM_RX_3, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_RX_3}, + { AFE_PORT_ID_QUINARY_TDM_TX_3, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_TX_3}, + { AFE_PORT_ID_QUINARY_TDM_RX_4, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_RX_4}, + { AFE_PORT_ID_QUINARY_TDM_TX_4, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_TX_4}, + { AFE_PORT_ID_QUINARY_TDM_RX_5, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_RX_5}, + { AFE_PORT_ID_QUINARY_TDM_TX_5, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_TX_5}, + { AFE_PORT_ID_QUINARY_TDM_RX_6, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_RX_6}, + { AFE_PORT_ID_QUINARY_TDM_TX_6, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_TX_6}, + { AFE_PORT_ID_QUINARY_TDM_RX_7, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_TDM_RX_7}, + { AFE_PORT_ID_QUINARY_TDM_TX_7, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_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}, @@ -513,6 +546,10 @@ struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = { 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_QUINARY_PCM_RX, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_AUXPCM_RX}, + { AFE_PORT_ID_QUINARY_PCM_TX, 0, {0}, {0}, 0, 0, 0, 0, {0}, + LPASS_BE_QUIN_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}, @@ -2315,6 +2352,36 @@ static int msm_routing_put_quat_mi2s_switch_mixer( return 1; } +static int msm_routing_get_quin_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = quin_mi2s_switch_enable; + pr_debug("%s: QUIN MI2S Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_put_quin_mi2s_switch_mixer( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: QUIN 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); + quin_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) { @@ -2787,8 +2854,13 @@ static const char *const be_name[] = { "QUAT_TDM_RX_2", "QUAT_TDM_TX_2", "QUAT_TDM_RX_3", "QUAT_TDM_TX_3", "QUAT_TDM_RX_4", "QUAT_TDM_TX_4", "QUAT_TDM_RX_5", "QUAT_TDM_TX_5", "QUAT_TDM_RX_6", "QUAT_TDM_TX_6", "QUAT_TDM_RX_7", "QUAT_TDM_TX_7", +"QUIN_TDM_RX_0", "QUIN_TDM_TX_0", "QUIN_TDM_RX_1", "QUIN_TDM_TX_1", +"QUIN_TDM_RX_2", "QUIN_TDM_TX_2", "QUIN_TDM_RX_3", "QUIN_TDM_TX_3", +"QUIN_TDM_RX_4", "QUIN_TDM_TX_4", "QUIN_TDM_RX_5", "QUIN_TDM_TX_5", +"QUIN_TDM_RX_6", "QUIN_TDM_TX_6", "QUIN_TDM_RX_7", "QUIN_TDM_TX_7", "INT_BT_A2DP_RX", "USB_RX", "USB_TX", "DISPLAY_PORT_RX", "TERT_AUXPCM_RX", "TERT_AUXPCM_TX", "QUAT_AUXPCM_RX", "QUAT_AUXPCM_TX", +"QUIN_AUXPCM_RX", "QUIN_AUXPCM_TX", "INT0_MI2S_RX", "INT0_MI2S_TX", "INT1_MI2S_RX", "INT1_MI2S_TX", "INT2_MI2S_RX", "INT2_MI2S_TX", "INT3_MI2S_RX", "INT3_MI2S_TX", "INT4_MI2S_RX", "INT4_MI2S_TX", "INT5_MI2S_RX", "INT5_MI2S_TX", @@ -5327,6 +5399,56 @@ static const struct snd_kcontrol_new quat_auxpcm_rx_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new quin_auxpcm_rx_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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, @@ -6410,6 +6532,273 @@ static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new quin_tdm_rx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_tx_0_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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 quin_tdm_rx_1_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_rx_2_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new quin_tdm_rx_3_mixer_controls[] = { + SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 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, @@ -6450,6 +6839,9 @@ static const struct snd_kcontrol_new mmul1_mixer_controls[] = { 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("QUIN_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUIN_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), @@ -6522,6 +6914,18 @@ static const struct snd_kcontrol_new mmul1_mixer_controls[] = { 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("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_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), @@ -6618,6 +7022,18 @@ static const struct snd_kcontrol_new mmul2_mixer_controls[] = { 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("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_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), @@ -6654,6 +7070,9 @@ static const struct snd_kcontrol_new mmul3_mixer_controls[] = { 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("QUIN_AUX_PCM_TX", MSM_BACKEND_DAI_QUIN_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), @@ -6714,7 +7133,19 @@ static const struct snd_kcontrol_new mmul3_mixer_controls[] = { 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), -}; + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_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, @@ -6786,6 +7217,18 @@ static const struct snd_kcontrol_new mmul4_mixer_controls[] = { 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("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_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), @@ -6834,6 +7277,9 @@ static const struct snd_kcontrol_new mmul5_mixer_controls[] = { 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("QUIN_AUX_PCM_TX", MSM_BACKEND_DAI_QUIN_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), @@ -6894,6 +7340,18 @@ static const struct snd_kcontrol_new mmul5_mixer_controls[] = { 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("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_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), @@ -6936,6 +7394,9 @@ static const struct snd_kcontrol_new mmul6_mixer_controls[] = { 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("QUIN_MI2S_TX", MSM_BACKEND_DAI_QUINARY_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), @@ -6996,6 +7457,18 @@ static const struct snd_kcontrol_new mmul6_mixer_controls[] = { 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("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_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), @@ -7089,6 +7562,18 @@ static const struct snd_kcontrol_new mmul8_mixer_controls[] = { 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("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_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), @@ -7254,6 +7739,18 @@ static const struct snd_kcontrol_new mmul9_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3, MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new mmul10_mixer_controls[] = { @@ -7424,6 +7921,9 @@ static const struct snd_kcontrol_new mmul20_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, MSM_FRONTEND_DAI_MULTIMEDIA20, 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_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), SOC_SINGLE_EXT("PRI_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_TX_0, MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), @@ -7472,6 +7972,18 @@ static const struct snd_kcontrol_new mmul20_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3, MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = { @@ -8113,6 +8625,39 @@ static const struct snd_kcontrol_new quat_aux_pcm_rx_voice_mixer_controls[] = { msm_routing_put_voice_mixer), }; +static const struct snd_kcontrol_new quin_aux_pcm_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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, @@ -8300,6 +8845,9 @@ static const struct snd_kcontrol_new tx_voice_mixer_controls[] = { 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("QUIN_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_QUIN_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), @@ -8348,6 +8896,9 @@ static const struct snd_kcontrol_new tx_voice2_mixer_controls[] = { 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("QUIN_AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_QUIN_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), @@ -8390,6 +8941,9 @@ static const struct snd_kcontrol_new tx_volte_mixer_controls[] = { 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("QUIN_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_QUIN_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), @@ -8435,6 +8989,9 @@ static const struct snd_kcontrol_new tx_vowlan_mixer_controls[] = { 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("QUIN_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_QUIN_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), @@ -8483,6 +9040,9 @@ static const struct snd_kcontrol_new tx_voicemmode1_mixer_controls[] = { 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("QUIN_AUX_PCM_TX_MMode1", + MSM_BACKEND_DAI_QUIN_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), @@ -8534,6 +9094,9 @@ static const struct snd_kcontrol_new tx_voicemmode2_mixer_controls[] = { 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("QUIN_AUX_PCM_TX_MMode2", + MSM_BACKEND_DAI_QUIN_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), @@ -8582,6 +9145,9 @@ static const struct snd_kcontrol_new tx_voip_mixer_controls[] = { 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("QUIN_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_QUIN_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), @@ -8642,6 +9208,9 @@ static const struct snd_kcontrol_new tx_voice_stub_mixer_controls[] = { 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("QUIN_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUIN_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), @@ -8681,6 +9250,9 @@ static const struct snd_kcontrol_new tx_voice2_stub_mixer_controls[] = { 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("QUIN_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUIN_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), @@ -8726,6 +9298,9 @@ static const struct snd_kcontrol_new tx_volte_stub_mixer_controls[] = { 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("QUIN_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUIN_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), @@ -8774,6 +9349,9 @@ static const struct snd_kcontrol_new tx_qchat_mixer_controls[] = { 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("QUIN_AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_QUIN_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), @@ -8810,6 +9388,9 @@ static const struct snd_kcontrol_new int0_mi2s_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_INT0_MI2S_RX, MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_MI2S_TX", MSM_BACKEND_DAI_INT0_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT0_MI2S_RX, MSM_BACKEND_DAI_INT3_MI2S_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), @@ -8840,6 +9421,9 @@ static const struct snd_kcontrol_new int4_mi2s_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_INT4_MI2S_RX, MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_MI2S_TX", MSM_BACKEND_DAI_INT4_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT4_MI2S_RX, MSM_BACKEND_DAI_INT3_MI2S_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), @@ -8885,6 +9469,9 @@ static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = { 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("QUIN_AUXPCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUIN_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), @@ -8900,6 +9487,9 @@ static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = { 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("QUIN_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUINARY_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), @@ -8915,6 +9505,9 @@ static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = { 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), + SOC_SINGLE_EXT("QUIN_MI2S_RX", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_BACKEND_DAI_QUINARY_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[] = { @@ -8971,6 +9564,18 @@ static const struct snd_kcontrol_new quat_auxpcm_rx_port_mixer_controls[] = { msm_routing_put_port_mixer), }; +static const struct snd_kcontrol_new quin_auxpcm_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("QUIN_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_QUIN_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_QUIN_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, @@ -9153,11 +9758,38 @@ static const struct snd_kcontrol_new quat_mi2s_rx_port_mixer_controls[] = { 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), + SOC_SINGLE_EXT("QUIN_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), }; +static const struct snd_kcontrol_new quin_mi2s_rx_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUINARY_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_QUINARY_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_QUINARY_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_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_MI2S_TX", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), +}; + static const struct snd_kcontrol_new pri_tdm_rx_0_port_mixer_controls[] = { SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_TDM_RX_0, MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, @@ -9223,6 +9855,22 @@ static const struct snd_kcontrol_new pri_tdm_rx_0_port_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_1_port_mixer_controls[] = { @@ -9290,6 +9938,22 @@ static const struct snd_kcontrol_new pri_tdm_rx_1_port_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_2_port_mixer_controls[] = { @@ -9357,6 +10021,22 @@ static const struct snd_kcontrol_new pri_tdm_rx_2_port_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new pri_tdm_rx_3_port_mixer_controls[] = { @@ -9424,6 +10104,22 @@ static const struct snd_kcontrol_new pri_tdm_rx_3_port_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_PRI_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new sec_tdm_rx_0_port_mixer_controls[] = { @@ -9491,6 +10187,22 @@ static const struct snd_kcontrol_new sec_tdm_rx_0_port_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new sec_tdm_rx_1_port_mixer_controls[] = { @@ -9558,6 +10270,22 @@ static const struct snd_kcontrol_new sec_tdm_rx_1_port_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new sec_tdm_rx_2_port_mixer_controls[] = { @@ -9625,6 +10353,22 @@ static const struct snd_kcontrol_new sec_tdm_rx_2_port_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new sec_tdm_rx_3_port_mixer_controls[] = { @@ -9692,6 +10436,22 @@ static const struct snd_kcontrol_new sec_tdm_rx_3_port_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_SEC_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, 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[] = { @@ -9759,6 +10519,22 @@ static const struct snd_kcontrol_new tert_tdm_rx_0_port_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_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[] = { @@ -9826,6 +10602,22 @@ static const struct snd_kcontrol_new tert_tdm_rx_1_port_mixer_controls[] = { MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_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[] = { @@ -9857,377 +10649,805 @@ static const struct snd_kcontrol_new tert_tdm_rx_2_port_mixer_controls[] = { 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, + 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), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_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), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_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), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_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), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_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_TERT_TDM_RX_2, + 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_TERT_TDM_RX_2, + 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_TERT_TDM_RX_2, + 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_TERT_TDM_RX_2, + 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_TERT_TDM_RX_2, + 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_TERT_TDM_RX_2, + 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_TERT_TDM_RX_2, + 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_TERT_TDM_RX_2, + 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), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_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, +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_TERT_TDM_RX_3, + 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("QUAT_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3, - MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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_TERT_TDM_RX_3, + 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), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_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, +static const struct snd_kcontrol_new quin_tdm_rx_0_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUIN_TDM_RX_0, MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_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, +static const struct snd_kcontrol_new quin_tdm_rx_1_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUIN_TDM_RX_1, MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_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, +static const struct snd_kcontrol_new quin_tdm_rx_2_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUIN_TDM_RX_2, MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_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, +static const struct snd_kcontrol_new quin_tdm_rx_3_port_mixer_controls[] = { + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUIN_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, + SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUIN_TDM_RX_3, MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_0", MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_1", MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_2", MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, 1, 0, + msm_routing_get_port_mixer, + msm_routing_put_port_mixer), + SOC_SINGLE_EXT("QUIN_TDM_TX_3", MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_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[] = { @@ -10243,6 +11463,9 @@ static const struct snd_kcontrol_new tert_mi2s_rx_port_mixer_controls[] = { 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("QUIN_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_BACKEND_DAI_QUINARY_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), @@ -10264,6 +11487,9 @@ static const struct snd_kcontrol_new sec_mi2s_rx_port_mixer_controls[] = { 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("QUIN_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_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_SECONDARY_MI2S_RX, MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), @@ -10551,6 +11777,11 @@ static const struct snd_kcontrol_new quat_mi2s_rx_switch_mixer_controls = 0, 1, 0, msm_routing_get_quat_mi2s_switch_mixer, msm_routing_put_quat_mi2s_switch_mixer); +static const struct snd_kcontrol_new quin_mi2s_rx_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_get_quin_mi2s_switch_mixer, + msm_routing_put_quin_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, @@ -11660,6 +12891,9 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_AIF_IN("QUAT_MI2S_DL_HL", "Quaternary MI2S_RX Hostless Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_MI2S_DL_HL", + "Quinary MI2S_RX Hostless Playback", + 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("AUXPCM_DL_HL", "AUXPCM_HOSTLESS Playback", 0, 0, 0, 0), @@ -11686,6 +12920,9 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_UL_HL", "Quaternary MI2S_TX Hostless Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_MI2S_UL_HL", + "Quinary MI2S_TX Hostless Capture", + 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_0_DL_HL", "Primary TDM0 Hostless Playback", @@ -11879,6 +13116,54 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_7_UL_HL", "Quaternary TDM7 Hostless Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_0_DL_HL", + "Quinary TDM0 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_0_UL_HL", + "Quinary TDM0 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_1_DL_HL", + "Quinary TDM1 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_1_UL_HL", + "Quinary TDM1 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_2_DL_HL", + "Quinary TDM2 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_2_UL_HL", + "Quinary TDM2 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_3_DL_HL", + "Quinary TDM3 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_3_UL_HL", + "Quinary TDM3 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_4_DL_HL", + "Quinary TDM4 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_4_UL_HL", + "Quinary TDM4 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_5_DL_HL", + "Quinary TDM5 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_5_UL_HL", + "Quinary TDM5 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_6_DL_HL", + "Quinary TDM6 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_6_UL_HL", + "Quinary TDM6 Hostless Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_RX_7_DL_HL", + "Quinary TDM7 Hostless Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_7_UL_HL", + "Quinary TDM7 Hostless Capture", + 0, 0, 0, 0), /* LSM */ SND_SOC_DAPM_AIF_OUT("LSM1_UL_HL", "Listen 1 Audio Service Capture", @@ -12091,6 +13376,38 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_7", "Quaternary TDM7 Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_0", "Quinary TDM0 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_0", "Quinary TDM0 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_1", "Quinary TDM1 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_1", "Quinary TDM1 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_2", "Quinary TDM2 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_2", "Quinary TDM2 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_3", "Quinary TDM3 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_3", "Quinary TDM3 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_4", "Quinary TDM4 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_4", "Quinary TDM4 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_5", "Quinary TDM5 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_5", "Quinary TDM5 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_6", "Quinary TDM6 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_6", "Quinary TDM6 Capture", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("QUIN_TDM_RX_7", "Quinary TDM7 Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_TDM_TX_7", "Quinary TDM7 Capture", + 0, 0, 0, 0), /* incall */ SND_SOC_DAPM_AIF_OUT("VOICE_PLAYBACK_TX", "Voice Farend Playback", 0, 0, 0, 0), @@ -12124,6 +13441,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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_OUT("QUIN_AUX_PCM_RX", "Quin AUX PCM Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("QUIN_AUX_PCM_TX", "Quin 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", @@ -12176,6 +13497,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { &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("QUIN_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0, + &quin_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, @@ -12295,6 +13618,21 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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("QUIN_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_0_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_tx_0_mixer_controls, + ARRAY_SIZE(quin_tdm_tx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_1_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_2_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_3_mixer_controls, + ARRAY_SIZE(quin_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, @@ -12333,6 +13671,9 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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)), + SND_SOC_DAPM_MIXER("QUIN_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + quin_auxpcm_rx_mixer_controls, + ARRAY_SIZE(quin_auxpcm_rx_mixer_controls)), /* incall */ SND_SOC_DAPM_MIXER("Incall_Music Audio Mixer", SND_SOC_NOPM, 0, 0, incall_music_delivery_mixer_controls, @@ -12389,6 +13730,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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("QUIN_AUX_PCM_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, + quin_aux_pcm_rx_voice_mixer_controls, + ARRAY_SIZE(quin_aux_pcm_rx_voice_mixer_controls)), SND_SOC_DAPM_MIXER("HDMI_RX_Voice Mixer", SND_SOC_NOPM, 0, 0, hdmi_rx_voice_mixer_controls, @@ -12494,6 +13839,9 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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("QUIN_AUXPCM_RX Port Mixer", + SND_SOC_NOPM, 0, 0, quin_auxpcm_rx_port_mixer_controls, + ARRAY_SIZE(quin_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)), @@ -12532,6 +13880,9 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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("QUIN_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, + quin_mi2s_rx_port_mixer_controls, + ARRAY_SIZE(quin_mi2s_rx_port_mixer_controls)), SND_SOC_DAPM_MIXER("PRI_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, pri_tdm_rx_0_port_mixer_controls, ARRAY_SIZE(pri_tdm_rx_0_port_mixer_controls)), @@ -12580,6 +13931,18 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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("QUIN_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_0_port_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_0_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_1_port_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_1_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_2_port_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_2_port_mixer_controls)), + SND_SOC_DAPM_MIXER("QUIN_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0, + quin_tdm_rx_3_port_mixer_controls, + ARRAY_SIZE(quin_tdm_rx_3_port_mixer_controls)), SND_SOC_DAPM_MIXER("INT0_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0, int0_mi2s_rx_port_mixer_controls, ARRAY_SIZE(int0_mi2s_rx_port_mixer_controls)), @@ -13355,42 +14718,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia20", "MM_DL20"}, {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 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"}, - - {"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"}, - {"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"}, @@ -13466,6 +14793,100 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia20", "MM_DL20"}, {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Audio Mixer"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUIN_TDM_RX_0", NULL, "QUIN_TDM_RX_0 Audio Mixer"}, + + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_TDM_TX_0", NULL, "QUIN_TDM_TX_0 Audio Mixer"}, + + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_TDM_RX_1 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUIN_TDM_RX_1", NULL, "QUIN_TDM_RX_1 Audio Mixer"}, + + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_TDM_RX_2 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUIN_TDM_RX_2", NULL, "QUIN_TDM_RX_2 Audio Mixer"}, + + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_TDM_RX_3 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUIN_TDM_RX_3", NULL, "QUIN_TDM_RX_3 Audio Mixer"}, + {"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"}, {"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"}, {"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"}, @@ -13504,6 +14925,9 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia5 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, {"MultiMedia10 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, {"MultiMedia16 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"}, + {"MultiMedia1 Mixer", "QUIN_AUXPCM_UL_TX", "QUIN_AUX_PCM_TX"}, + {"MultiMedia3 Mixer", "QUIN_AUX_PCM_TX", "QUIN_AUX_PCM_TX"}, + {"MultiMedia5 Mixer", "QUIN_AUX_PCM_TX", "QUIN_AUX_PCM_TX"}, {"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia2 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"}, {"MultiMedia2 Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"}, @@ -13530,6 +14954,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia6 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, {"MultiMedia6 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, {"MultiMedia6 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"MultiMedia6 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, {"MultiMedia1 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, {"MultiMedia1 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, @@ -13547,6 +14972,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia1 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia1 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia1 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia1 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"MultiMedia2 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, {"MultiMedia2 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, @@ -13564,6 +14993,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia2 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia2 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia2 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia2 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"MultiMedia3 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, {"MultiMedia3 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, @@ -13581,6 +15014,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia3 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia3 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia3 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia3 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"MultiMedia4 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, {"MultiMedia4 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, @@ -13598,6 +15035,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia4 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia4 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia4 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia4 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"MultiMedia5 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, {"MultiMedia5 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, @@ -13615,6 +15056,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia5 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia5 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia5 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia5 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"MultiMedia6 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, {"MultiMedia6 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, @@ -13632,6 +15077,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia6 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia6 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia6 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia6 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"MultiMedia8 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, {"MultiMedia8 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, @@ -13649,6 +15098,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia8 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia8 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia8 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia8 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"MultiMedia9 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, {"MultiMedia9 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, @@ -13658,6 +15111,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia9 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, {"MultiMedia9 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"MultiMedia9 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia9 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia9 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia9 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia9 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"MultiMedia10 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, {"MultiMedia10 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, @@ -13671,6 +15128,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia20 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"MultiMedia20 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"MultiMedia20 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia20 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, {"MultiMedia20 Mixer", "PRI_TDM_TX_0", "PRI_TDM_TX_0"}, {"MultiMedia20 Mixer", "PRI_TDM_TX_1", "PRI_TDM_TX_1"}, {"MultiMedia20 Mixer", "PRI_TDM_TX_2", "PRI_TDM_TX_2"}, @@ -13687,6 +15145,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia20 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, {"MultiMedia20 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"MultiMedia20 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"MultiMedia20 Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"MultiMedia20 Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"MultiMedia20 Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"MultiMedia20 Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"MultiMedia1 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, {"MultiMedia2 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"}, @@ -13906,6 +15368,24 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX Audio Mixer"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"}, + {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, + {"QUIN_AUX_PCM_RX", NULL, "QUIN_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"}, @@ -14063,6 +15543,18 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX_Voice Mixer"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"QUIN_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"QUIN_AUX_PCM_RX", NULL, "QUIN_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"}, @@ -14161,6 +15653,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_MI2S_TX", "QUIN_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"}, @@ -14275,6 +15768,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_AUX_PCM_TX_Voice", "QUIN_AUX_PCM_TX"}, {"Voice_Tx Mixer", "SEC_MI2S_TX_Voice", "SEC_MI2S_TX"}, {"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"}, @@ -14292,6 +15786,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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_Tx Mixer", "QUIN_AUX_PCM_TX_Voice2", "QUIN_AUX_PCM_TX"}, {"VOICE2_UL", NULL, "Voice2_Tx Mixer"}, {"VoLTE_Tx Mixer", "PRI_TX_VoLTE", "PRI_I2S_TX"}, @@ -14305,6 +15800,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_AUX_PCM_TX_VoLTE", "QUIN_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"}, @@ -14321,6 +15817,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_AUX_PCM_TX_VoWLAN", "QUIN_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"}, @@ -14341,6 +15838,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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_Tx Mixer", "QUIN_AUX_PCM_TX_MMode1", "QUIN_AUX_PCM_TX"}, {"VoiceMMode1_Tx Mixer", "QUAT_TDM_TX_0_MMode1", "QUAT_TDM_TX_0"}, {"VOICEMMODE1_UL", NULL, "VoiceMMode1_Tx Mixer"}, @@ -14359,6 +15857,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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_Tx Mixer", "QUIN_AUX_PCM_TX_MMode2", "QUIN_AUX_PCM_TX"}, {"VOICEMMODE2_UL", NULL, "VoiceMMode2_Tx Mixer"}, {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"}, @@ -14375,6 +15874,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_AUX_PCM_TX_Voip", "QUIN_AUX_PCM_TX"}, {"Voip_Tx Mixer", "PRI_MI2S_TX_Voip", "PRI_MI2S_TX"}, {"VOIP_UL", NULL, "Voip_Tx Mixer"}, @@ -14482,6 +15982,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_AUX_PCM_TX_QCHAT", "QUIN_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"}, @@ -14521,6 +16022,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_MI2S_RX_DL_HL", "Switch", "QUAT_MI2S_DL_HL"}, {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX_DL_HL"}, + {"QUIN_MI2S_RX_DL_HL", "Switch", "QUIN_MI2S_DL_HL"}, + {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX_DL_HL"}, {"MI2S_UL_HL", NULL, "TERT_MI2S_TX"}, {"INT3_MI2S_UL_HL", NULL, "INT3_MI2S_TX"}, {"TERT_MI2S_UL_HL", NULL, "TERT_MI2S_TX"}, @@ -14564,6 +16067,14 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"QUIN_TDM_TX_0_UL_HL", NULL, "QUIN_TDM_TX_0"}, + {"QUIN_TDM_TX_1_UL_HL", NULL, "QUIN_TDM_TX_1"}, + {"QUIN_TDM_TX_2_UL_HL", NULL, "QUIN_TDM_TX_2"}, + {"QUIN_TDM_TX_3_UL_HL", NULL, "QUIN_TDM_TX_3"}, + {"QUIN_TDM_RX_0", NULL, "QUIN_TDM_RX_0_DL_HL"}, + {"QUIN_TDM_RX_1", NULL, "QUIN_TDM_RX_1_DL_HL"}, + {"QUIN_TDM_RX_2", NULL, "QUIN_TDM_RX_2_DL_HL"}, + {"QUIN_TDM_RX_3", NULL, "QUIN_TDM_RX_3_DL_HL"}, {"PRI_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"PRI_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, @@ -14581,6 +16092,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"PRI_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"PRI_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"PRI_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"PRI_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Port Mixer"}, {"PRI_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -14599,6 +16114,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"PRI_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"PRI_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"PRI_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"PRI_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"PRI_TDM_RX_1", NULL, "PRI_TDM_RX_1 Port Mixer"}, {"PRI_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -14617,6 +16136,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"PRI_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"PRI_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"PRI_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"PRI_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"PRI_TDM_RX_2", NULL, "PRI_TDM_RX_2 Port Mixer"}, {"PRI_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -14635,6 +16158,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"PRI_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"PRI_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"PRI_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"PRI_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"PRI_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"PRI_TDM_RX_3", NULL, "PRI_TDM_RX_3 Port Mixer"}, {"SEC_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -14653,6 +16180,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"SEC_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"SEC_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"SEC_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"SEC_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Port Mixer"}, {"SEC_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -14671,6 +16202,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"SEC_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"SEC_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"SEC_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"SEC_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"SEC_TDM_RX_1", NULL, "SEC_TDM_RX_1 Port Mixer"}, {"SEC_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -14689,6 +16224,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"SEC_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"SEC_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"SEC_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"SEC_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"SEC_TDM_RX_2", NULL, "SEC_TDM_RX_2 Port Mixer"}, {"SEC_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -14707,6 +16246,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, {"SEC_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"SEC_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"SEC_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"SEC_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"SEC_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"SEC_TDM_RX_3", NULL, "SEC_TDM_RX_3 Port Mixer"}, {"TERT_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -14725,6 +16268,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"TERT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"TERT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"TERT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_3", "QUIN_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"}, @@ -14743,6 +16290,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"TERT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"TERT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"TERT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_3", "QUIN_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"}, @@ -14761,6 +16312,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"TERT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"TERT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"TERT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_3", "QUIN_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"}, @@ -14779,6 +16334,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"TERT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"TERT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"TERT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_3", "QUIN_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"}, @@ -14797,6 +16356,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUAT_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_3", "QUIN_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"}, @@ -14815,6 +16378,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUAT_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_3", "QUIN_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"}, @@ -14833,6 +16400,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUAT_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_3", "QUIN_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"}, @@ -14851,12 +16422,105 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUAT_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Port Mixer"}, + {"QUIN_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUIN_TDM_RX_0 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUIN_TDM_RX_0 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUIN_TDM_RX_0 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUIN_TDM_RX_0 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUIN_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUIN_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Port Mixer"}, + + {"QUIN_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUIN_TDM_RX_1 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUIN_TDM_RX_1 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUIN_TDM_RX_1 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUIN_TDM_RX_1 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUIN_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUIN_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Port Mixer"}, + + {"QUIN_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUIN_TDM_RX_2 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUIN_TDM_RX_2 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUIN_TDM_RX_2 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUIN_TDM_RX_2 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUIN_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUIN_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Port Mixer"}, + + {"QUIN_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, + {"QUIN_TDM_RX_3 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"}, + {"QUIN_TDM_RX_3 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"}, + {"QUIN_TDM_RX_3 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"}, + {"QUIN_TDM_RX_3 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_0", "QUIN_TDM_TX_0"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, + {"QUIN_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, + {"QUIN_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Port Mixer"}, + {"INT0_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"INT0_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"INT0_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"INT0_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"INT0_MI2S_RX Port Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, {"INT0_MI2S_RX Port Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"INT0_MI2S_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, {"INT0_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, @@ -14868,6 +16532,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"INT4_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, {"INT4_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"INT4_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"INT4_MI2S_RX Port Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, {"INT4_MI2S_RX Port Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"INT4_MI2S_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"}, {"INT4_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, @@ -14884,11 +16549,13 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_AUXPCM_UL_TX", "QUIN_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", "QUIN_MI2S_TX", "QUIN_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"}, @@ -14923,6 +16590,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUXPCM_RX Port Mixer"}, + {"QUIN_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, + {"QUIN_AUXPCM_RX Port Mixer", "QUIN_AUXPCM_UL_TX", "QUIN_AUX_PCM_TX"}, + {"QUIN_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"QUIN_AUX_PCM_RX", NULL, "QUIN_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"}, @@ -14931,6 +16603,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_AUXPCM_UL_TX", "QUIN_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"}, @@ -15058,6 +16731,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_MI2S_TX", "QUIN_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"}, @@ -15068,6 +16742,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_MI2S_TX", "QUIN_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 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, @@ -15077,6 +16752,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, {"TERT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"TERT_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Port Mixer"}, @@ -15085,12 +16761,22 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_MI2S_TX", "QUIN_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 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, {"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Port Mixer"}, + {"QUIN_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"QUIN_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"QUIN_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"QUIN_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"QUIN_MI2S_RX Port Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"}, + {"QUIN_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"QUIN_MI2S_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX Port Mixer"}, + /* Backend Enablement */ {"BE_OUT", NULL, "PRI_I2S_RX"}, @@ -15126,6 +16812,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"BE_OUT", NULL, "SEC_AUX_PCM_RX"}, {"BE_OUT", NULL, "TERT_AUX_PCM_RX"}, {"BE_OUT", NULL, "QUAT_AUX_PCM_RX"}, + {"BE_OUT", NULL, "QUIN_AUX_PCM_RX"}, {"BE_OUT", NULL, "INT_BT_SCO_RX"}, {"BE_OUT", NULL, "INT_FM_RX"}, {"BE_OUT", NULL, "PCM_RX"}, @@ -15149,6 +16836,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"BE_OUT", NULL, "QUAT_TDM_RX_1"}, {"BE_OUT", NULL, "QUAT_TDM_RX_2"}, {"BE_OUT", NULL, "QUAT_TDM_RX_3"}, + {"BE_OUT", NULL, "QUIN_TDM_RX_0"}, + {"BE_OUT", NULL, "QUIN_TDM_RX_1"}, + {"BE_OUT", NULL, "QUIN_TDM_RX_2"}, + {"BE_OUT", NULL, "QUIN_TDM_RX_3"}, {"PRI_I2S_TX", NULL, "BE_IN"}, {"MI2S_TX", NULL, "BE_IN"}, @@ -15182,6 +16873,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_AUX_PCM_TX", NULL, "BE_IN"}, {"TERT_AUX_PCM_TX", NULL, "BE_IN"}, {"QUAT_AUX_PCM_TX", NULL, "BE_IN"}, + {"QUIN_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"}, @@ -15210,6 +16902,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_TX_1", NULL, "BE_IN"}, {"QUAT_TDM_TX_2", NULL, "BE_IN"}, {"QUAT_TDM_TX_3", NULL, "BE_IN"}, + {"QUIN_TDM_TX_0", NULL, "BE_IN"}, + {"QUIN_TDM_TX_1", NULL, "BE_IN"}, + {"QUIN_TDM_TX_2", NULL, "BE_IN"}, + {"QUIN_TDM_TX_3", NULL, "BE_IN"}, }; static int msm_pcm_routing_hw_params(struct snd_pcm_substream *substream, diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index 4e823d3eba3c..479ba35309b9 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -40,6 +40,8 @@ #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_QUIN_AUXPCM_RX "QUIN_AUX_PCM_RX" +#define LPASS_BE_QUIN_AUXPCM_TX "QUIN_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" @@ -143,6 +145,22 @@ #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_QUIN_TDM_RX_0 "QUIN_TDM_RX_0" +#define LPASS_BE_QUIN_TDM_TX_0 "QUIN_TDM_TX_0" +#define LPASS_BE_QUIN_TDM_RX_1 "QUIN_TDM_RX_1" +#define LPASS_BE_QUIN_TDM_TX_1 "QUIN_TDM_TX_1" +#define LPASS_BE_QUIN_TDM_RX_2 "QUIN_TDM_RX_2" +#define LPASS_BE_QUIN_TDM_TX_2 "QUIN_TDM_TX_2" +#define LPASS_BE_QUIN_TDM_RX_3 "QUIN_TDM_RX_3" +#define LPASS_BE_QUIN_TDM_TX_3 "QUIN_TDM_TX_3" +#define LPASS_BE_QUIN_TDM_RX_4 "QUIN_TDM_RX_4" +#define LPASS_BE_QUIN_TDM_TX_4 "QUIN_TDM_TX_4" +#define LPASS_BE_QUIN_TDM_RX_5 "QUIN_TDM_RX_5" +#define LPASS_BE_QUIN_TDM_TX_5 "QUIN_TDM_TX_5" +#define LPASS_BE_QUIN_TDM_RX_6 "QUIN_TDM_RX_6" +#define LPASS_BE_QUIN_TDM_TX_6 "QUIN_TDM_TX_6" +#define LPASS_BE_QUIN_TDM_RX_7 "QUIN_TDM_RX_7" +#define LPASS_BE_QUIN_TDM_TX_7 "QUIN_TDM_TX_7" #define LPASS_BE_SLIMBUS_7_RX "SLIMBUS_7_RX" #define LPASS_BE_SLIMBUS_7_TX "SLIMBUS_7_TX" @@ -341,6 +359,22 @@ enum { MSM_BACKEND_DAI_QUAT_TDM_TX_6, MSM_BACKEND_DAI_QUAT_TDM_RX_7, MSM_BACKEND_DAI_QUAT_TDM_TX_7, + MSM_BACKEND_DAI_QUIN_TDM_RX_0, + MSM_BACKEND_DAI_QUIN_TDM_TX_0, + MSM_BACKEND_DAI_QUIN_TDM_RX_1, + MSM_BACKEND_DAI_QUIN_TDM_TX_1, + MSM_BACKEND_DAI_QUIN_TDM_RX_2, + MSM_BACKEND_DAI_QUIN_TDM_TX_2, + MSM_BACKEND_DAI_QUIN_TDM_RX_3, + MSM_BACKEND_DAI_QUIN_TDM_TX_3, + MSM_BACKEND_DAI_QUIN_TDM_RX_4, + MSM_BACKEND_DAI_QUIN_TDM_TX_4, + MSM_BACKEND_DAI_QUIN_TDM_RX_5, + MSM_BACKEND_DAI_QUIN_TDM_TX_5, + MSM_BACKEND_DAI_QUIN_TDM_RX_6, + MSM_BACKEND_DAI_QUIN_TDM_TX_6, + MSM_BACKEND_DAI_QUIN_TDM_RX_7, + MSM_BACKEND_DAI_QUIN_TDM_TX_7, MSM_BACKEND_DAI_INT_BT_A2DP_RX, MSM_BACKEND_DAI_USB_RX, MSM_BACKEND_DAI_USB_TX, @@ -349,6 +383,8 @@ enum { MSM_BACKEND_DAI_TERT_AUXPCM_TX, MSM_BACKEND_DAI_QUAT_AUXPCM_RX, MSM_BACKEND_DAI_QUAT_AUXPCM_TX, + MSM_BACKEND_DAI_QUIN_AUXPCM_RX, + MSM_BACKEND_DAI_QUIN_AUXPCM_TX, MSM_BACKEND_DAI_INT0_MI2S_RX, MSM_BACKEND_DAI_INT0_MI2S_TX, MSM_BACKEND_DAI_INT1_MI2S_RX, diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index e755e4d86f77..df635ccad1cd 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -1051,7 +1051,7 @@ struct adm_cmd_connect_afe_port_v5 { /* 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) + (AFE_PORT_ID_TDM_PORT_RANGE_START+0x50-1) /* Size of the range of port IDs for TDM ports. */ #define AFE_PORT_ID_TDM_PORT_RANGE_SIZE \ @@ -1084,6 +1084,7 @@ struct adm_cmd_connect_afe_port_v5 { #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 */ @@ -1112,6 +1113,10 @@ struct adm_cmd_connect_afe_port_v5 { #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_QUINARY_PCM_RX 0x103C +#define AFE_PORT_ID_QUINARY_PCM_TX 0x103D + #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 @@ -1328,6 +1333,40 @@ struct adm_cmd_connect_afe_port_v5 { #define AFE_PORT_ID_QUATERNARY_TDM_TX_7 \ (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0E) +#define AFE_PORT_ID_QUINARY_TDM_RX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x40) +#define AFE_PORT_ID_QUINARY_TDM_RX_1 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x02) +#define AFE_PORT_ID_QUINARY_TDM_RX_2 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x04) +#define AFE_PORT_ID_QUINARY_TDM_RX_3 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x06) +#define AFE_PORT_ID_QUINARY_TDM_RX_4 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x08) +#define AFE_PORT_ID_QUINARY_TDM_RX_5 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x0A) +#define AFE_PORT_ID_QUINARY_TDM_RX_6 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x0C) +#define AFE_PORT_ID_QUINARY_TDM_RX_7 \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x0E) + +#define AFE_PORT_ID_QUINARY_TDM_TX \ + (AFE_PORT_ID_TDM_PORT_RANGE_START + 0x41) +#define AFE_PORT_ID_QUINARY_TDM_TX_1 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x02) +#define AFE_PORT_ID_QUINARY_TDM_TX_2 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x04) +#define AFE_PORT_ID_QUINARY_TDM_TX_3 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x06) +#define AFE_PORT_ID_QUINARY_TDM_TX_4 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x08) +#define AFE_PORT_ID_QUINARY_TDM_TX_5 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x0A) +#define AFE_PORT_ID_QUINARY_TDM_TX_6 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x0C) +#define AFE_PORT_ID_QUINARY_TDM_TX_7 \ + (AFE_PORT_ID_QUINARY_TDM_TX + 0x0E) + #define AFE_PORT_ID_INVALID 0xFFFF #define AAC_ENC_MODE_AAC_LC 0x02 @@ -9595,6 +9634,9 @@ enum afe_lpass_clk_mode { /* Clock ID for INT6 I2S IBIT */ #define Q6AFE_LPASS_CLK_ID_INT6_MI2S_IBIT 0x115 +/* Clock ID for QUINARY MI2S OSR CLK */ +#define Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR 0x116 + /* Clock ID for Primary PCM IBIT */ #define Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT 0x200 /* Clock ID for Primary PCM EBIT */ @@ -9611,6 +9653,12 @@ enum afe_lpass_clk_mode { #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 Quinary PCM IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUIN_PCM_IBIT 0x208 +/* Clock ID for Quinary PCM EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUIN_PCM_EBIT 0x209 +/* Clock ID for QUINARY PCM OSR */ +#define Q6AFE_LPASS_CLK_ID_QUI_PCM_OSR 0x20A /** Clock ID for Primary TDM IBIT */ #define Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT 0x200 @@ -9628,6 +9676,12 @@ enum afe_lpass_clk_mode { #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 Quinary TDM IBIT */ +#define Q6AFE_LPASS_CLK_ID_QUIN_TDM_IBIT 0x208 +/** Clock ID for Quinary TDM EBIT */ +#define Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT 0x209 +/** Clock ID for Quinary TDM OSR */ +#define Q6AFE_LPASS_CLK_ID_QUIN_TDM_OSR 0x20A /* Clock ID for MCLK1 */ #define Q6AFE_LPASS_CLK_ID_MCLK_1 0x300 @@ -10089,6 +10143,10 @@ struct afe_group_device_group_cfg { (AFE_PORT_ID_QUATERNARY_TDM_RX + 0x100) #define AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX \ (AFE_PORT_ID_QUATERNARY_TDM_TX + 0x100) +#define AFE_GROUP_DEVICE_ID_QUINARY_TDM_RX \ + (AFE_PORT_ID_QUINARY_TDM_RX + 0x100) +#define AFE_GROUP_DEVICE_ID_QUINARY_TDM_TX \ + (AFE_PORT_ID_QUINARY_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. From d17544899a099da44098f565f284dfe8fdcd2468 Mon Sep 17 00:00:00 2001 From: Rohit Kumar Date: Sun, 10 Sep 2017 22:57:39 +0530 Subject: [PATCH 045/276] asoc: sdm660: add support for quinary interfaces Add support for Quinary MI2S, AuxPCM and TDM interfaces in sdm660 machine driver. Change-Id: I2dad713a3286499e9b76206b28a82b56638949a4 Signed-off-by: Rohit Kumar --- asoc/sdm660-common.c | 143 +++++++++++++++++++++++++++++++++++- asoc/sdm660-common.h | 1 + asoc/sdm660-ext-dai-links.c | 90 +++++++++++++++++++++++ asoc/sdm660-internal.c | 90 +++++++++++++++++++++++ 4 files changed, 322 insertions(+), 2 deletions(-) diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c index 8006472ff338..78218ab056a3 100644 --- a/asoc/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -87,6 +87,16 @@ static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { {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 */ + }, + { /* QUIN 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 */ } }; @@ -131,6 +141,16 @@ static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { {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 */ + }, + { /* QUIN 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 */ } }; @@ -155,6 +175,7 @@ enum { SEC_MI2S, TERT_MI2S, QUAT_MI2S, + QUIN_MI2S, MI2S_MAX, }; @@ -163,6 +184,7 @@ enum { SEC_AUX_PCM, TERT_AUX_PCM, QUAT_AUX_PCM, + QUIN_AUX_PCM, AUX_PCM_MAX, }; @@ -171,6 +193,7 @@ enum { PCM_I2S_SEL_SEC, PCM_I2S_SEL_TERT, PCM_I2S_SEL_QUAT, + PCM_I2S_SEL_QUIN, PCM_I2S_SEL_MAX, }; @@ -231,6 +254,7 @@ static struct dev_config mi2s_rx_cfg[] = { [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}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2}, }; static struct dev_config mi2s_tx_cfg[] = { @@ -238,6 +262,7 @@ static struct dev_config mi2s_tx_cfg[] = { [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}, + [QUIN_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, }; static struct dev_config aux_pcm_rx_cfg[] = { @@ -245,6 +270,7 @@ static struct dev_config aux_pcm_rx_cfg[] = { [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}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, }; static struct dev_config aux_pcm_tx_cfg[] = { @@ -252,6 +278,7 @@ static struct dev_config aux_pcm_tx_cfg[] = { [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}, + [QUIN_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, }; static char const *ch_text[] = {"Two", "Three", "Four", "Five", @@ -290,26 +317,32 @@ 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(quin_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(quin_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(quin_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(quin_mi2s_tx_sample_rate, mi2s_rate_text); static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_format, mi2s_format_text); static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_format, mi2s_format_text); static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_format, mi2s_format_text); static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_rx_format, mi2s_format_text); static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_format, mi2s_format_text); static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_format, mi2s_format_text); static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_format, mi2s_format_text); static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_format, mi2s_format_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_mi2s_tx_format, mi2s_format_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); @@ -318,6 +351,8 @@ 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(quin_mi2s_rx_chs, mi2s_ch_text); +static SOC_ENUM_SINGLE_EXT_DECL(quin_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); @@ -520,6 +555,9 @@ static int tdm_get_port_idx(struct snd_kcontrol *kcontrol, } else if (strnstr(kcontrol->id.name, "QUAT", sizeof(kcontrol->id.name))) { port->mode = TDM_QUAT; + } else if (strnstr(kcontrol->id.name, "QUIN", + sizeof(kcontrol->id.name))) { + port->mode = TDM_QUIN; } else { pr_err("%s: unsupported mode in: %s", __func__, kcontrol->id.name); @@ -955,6 +993,9 @@ static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM", sizeof("QUAT_AUX_PCM"))) idx = QUAT_AUX_PCM; + else if (strnstr(kcontrol->id.name, "QUIN_AUX_PCM", + sizeof("QUIN_AUX_PCM"))) + idx = QUIN_AUX_PCM; else { pr_err("%s: unsupported port: %s", __func__, kcontrol->id.name); @@ -1052,6 +1093,9 @@ static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX", sizeof("QUAT_MI2S_RX"))) idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "QUIN_MI2S_RX", + sizeof("QUIN_MI2S_RX"))) + idx = QUIN_MI2S; else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX", sizeof("PRIM_MI2S_TX"))) idx = PRIM_MI2S; @@ -1064,6 +1108,9 @@ static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol) else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX", sizeof("QUAT_MI2S_TX"))) idx = QUAT_MI2S; + else if (strnstr(kcontrol->id.name, "QUIN_MI2S_TX", + sizeof("QUIN_MI2S_TX"))) + idx = QUIN_MI2S; else { pr_err("%s: unsupported channel: %s", __func__, kcontrol->id.name); @@ -1836,6 +1883,9 @@ const struct snd_kcontrol_new msm_common_snd_controls[] = { 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("QUIN_AUX_PCM_RX SampleRate", quin_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), @@ -1848,6 +1898,9 @@ const struct snd_kcontrol_new msm_common_snd_controls[] = { 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("QUIN_AUX_PCM_TX SampleRate", quin_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), @@ -1860,6 +1913,9 @@ const struct snd_kcontrol_new msm_common_snd_controls[] = { 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("QUIN_MI2S_RX SampleRate", quin_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), @@ -1872,6 +1928,9 @@ const struct snd_kcontrol_new msm_common_snd_controls[] = { 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("QUIN_MI2S_TX SampleRate", quin_mi2s_tx_sample_rate, + mi2s_tx_sample_rate_get, + mi2s_tx_sample_rate_put), SOC_ENUM_EXT("PRIM_MI2S_RX Format", prim_mi2s_rx_format, mi2s_rx_format_get, mi2s_rx_format_put), @@ -1884,6 +1943,9 @@ const struct snd_kcontrol_new msm_common_snd_controls[] = { SOC_ENUM_EXT("QUAT_MI2S_RX Format", quat_mi2s_rx_format, mi2s_rx_format_get, mi2s_rx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_RX Format", quin_mi2s_rx_format, + mi2s_rx_format_get, + mi2s_rx_format_put), SOC_ENUM_EXT("PRIM_MI2S_TX Format", prim_mi2s_tx_format, mi2s_tx_format_get, mi2s_tx_format_put), @@ -1896,6 +1958,9 @@ const struct snd_kcontrol_new msm_common_snd_controls[] = { SOC_ENUM_EXT("QUAT_MI2S_TX Format", quat_mi2s_tx_format, mi2s_tx_format_get, mi2s_tx_format_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Format", quin_mi2s_tx_format, + mi2s_tx_format_get, + mi2s_tx_format_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, @@ -1912,6 +1977,10 @@ const struct snd_kcontrol_new msm_common_snd_controls[] = { 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("QUIN_MI2S_RX Channels", quin_mi2s_rx_chs, + msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put), + SOC_ENUM_EXT("QUIN_MI2S_TX Channels", quin_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, @@ -2005,6 +2074,24 @@ const struct snd_kcontrol_new msm_common_snd_controls[] = { SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs, tdm_tx_ch_get, tdm_tx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + tdm_rx_sample_rate_get, + tdm_rx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + tdm_tx_sample_rate_get, + tdm_tx_sample_rate_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Format", tdm_rx_format, + tdm_rx_format_get, + tdm_rx_format_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Format", tdm_tx_format, + tdm_tx_format_get, + tdm_tx_format_put), + SOC_ENUM_EXT("QUIN_TDM_RX_0 Channels", tdm_rx_chs, + tdm_rx_ch_get, + tdm_rx_ch_put), + SOC_ENUM_EXT("QUIN_TDM_TX_0 Channels", tdm_tx_chs, + tdm_tx_ch_get, + tdm_tx_ch_put), }; /** @@ -2187,6 +2274,22 @@ int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate; break; + case MSM_BACKEND_DAI_QUIN_TDM_RX_0: + channels->min = channels->max = + tdm_rx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_rx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + + case MSM_BACKEND_DAI_QUIN_TDM_TX_0: + channels->min = channels->max = + tdm_tx_cfg[TDM_QUIN][TDM_0].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_tx_cfg[TDM_QUIN][TDM_0].bit_format); + rate->min = rate->max = tdm_tx_cfg[TDM_QUIN][TDM_0].sample_rate; + break; + case MSM_BACKEND_DAI_AUXPCM_RX: rate->min = rate->max = aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate; @@ -2243,6 +2346,20 @@ int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, aux_pcm_tx_cfg[QUAT_AUX_PCM].channels; break; + case MSM_BACKEND_DAI_QUIN_AUXPCM_RX: + rate->min = rate->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_rx_cfg[QUIN_AUX_PCM].channels; + break; + + case MSM_BACKEND_DAI_QUIN_AUXPCM_TX: + rate->min = rate->max = + aux_pcm_tx_cfg[QUIN_AUX_PCM].sample_rate; + channels->min = channels->max = + aux_pcm_tx_cfg[QUIN_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 = @@ -2307,6 +2424,22 @@ int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, mi2s_tx_cfg[QUAT_MI2S].bit_format); break; + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + rate->min = rate->max = mi2s_rx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_rx_cfg[QUIN_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_rx_cfg[QUIN_MI2S].bit_format); + break; + + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + rate->min = rate->max = mi2s_tx_cfg[QUIN_MI2S].sample_rate; + channels->min = channels->max = + mi2s_tx_cfg[QUIN_MI2S].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + mi2s_tx_cfg[QUIN_MI2S].bit_format); + break; + default: rate->min = rate->max = SAMPLING_RATE_48KHZ; break; @@ -2381,6 +2514,12 @@ static int msm_get_port_id(int id) case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX: afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX; break; + case MSM_BACKEND_DAI_QUINARY_MI2S_RX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_RX; + break; + case MSM_BACKEND_DAI_QUINARY_MI2S_TX: + afe_port_id = AFE_PORT_ID_QUINARY_MI2S_TX; + break; default: pr_err("%s: Invalid id: %d\n", __func__, id); afe_port_id = -EINVAL; @@ -2481,7 +2620,7 @@ int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) __func__, substream->name, substream->stream, cpu_dai->name, cpu_dai->id); - if (index < PRIM_MI2S || index > QUAT_MI2S) { + if (index < PRIM_MI2S || index >= MI2S_MAX) { ret = -EINVAL; dev_err(rtd->card->dev, "%s: CPU DAI id (%d) out of range\n", @@ -2555,7 +2694,7 @@ void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) pr_debug("%s(): substream = %s stream = %d\n", __func__, substream->name, substream->stream); - if (index < PRIM_MI2S || index > QUAT_MI2S) { + if (index < PRIM_MI2S || index >= MI2S_MAX) { pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index); return; } diff --git a/asoc/sdm660-common.h b/asoc/sdm660-common.h index c7de18f7f4db..3276d1fa2c58 100644 --- a/asoc/sdm660-common.h +++ b/asoc/sdm660-common.h @@ -51,6 +51,7 @@ enum { TDM_SEC, TDM_TERT, TDM_QUAT, + TDM_QUIN, TDM_INTERFACE_MAX, }; diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c index a76c16d76d00..4777a5ff3ee1 100644 --- a/asoc/sdm660-ext-dai-links.c +++ b/asoc/sdm660-ext-dai-links.c @@ -205,10 +205,12 @@ static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, 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_QUINARY_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: + case AFE_PORT_ID_QUINARY_TDM_TX: slot_offset = tdm_slot_offset[TDM_0]; break; default: @@ -1568,6 +1570,34 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .ops = &msm_tdm_be_ops, .ignore_suspend = 1, }, + { + .name = LPASS_BE_QUIN_TDM_RX_0, + .stream_name = "Quinary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.37184", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_TDM_TX_0, + .stream_name = "Quinary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.37185", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_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[] = { @@ -1687,6 +1717,35 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .ops = &msm_mi2s_be_ops, .ignore_suspend = 1, }, + { + .name = LPASS_BE_QUIN_MI2S_RX, + .stream_name = "Quinary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUINARY_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_QUIN_MI2S_TX, + .stream_name = "Quinary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUINARY_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[] = { @@ -1814,6 +1873,37 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .ignore_pmdown_time = 1, .ops = &msm_aux_pcm_be_ops, }, + /* Quinary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUIN_AUXPCM_RX, + .stream_name = "Quin AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_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_QUIN_AUXPCM_TX, + .stream_name = "Quin AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_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[] = { diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c index b5b05b10c9a5..713da55cb0a9 100644 --- a/asoc/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -1499,10 +1499,12 @@ static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream, 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_QUINARY_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: + case AFE_PORT_ID_QUINARY_TDM_TX: slot_offset = tdm_slot_offset[TDM_0]; break; default: @@ -2607,6 +2609,34 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .ops = &msm_tdm_be_ops, .ignore_suspend = 1, }, + { + .name = LPASS_BE_QUIN_TDM_RX_0, + .stream_name = "Quinary TDM0 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.37184", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_TDM_RX_0, + .be_hw_params_fixup = msm_common_be_hw_params_fixup, + .ops = &msm_tdm_be_ops, + .ignore_suspend = 1, + }, + { + .name = LPASS_BE_QUIN_TDM_TX_0, + .stream_name = "Quinary TDM0 Capture", + .cpu_dai_name = "msm-dai-q6-tdm.37185", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_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[] = { @@ -2726,6 +2756,35 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { .ops = &msm_mi2s_be_ops, .ignore_suspend = 1, }, + { + .name = LPASS_BE_QUIN_MI2S_RX, + .stream_name = "Quinary MI2S Playback", + .cpu_dai_name = "msm-dai-q6-mi2s.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUINARY_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_QUIN_MI2S_TX, + .stream_name = "Quinary MI2S Capture", + .cpu_dai_name = "msm-dai-q6-mi2s.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUINARY_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[] = { @@ -2853,6 +2912,37 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .ignore_pmdown_time = 1, .ops = &msm_aux_pcm_be_ops, }, + /* Quinary AUX PCM Backend DAI Links */ + { + .name = LPASS_BE_QUIN_AUXPCM_RX, + .stream_name = "Quin AUX PCM Playback", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUIN_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_QUIN_AUXPCM_TX, + .stream_name = "Quin AUX PCM Capture", + .cpu_dai_name = "msm-dai-q6-auxpcm.5", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_QUIN_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, + }, }; From b3154657732a3b73b04c5b7d63ff7952c7425281 Mon Sep 17 00:00:00 2001 From: Rohit Kumar Date: Mon, 11 Sep 2017 00:33:13 +0530 Subject: [PATCH 046/276] autoconf: sdm670: add support to compile analog and digital codec Make autoconf changes to enable compilation of analog and digital codec separately. Change-Id: I0e7da48ead0bfe535f9103776845e068a3c149d3 Signed-off-by: Rohit Kumar --- config/sdm670auto.conf | 2 ++ config/sdm670autoconf.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/config/sdm670auto.conf b/config/sdm670auto.conf index cbeb2dee9ae8..ca30dd039e7d 100644 --- a/config/sdm670auto.conf +++ b/config/sdm670auto.conf @@ -43,5 +43,7 @@ CONFIG_SND_SOC_MSM_STUB=y CONFIG_WCD_DSP_GLINK=y CONFIG_MSM_AVTIMER=y CONFIG_SND_SOC_SDM660_CDC=y +CONFIG_SND_SOC_ANALOG_CDC=y +CONFIG_SND_SOC_DIGITAL_CDC=y CONFIG_SND_SOC_MSM_SDW=y CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y diff --git a/config/sdm670autoconf.h b/config/sdm670autoconf.h index 1cec92c8c7a8..6aba91d63f8d 100644 --- a/config/sdm670autoconf.h +++ b/config/sdm670autoconf.h @@ -56,5 +56,7 @@ #define CONFIG_SND_SOC_EXT_CODEC 1 #define CONFIG_SND_SOC_INT_CODEC 1 #define CONFIG_SND_SOC_SDM660_CDC 1 +#define CONFIG_SND_SOC_ANALOG_CDC 1 +#define CONFIG_SND_SOC_DIGITAL_CDC 1 #define CONFIG_SND_SOC_MSM_SDW 1 #define CONFIG_SND_SOC_MSM_HDMI_CODEC_RX 1 From 6f90162f22361d74d3d2deb08dae0afffce5bcb0 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Tue, 19 Sep 2017 10:21:57 +0800 Subject: [PATCH 047/276] ASoC: wcd-mbhc: update electrical removal detection logic when moisture is in jack, removing of the plug result in electrical removal interrupt. So, update electrical interrupt handler to handle mechanical removal based on moisture status. CRs-Fixed: 2058106 Change-Id: I9cfbfbaf04783f0edcadb14d7828759020745289 Signed-off-by: Meng Wang --- asoc/codecs/wcd-mbhc-adc.c | 66 ++++++++++++++++++---- asoc/codecs/wcd-mbhc-legacy.c | 90 ++++++++++++++++++++++++------ asoc/codecs/wcd-mbhc-v2.c | 3 +- asoc/codecs/wcd-mbhc-v2.h | 6 ++ asoc/codecs/wcd9335.c | 6 ++ asoc/codecs/wcd934x/wcd934x-mbhc.c | 6 ++ 6 files changed, 148 insertions(+), 29 deletions(-) diff --git a/asoc/codecs/wcd-mbhc-adc.c b/asoc/codecs/wcd-mbhc-adc.c index e44eec9fa5c7..ad414770a2cc 100644 --- a/asoc/codecs/wcd-mbhc-adc.c +++ b/asoc/codecs/wcd-mbhc-adc.c @@ -895,6 +895,8 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) struct wcd_mbhc *mbhc = data; unsigned long timeout; int adc_threshold, output_mv, retry = 0; + bool hphpa_on = false; + u8 moisture_status = 0; pr_debug("%s: enter\n", __func__); WCD_MBHC_RSC_LOCK(mbhc); @@ -928,17 +930,59 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) goto exit; } - /* - * ADC COMPLETE and ELEC_REM interrupts are both enabled for HEADPHONE, - * need to reject the ADC COMPLETE interrupt which follows ELEC_REM one - * when HEADPHONE is removed. - */ - if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) - mbhc->extn_cable_hph_rem = true; - WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); - WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); - WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); - wcd_mbhc_elec_hs_report_unplug(mbhc); + if (mbhc->mbhc_cfg->moisture_en) { + if (mbhc->mbhc_cb->hph_pa_on_status) + if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) { + hphpa_on = true; + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_HPHL_PA_EN, 0); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_HPH_PA_EN, 0); + } + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_GND, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_GND, 1); + /* wait for 50ms to get moisture status */ + usleep_range(50000, 50100); + + WCD_MBHC_REG_READ(WCD_MBHC_MOISTURE_STATUS, moisture_status); + } + + if (mbhc->mbhc_cfg->moisture_en && !moisture_status) { + pr_debug("%s: moisture present in jack\n", __func__); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + mbhc->btn_press_intr = false; + mbhc->is_btn_press = false; + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET); + else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE); + else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED); + else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) + wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT); + } else { + /* + * ADC COMPLETE and ELEC_REM interrupts are both enabled for + * HEADPHONE, need to reject the ADC COMPLETE interrupt which + * follows ELEC_REM one when HEADPHONE is removed. + */ + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) + mbhc->extn_cable_hph_rem = true; + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_DETECTION_DONE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); + wcd_mbhc_elec_hs_report_unplug(mbhc); + + if (hphpa_on) { + hphpa_on = false; + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 1); + } + } exit: WCD_MBHC_RSC_UNLOCK(mbhc); pr_debug("%s: leave\n", __func__); diff --git a/asoc/codecs/wcd-mbhc-legacy.c b/asoc/codecs/wcd-mbhc-legacy.c index 745e2e81a556..a72f64b40861 100644 --- a/asoc/codecs/wcd-mbhc-legacy.c +++ b/asoc/codecs/wcd-mbhc-legacy.c @@ -794,6 +794,8 @@ static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data) unsigned long timeout; bool removed = true; int retry = 0; + bool hphpa_on = false; + u8 moisture_status = 0; pr_debug("%s: enter\n", __func__); @@ -830,29 +832,78 @@ static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data) WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result); if (removed) { - if (!(hphl_sch && mic_sch && hs_comp_result)) { - /* - * extension cable is still plugged in - * report it as LINEOUT device - */ - goto report_unplug; + if (mbhc->mbhc_cfg->moisture_en) { + if (mbhc->mbhc_cb->hph_pa_on_status) + if ( + mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec)) { + hphpa_on = true; + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_HPHL_PA_EN, 0); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_HPH_PA_EN, 0); + } + + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_GND, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_GND, 1); + /* wait for 50ms to get moisture status */ + usleep_range(50000, 50100); + + WCD_MBHC_REG_READ( + WCD_MBHC_MOISTURE_STATUS, moisture_status); + } + + if (mbhc->mbhc_cfg->moisture_en && !moisture_status) { + pr_debug("%s: moisture present in jack\n", __func__); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 0); + WCD_MBHC_REG_UPDATE_BITS( + WCD_MBHC_MECH_DETECTION_TYPE, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); + mbhc->btn_press_intr = false; + mbhc->is_btn_press = false; + if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) + wcd_mbhc_report_plug( + mbhc, 0, SND_JACK_HEADSET); + else if (mbhc->current_plug == + MBHC_PLUG_TYPE_HEADPHONE) + wcd_mbhc_report_plug( + mbhc, 0, SND_JACK_HEADPHONE); + else if (mbhc->current_plug == + MBHC_PLUG_TYPE_GND_MIC_SWAP) + wcd_mbhc_report_plug( + mbhc, 0, SND_JACK_UNSUPPORTED); + else if (mbhc->current_plug == + MBHC_PLUG_TYPE_HIGH_HPH) + wcd_mbhc_report_plug( + mbhc, 0, SND_JACK_LINEOUT); } else { - if (!mic_sch) { - mic_trigerred++; - pr_debug("%s: Removal MIC trigerred %d\n", - __func__, mic_trigerred); - } - if (!hphl_sch) { - hphl_trigerred++; - pr_debug("%s: Removal HPHL trigerred %d\n", - __func__, hphl_trigerred); - } - if (mic_trigerred && hphl_trigerred) { + if (!(hphl_sch && mic_sch && hs_comp_result)) { /* * extension cable is still plugged in * report it as LINEOUT device */ goto report_unplug; + } else { + if (!mic_sch) { + mic_trigerred++; + pr_debug( + "%s: Removal MIC trigerred %d\n", + __func__, mic_trigerred); + } + if (!hphl_sch) { + hphl_trigerred++; + pr_debug( + "%s: Removal HPHL trigerred %d\n", + __func__, hphl_trigerred); + } + if (mic_trigerred && hphl_trigerred) { + /* + * extension cable is still plugged in + * report it as LINEOUT device + */ + goto report_unplug; + } } } } @@ -863,6 +914,11 @@ static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data) report_unplug: wcd_mbhc_elec_hs_report_unplug(mbhc); + if (hphpa_on) { + hphpa_on = false; + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 1); + } hphl_trigerred = 0; mic_trigerred = 0; WCD_MBHC_RSC_UNLOCK(mbhc); diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index 6b3dd8626da5..339d0f3d420b 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -550,7 +550,7 @@ void wcd_mbhc_hs_elec_irq(struct wcd_mbhc *mbhc, int irq_type, } EXPORT_SYMBOL(wcd_mbhc_hs_elec_irq); -static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, +void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, enum snd_jack_types jack_type) { struct snd_soc_codec *codec = mbhc->codec; @@ -725,6 +725,7 @@ static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, } pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status); } +EXPORT_SYMBOL(wcd_mbhc_report_plug); void wcd_mbhc_elec_hs_report_unplug(struct wcd_mbhc *mbhc) { diff --git a/asoc/codecs/wcd-mbhc-v2.h b/asoc/codecs/wcd-mbhc-v2.h index c8714fc3abbc..32dc78ca0a09 100644 --- a/asoc/codecs/wcd-mbhc-v2.h +++ b/asoc/codecs/wcd-mbhc-v2.h @@ -16,6 +16,7 @@ #include #include #include "wcdcal-hwdep.h" +#include #define TOMBAK_MBHC_NC 0 #define TOMBAK_MBHC_NO 1 @@ -204,6 +205,9 @@ enum wcd_mbhc_register_function { WCD_MBHC_ANC_DET_EN, WCD_MBHC_FSM_STATUS, WCD_MBHC_MUX_CTL, + WCD_MBHC_MOISTURE_STATUS, + WCD_MBHC_HPHR_GND, + WCD_MBHC_HPHL_GND, WCD_MBHC_HPHL_OCP_DET_EN, WCD_MBHC_HPHR_OCP_DET_EN, WCD_MBHC_HPHL_OCP_STATUS, @@ -594,5 +598,7 @@ void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc, struct snd_soc_jack *jack, int status, int mask); int wcd_cancel_btn_work(struct wcd_mbhc *mbhc); int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc); +void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, + enum snd_jack_types jack_type); #endif /* __WCD_MBHC_V2_H__ */ diff --git a/asoc/codecs/wcd9335.c b/asoc/codecs/wcd9335.c index 21de9f5f6d6f..19877b96b0ed 100644 --- a/asoc/codecs/wcd9335.c +++ b/asoc/codecs/wcd9335.c @@ -650,6 +650,12 @@ static struct wcd_mbhc_register 0, 0, 0, 0), WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", WCD9335_MBHC_CTL_2, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MOISTURE_STATUS", + WCD9335_MBHC_FSM_STATUS, 0X20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_GND", + WCD9335_HPH_PA_CTL2, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_GND", + WCD9335_HPH_PA_CTL2, 0x10, 4, 0), }; static const struct wcd_mbhc_intr intr_ids = { diff --git a/asoc/codecs/wcd934x/wcd934x-mbhc.c b/asoc/codecs/wcd934x/wcd934x-mbhc.c index 4eb14de31075..807d4ea58e89 100644 --- a/asoc/codecs/wcd934x/wcd934x-mbhc.c +++ b/asoc/codecs/wcd934x/wcd934x-mbhc.c @@ -120,6 +120,12 @@ static struct wcd_mbhc_register WCD934X_MBHC_STATUS_SPARE_1, 0x01, 0, 0), WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL", WCD934X_MBHC_NEW_CTL_2, 0x70, 4, 0), + WCD_MBHC_REGISTER("WCD_MBHC_MOISTURE_STATUS", + WCD934X_MBHC_NEW_FSM_STATUS, 0x20, 5, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHR_GND", + WCD934X_HPH_PA_CTL2, 0x40, 6, 0), + WCD_MBHC_REGISTER("WCD_MBHC_HPHL_GND", + WCD934X_HPH_PA_CTL2, 0x10, 4, 0), WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN", WCD934X_HPH_L_TEST, 0x01, 0, 0), WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN", From 8968c5f2a2dc09b0c9a97fad45cbb7aa2273cba6 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Thu, 28 Sep 2017 15:49:45 +0530 Subject: [PATCH 048/276] ASoC: Modify name for Tertiary MI2S RX and TX Change name from "TERTIARY_MI2S" to "TERT_MI2S" for RX and TX in order to maintain consistency with other Mi2S back-ends. CRs-Fixed: 2075701 Change-Id: I52116df27a780e42de4a6b3d74249f77d311d8d4 Signed-off-by: Vatsal Bucha --- asoc/msm-pcm-routing-v2.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index 4e823d3eba3c..e5cc7bb2066b 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -56,8 +56,8 @@ #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_TERT_MI2S_RX "TERT_MI2S_RX" +#define LPASS_BE_TERT_MI2S_TX "TERT_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" From 9c429506158aac4d5903f5e157c6c0b51e5b1d31 Mon Sep 17 00:00:00 2001 From: kunleiz Date: Mon, 18 Sep 2017 16:10:19 +0800 Subject: [PATCH 049/276] rtac: add size check when reading cal data kvaddr buffer Add size check to ensure cal data bytes size fits inside the cal data when copying to user space buffer. CRs-Fixed: 2110256 Change-Id: I511999984684a9db4aaf1cf2c65eb1495c36980f Signed-off-by: kunleiz --- dsp/rtac.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/dsp/rtac.c b/dsp/rtac.c index 456ac5773d64..84ef8355a1c3 100644 --- a/dsp/rtac.c +++ b/dsp/rtac.c @@ -909,6 +909,14 @@ int send_adm_apr(void *buf, u32 opcode) bytes_returned = ((u32 *)rtac_cal[ADM_RTAC_CAL].cal_data. kvaddr)[2] + 3 * sizeof(u32); + if (bytes_returned > rtac_cal[ADM_RTAC_CAL]. + map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + 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); @@ -1132,6 +1140,14 @@ int send_rtac_asm_apr(void *buf, u32 opcode) bytes_returned = ((u32 *)rtac_cal[ASM_RTAC_CAL].cal_data. kvaddr)[2] + 3 * sizeof(u32); + if (bytes_returned > rtac_cal[ASM_RTAC_CAL]. + map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + 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); @@ -1392,6 +1408,14 @@ static int send_rtac_afe_apr(void *buf, uint32_t opcode) bytes_returned = get_resp->param_size + sizeof(struct afe_port_param_data_v2); + if (bytes_returned > rtac_cal[AFE_RTAC_CAL]. + map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + 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, @@ -1617,6 +1641,14 @@ int send_voice_apr(u32 mode, void *buf, u32 opcode) bytes_returned = ((u32 *)rtac_cal[VOICE_RTAC_CAL].cal_data. kvaddr)[2] + 3 * sizeof(u32); + if (bytes_returned > rtac_cal[VOICE_RTAC_CAL]. + map_data.map_size) { + pr_err("%s: Invalid data size = %d\n", + __func__, bytes_returned); + result = -EINVAL; + goto err; + } + 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); From 4b5bd80a91f86cca6dc2b1197ff0716539c7779c Mon Sep 17 00:00:00 2001 From: Rohit Kumar Date: Mon, 11 Sep 2017 00:30:40 +0530 Subject: [PATCH 050/276] asoc: codecs: Add support to compile internal codec as module Separate compilation of analog and digital codec and add support to compile them as dynamic module. Change-Id: I81f01bf60aef71f67e025fa4ff1ba805a960e61d Signed-off-by: Rohit Kumar --- asoc/codecs/sdm660_cdc/Makefile | 6 +- .../codecs/sdm660_cdc/msm-analog-cdc-regmap.h | 186 ++++++++++++++++++ asoc/codecs/sdm660_cdc/msm-analog-cdc.c | 4 +- ...m660-regmap.c => msm-digital-cdc-regmap.c} | 159 --------------- 4 files changed, 193 insertions(+), 162 deletions(-) create mode 100644 asoc/codecs/sdm660_cdc/msm-analog-cdc-regmap.h rename asoc/codecs/sdm660_cdc/{sdm660-regmap.c => msm-digital-cdc-regmap.c} (72%) diff --git a/asoc/codecs/sdm660_cdc/Makefile b/asoc/codecs/sdm660_cdc/Makefile index d846fae26054..0ebf40717dd7 100644 --- a/asoc/codecs/sdm660_cdc/Makefile +++ b/asoc/codecs/sdm660_cdc/Makefile @@ -1,2 +1,4 @@ -snd-soc-sdm660-cdc-objs := msm-analog-cdc.o msm-digital-cdc.o sdm660-regmap.o -obj-$(CONFIG_SND_SOC_SDM660_CDC) += snd-soc-sdm660-cdc.o sdm660-cdc-irq.o +snd-soc-analog-cdc-objs := sdm660-cdc-irq.o msm-analog-cdc.o +snd-soc-digital-cdc-objs := msm-digital-cdc.o msm-digital-cdc-regmap.o +obj-$(CONFIG_SND_SOC_ANALOG_CDC) += snd-soc-analog-cdc.o +obj-$(CONFIG_SND_SOC_DIGITAL_CDC) += snd-soc-digital-cdc.o diff --git a/asoc/codecs/sdm660_cdc/msm-analog-cdc-regmap.h b/asoc/codecs/sdm660_cdc/msm-analog-cdc-regmap.h new file mode 100644 index 000000000000..55846a1a20ca --- /dev/null +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc-regmap.h @@ -0,0 +1,186 @@ +/* + * 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 ANALOG_CDC_REGMAP_H +#define ANALOG_CDC_REGMAP_H + +#include +#include "sdm660-cdc-registers.h" + +/* + * Default register reset values that are common across different versions + * are defined here. If a register reset value is changed based on version + * then remove it from this structure and add it in version specific + * structures. + */ + +struct reg_default + msm89xx_pmic_cdc_defaults[MSM89XX_PMIC_CDC_CACHE_SIZE] = { + {MSM89XX_PMIC_DIGITAL_REVISION1, 0x00}, + {MSM89XX_PMIC_DIGITAL_REVISION2, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_TYPE, 0x23}, + {MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE, 0x01}, + {MSM89XX_PMIC_DIGITAL_INT_RT_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_SET_TYPE, 0xFF}, + {MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH, 0xFF}, + {MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_EN_SET, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_EN_CLR, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_PENDING_STS, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_MID_SEL, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_PRIORITY, 0x00}, + {MSM89XX_PMIC_DIGITAL_GPIO_MODE, 0x00}, + {MSM89XX_PMIC_DIGITAL_PIN_CTL_OE, 0x01}, + {MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA, 0x00}, + {MSM89XX_PMIC_DIGITAL_PIN_STATUS, 0x00}, + {MSM89XX_PMIC_DIGITAL_HDRIVE_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, 0x02}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, 0x02}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1, 0x7C}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2, 0x7C}, + {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3, 0x7C}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2, 0x00}, + {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3, 0x00}, + {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL, 0x00}, + {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_0, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_1, 0x00}, + {MSM89XX_PMIC_DIGITAL_SPARE_2, 0x00}, + {MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1, 0x00}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2, 0x02}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x05}, + {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_TEST1, 0x00}, + {MSM89XX_PMIC_DIGITAL_INT_TEST_VAL, 0x00}, + {MSM89XX_PMIC_DIGITAL_TRIM_NUM, 0x00}, + {MSM89XX_PMIC_DIGITAL_TRIM_CTRL, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION1, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION2, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION3, 0x00}, + {MSM89XX_PMIC_ANALOG_REVISION4, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_TYPE, 0x23}, + {MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x09}, + {MSM89XX_PMIC_ANALOG_INT_RT_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_SET_TYPE, 0x3F}, + {MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH, 0x3F}, + {MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_EN_SET, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_EN_CLR, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_LATCHED_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_PENDING_STS, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_MID_SEL, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_PRIORITY, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_VAL, 0x20}, + {MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, 0x49}, + {MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20}, + {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, 0x00}, + {MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x35}, + {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08}, + {MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x98}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, 0x40}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x61}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL, 0x80}, + {MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0x00}, + {MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x00}, + {MSM89XX_PMIC_ANALOG_TX_1_EN, 0x03}, + {MSM89XX_PMIC_ANALOG_TX_2_EN, 0x03}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1, 0xBF}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2, 0x8C}, + {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x6B}, + {MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, 0x51}, + {MSM89XX_PMIC_ANALOG_TX_3_EN, 0x02}, + {MSM89XX_PMIC_ANALOG_NCP_EN, 0x26}, + {MSM89XX_PMIC_ANALOG_NCP_CLK, 0x23}, + {MSM89XX_PMIC_ANALOG_NCP_DEGLITCH, 0x5B}, + {MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x08}, + {MSM89XX_PMIC_ANALOG_NCP_BIAS, 0x29}, + {MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0x24}, + {MSM89XX_PMIC_ANALOG_NCP_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR, 0xD5}, + {MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER, 0xE8}, + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xCF}, + {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0x6E}, + {MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x18}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0x5A}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP, 0x69}, + {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, 0x29}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x80}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL, 0xDA}, + {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0x16}, + {MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20}, + {MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12}, + {MSM89XX_PMIC_ANALOG_RX_ATEST, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_HPH_STATUS, 0x0C}, + {MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x83}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET, 0x91}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x29}, + {MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x4D}, + {MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1}, + {MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x1E}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC, 0xCB}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x00}, + {MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x02}, + {MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE, 0x14}, + {MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x00}, + {MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x1F}, + {MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x8C}, + {MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE, 0xC0}, + {MSM89XX_PMIC_ANALOG_BOOST_TEST1_1, 0x00}, + {MSM89XX_PMIC_ANALOG_BOOST_TEST_2, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS, 0x00}, + {MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR, 0x00}, + {MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL, 0x00}, + {MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1, 0x00}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2, 0x01}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x05}, + {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_TEST1, 0x00}, + {MSM89XX_PMIC_ANALOG_INT_TEST_VAL, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_NUM, 0x04}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL1, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL2, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL3, 0x00}, + {MSM89XX_PMIC_ANALOG_TRIM_CTRL4, 0x00}, +}; + +#endif diff --git a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c index ddef508cb682..623303c3adca 100644 --- a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -30,7 +30,7 @@ #include "msm-analog-cdc.h" #include "msm-cdc-common.h" #include "sdm660-cdc-irq.h" -#include "sdm660-cdc-registers.h" +#include "msm-analog-cdc-regmap.h" #include "../../sdm660-common.h" #include "../wcd-mbhc-v2-api.h" @@ -351,6 +351,7 @@ void msm_anlg_cdc_spk_ext_pa_cb( dev_dbg(codec->dev, "%s: Enter\n", __func__); sdm660_cdc->codec_spk_ext_pa_cb = codec_spk_ext_pa; } +EXPORT_SYMBOL(msm_anlg_cdc_spk_ext_pa_cb); static void msm_anlg_cdc_compute_impedance(struct snd_soc_codec *codec, s16 l, s16 r, uint32_t *zl, uint32_t *zr, @@ -3172,6 +3173,7 @@ int msm_anlg_cdc_mclk_enable(struct snd_soc_codec *codec, } return 0; } +EXPORT_SYMBOL(msm_anlg_cdc_mclk_enable); static int msm_anlg_cdc_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) diff --git a/asoc/codecs/sdm660_cdc/sdm660-regmap.c b/asoc/codecs/sdm660_cdc/msm-digital-cdc-regmap.c similarity index 72% rename from asoc/codecs/sdm660_cdc/sdm660-regmap.c rename to asoc/codecs/sdm660_cdc/msm-digital-cdc-regmap.c index 7d8ac6df14bb..a62679174567 100644 --- a/asoc/codecs/sdm660_cdc/sdm660-regmap.c +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc-regmap.c @@ -154,165 +154,6 @@ struct reg_default {MSM89XX_CDC_CORE_TX4_DMIC_CTL, 0x00}, }; -struct reg_default - msm89xx_pmic_cdc_defaults[MSM89XX_PMIC_CDC_CACHE_SIZE] = { - {MSM89XX_PMIC_DIGITAL_REVISION1, 0x00}, - {MSM89XX_PMIC_DIGITAL_REVISION2, 0x00}, - {MSM89XX_PMIC_DIGITAL_PERPH_TYPE, 0x23}, - {MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE, 0x01}, - {MSM89XX_PMIC_DIGITAL_INT_RT_STS, 0x00}, - {MSM89XX_PMIC_DIGITAL_INT_SET_TYPE, 0xFF}, - {MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH, 0xFF}, - {MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW, 0x00}, - {MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR, 0x00}, - {MSM89XX_PMIC_DIGITAL_INT_EN_SET, 0x00}, - {MSM89XX_PMIC_DIGITAL_INT_EN_CLR, 0x00}, - {MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS, 0x00}, - {MSM89XX_PMIC_DIGITAL_INT_PENDING_STS, 0x00}, - {MSM89XX_PMIC_DIGITAL_INT_MID_SEL, 0x00}, - {MSM89XX_PMIC_DIGITAL_INT_PRIORITY, 0x00}, - {MSM89XX_PMIC_DIGITAL_GPIO_MODE, 0x00}, - {MSM89XX_PMIC_DIGITAL_PIN_CTL_OE, 0x01}, - {MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA, 0x00}, - {MSM89XX_PMIC_DIGITAL_PIN_STATUS, 0x00}, - {MSM89XX_PMIC_DIGITAL_HDRIVE_CTL, 0x00}, - {MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x00}, - {MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x00}, - {MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x00}, - {MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x00}, - {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, 0x02}, - {MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, 0x02}, - {MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL, 0x00}, - {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL, 0x00}, - {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL, 0x00}, - {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL, 0x00}, - {MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL, 0x00}, - {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1, 0x7C}, - {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2, 0x7C}, - {MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3, 0x7C}, - {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0, 0x00}, - {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1, 0x00}, - {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2, 0x00}, - {MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3, 0x00}, - {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL, 0x00}, - {MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN, 0x00}, - {MSM89XX_PMIC_DIGITAL_SPARE_0, 0x00}, - {MSM89XX_PMIC_DIGITAL_SPARE_1, 0x00}, - {MSM89XX_PMIC_DIGITAL_SPARE_2, 0x00}, - {MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0x00}, - {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1, 0x00}, - {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2, 0x02}, - {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x05}, - {MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00}, - {MSM89XX_PMIC_DIGITAL_INT_TEST1, 0x00}, - {MSM89XX_PMIC_DIGITAL_INT_TEST_VAL, 0x00}, - {MSM89XX_PMIC_DIGITAL_TRIM_NUM, 0x00}, - {MSM89XX_PMIC_DIGITAL_TRIM_CTRL, 0x00}, - {MSM89XX_PMIC_ANALOG_REVISION1, 0x00}, - {MSM89XX_PMIC_ANALOG_REVISION2, 0x00}, - {MSM89XX_PMIC_ANALOG_REVISION3, 0x00}, - {MSM89XX_PMIC_ANALOG_REVISION4, 0x00}, - {MSM89XX_PMIC_ANALOG_PERPH_TYPE, 0x23}, - {MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x09}, - {MSM89XX_PMIC_ANALOG_INT_RT_STS, 0x00}, - {MSM89XX_PMIC_ANALOG_INT_SET_TYPE, 0x3F}, - {MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH, 0x3F}, - {MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW, 0x00}, - {MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR, 0x00}, - {MSM89XX_PMIC_ANALOG_INT_EN_SET, 0x00}, - {MSM89XX_PMIC_ANALOG_INT_EN_CLR, 0x00}, - {MSM89XX_PMIC_ANALOG_INT_LATCHED_STS, 0x00}, - {MSM89XX_PMIC_ANALOG_INT_PENDING_STS, 0x00}, - {MSM89XX_PMIC_ANALOG_INT_MID_SEL, 0x00}, - {MSM89XX_PMIC_ANALOG_INT_PRIORITY, 0x00}, - {MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x00}, - {MSM89XX_PMIC_ANALOG_MICB_1_VAL, 0x20}, - {MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x00}, - {MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, 0x49}, - {MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20}, - {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, 0x00}, - {MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x00}, - {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x35}, - {MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08}, - {MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x00}, - {MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x98}, - {MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, 0x00}, - {MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, 0x20}, - {MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, 0x40}, - {MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x61}, - {MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL, 0x80}, - {MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0x00}, - {MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x00}, - {MSM89XX_PMIC_ANALOG_TX_1_EN, 0x03}, - {MSM89XX_PMIC_ANALOG_TX_2_EN, 0x03}, - {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1, 0xBF}, - {MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2, 0x8C}, - {MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL, 0x00}, - {MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x6B}, - {MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, 0x51}, - {MSM89XX_PMIC_ANALOG_TX_3_EN, 0x02}, - {MSM89XX_PMIC_ANALOG_NCP_EN, 0x26}, - {MSM89XX_PMIC_ANALOG_NCP_CLK, 0x23}, - {MSM89XX_PMIC_ANALOG_NCP_DEGLITCH, 0x5B}, - {MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x08}, - {MSM89XX_PMIC_ANALOG_NCP_BIAS, 0x29}, - {MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0x24}, - {MSM89XX_PMIC_ANALOG_NCP_TEST, 0x00}, - {MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR, 0xD5}, - {MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER, 0xE8}, - {MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xCF}, - {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0x6E}, - {MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x18}, - {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0x5A}, - {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP, 0x69}, - {MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, 0x29}, - {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x80}, - {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL, 0xDA}, - {MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0x16}, - {MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x00}, - {MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20}, - {MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x00}, - {MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20}, - {MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12}, - {MSM89XX_PMIC_ANALOG_RX_ATEST, 0x00}, - {MSM89XX_PMIC_ANALOG_RX_HPH_STATUS, 0x0C}, - {MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x00}, - {MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x00}, - {MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x00}, - {MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x83}, - {MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET, 0x91}, - {MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x29}, - {MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x4D}, - {MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1}, - {MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x1E}, - {MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC, 0xCB}, - {MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x00}, - {MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x02}, - {MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE, 0x14}, - {MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x00}, - {MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x1F}, - {MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x8C}, - {MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE, 0xC0}, - {MSM89XX_PMIC_ANALOG_BOOST_TEST1_1, 0x00}, - {MSM89XX_PMIC_ANALOG_BOOST_TEST_2, 0x00}, - {MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS, 0x00}, - {MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS, 0x00}, - {MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR, 0x00}, - {MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL, 0x00}, - {MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0x00}, - {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1, 0x00}, - {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2, 0x01}, - {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x05}, - {MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00}, - {MSM89XX_PMIC_ANALOG_INT_TEST1, 0x00}, - {MSM89XX_PMIC_ANALOG_INT_TEST_VAL, 0x00}, - {MSM89XX_PMIC_ANALOG_TRIM_NUM, 0x04}, - {MSM89XX_PMIC_ANALOG_TRIM_CTRL1, 0x00}, - {MSM89XX_PMIC_ANALOG_TRIM_CTRL2, 0x00}, - {MSM89XX_PMIC_ANALOG_TRIM_CTRL3, 0x00}, - {MSM89XX_PMIC_ANALOG_TRIM_CTRL4, 0x00}, -}; - static const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE] = { [MSM89XX_CDC_CORE_CLK_RX_RESET_CTL] = 1, [MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL] = 1, From 804f26b95f9c79c1ae988d38b471ac60e9c0f9b8 Mon Sep 17 00:00:00 2001 From: Rohit Kumar Date: Mon, 2 Oct 2017 10:35:21 +0530 Subject: [PATCH 051/276] asoc: sdm660: add sclk and mclk support for quinary interfaces Add sclk and mclk support for quinary MI2S interfaces for sdm660. Also, update MCLK IDs for secondary and quaternary MI2S. Change-Id: If6271224caffbf1623939d409e3e031d5a0c8423 Signed-off-by: Rohit Kumar --- asoc/sdm660-common.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c index 78218ab056a3..e90037a1d211 100644 --- a/asoc/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -208,7 +208,8 @@ static u32 mi2s_ebit_clk[MI2S_MAX] = { Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT, Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT, Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT, - Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT + Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT }; struct msm_wsa881x_dev_info { @@ -401,6 +402,14 @@ static struct afe_clk_set mi2s_clk[MI2S_MAX] = { Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, Q6AFE_LPASS_CLK_ROOT_DEFAULT, 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT, + Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, } }; @@ -415,7 +424,7 @@ static struct afe_clk_set mi2s_mclk[MI2S_MAX] = { }, { AFE_API_VERSION_I2S_CONFIG, - Q6AFE_LPASS_CLK_ID_MCLK_4, + Q6AFE_LPASS_CLK_ID_MCLK_2, Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, Q6AFE_LPASS_CLK_ROOT_DEFAULT, @@ -431,7 +440,15 @@ static struct afe_clk_set mi2s_mclk[MI2S_MAX] = { }, { AFE_API_VERSION_I2S_CONFIG, - Q6AFE_LPASS_CLK_ID_MCLK_2, + Q6AFE_LPASS_CLK_ID_MCLK_1, + Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, + Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, + 0, + }, + { + AFE_API_VERSION_I2S_CONFIG, + Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR, Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, Q6AFE_LPASS_CLK_ROOT_DEFAULT, From af88e4c53bf79693197d64a2adfbede01aa196c6 Mon Sep 17 00:00:00 2001 From: Rohit Kumar Date: Wed, 4 Oct 2017 13:47:10 +0530 Subject: [PATCH 052/276] asoc: sdm660: configure mi2s gpios from machine driver Add support to configure MI2S gpios from machine driver. Change-Id: I561fdc775c63fd7b31d41a3053956d1643bea566 Signed-off-by: Rohit Kumar --- asoc/sdm660-common.c | 30 +++++++++++++++++++++--------- asoc/sdm660-common.h | 10 ++++++++++ 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c index e90037a1d211..e6b82dfda7a2 100644 --- a/asoc/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -170,15 +170,6 @@ static struct dev_config usb_tx_cfg = { .channels = 1, }; -enum { - PRIM_MI2S = 0, - SEC_MI2S, - TERT_MI2S, - QUAT_MI2S, - QUIN_MI2S, - MI2S_MAX, -}; - enum { PRIM_AUX_PCM = 0, SEC_AUX_PCM, @@ -2631,6 +2622,8 @@ int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) int port_id = msm_get_port_id(rtd->dai_link->id); int index = cpu_dai->id; unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); dev_dbg(rtd->card->dev, "%s: substream = %s stream = %d, dai name %s, dai ID %d\n", @@ -2682,6 +2675,9 @@ int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) goto clk_off; } } + if (pdata->mi2s_gpio_p[index]) + msm_cdc_pinctrl_select_active_state( + pdata->mi2s_gpio_p[index]); } mutex_unlock(&mi2s_intf_conf[index].lock); return 0; @@ -2708,6 +2704,8 @@ void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; int port_id = msm_get_port_id(rtd->dai_link->id); int index = rtd->cpu_dai->id; + struct msm_asoc_mach_data *pdata = + snd_soc_card_get_drvdata(rtd->card); pr_debug("%s(): substream = %s stream = %d\n", __func__, substream->name, substream->stream); @@ -2718,6 +2716,10 @@ void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) mutex_lock(&mi2s_intf_conf[index].lock); if (--mi2s_intf_conf[index].ref_cnt == 0) { + if (pdata->mi2s_gpio_p[index]) + msm_cdc_pinctrl_select_sleep_state( + pdata->mi2s_gpio_p[index]); + ret = msm_mi2s_set_sclk(substream, false); if (ret < 0) { pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", @@ -3262,6 +3264,16 @@ static int msm_asoc_machine_probe(struct platform_device *pdev) "qcom,cdc-ext-spk-gpios", 0); } + pdata->mi2s_gpio_p[PRIM_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,pri-mi2s-gpios", 0); + pdata->mi2s_gpio_p[SEC_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,sec-mi2s-gpios", 0); + pdata->mi2s_gpio_p[TERT_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,tert-mi2s-gpios", 0); + pdata->mi2s_gpio_p[QUAT_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,quat-mi2s-gpios", 0); + pdata->mi2s_gpio_p[QUIN_MI2S] = of_parse_phandle(pdev->dev.of_node, + "qcom,quin-mi2s-gpios", 0); /* * 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 diff --git a/asoc/sdm660-common.h b/asoc/sdm660-common.h index 3276d1fa2c58..2c8e29e8792f 100644 --- a/asoc/sdm660-common.h +++ b/asoc/sdm660-common.h @@ -60,6 +60,15 @@ struct tdm_port { u32 channel; }; +enum { + PRIM_MI2S = 0, + SEC_MI2S, + TERT_MI2S, + QUAT_MI2S, + QUIN_MI2S, + MI2S_MAX, +}; + enum { DIG_CDC, ANA_CDC, @@ -90,6 +99,7 @@ struct msm_asoc_mach_data { struct device_node *comp_gpio_p; /* used by pinctrl API */ struct device_node *dmic_gpio_p; /* used by pinctrl API */ struct device_node *ext_spk_gpio_p; /* used by pinctrl API */ + struct device_node *mi2s_gpio_p[MI2S_MAX]; /* used by pinctrl API */ struct snd_soc_codec *codec; struct sdm660_codec sdm660_codec_fn; struct snd_info_entry *codec_root; From e87298f55a186eb290cce6a924ec895c88095715 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Mon, 11 Sep 2017 13:34:34 -0700 Subject: [PATCH 053/276] asoc: wcd934x: update micbias noise filter resistance Update micbias noise filter resistance for better performance during audio capture. CRs-Fixed: 2055467 Change-Id: I56eb08ca9785d36cf577f32297f2c719224f0f65 Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd934x/wcd934x.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 66c7830e2a3f..7fd1e63dd6fe 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -8633,6 +8633,10 @@ static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { {WCD934X_TLMM_DMIC3_DATA_PINCFG, 0xFF, 0x0a}, {WCD934X_CPE_SS_SVA_CFG, 0x60, 0x00}, {WCD934X_CPE_SS_CPAR_CFG, 0x10, 0x10}, + {WCD934X_MICB1_TEST_CTL_1, 0xff, 0xfa}, + {WCD934X_MICB2_TEST_CTL_1, 0xff, 0xfa}, + {WCD934X_MICB3_TEST_CTL_1, 0xff, 0xfa}, + {WCD934X_MICB4_TEST_CTL_1, 0xff, 0xfa}, }; static void tavil_codec_init_reg(struct tavil_priv *priv) From 0cf63fe351d62f05e49120a6e4c0d7a6e5181867 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 25 Sep 2017 18:41:31 -0700 Subject: [PATCH 054/276] soc: define dummy function for swrm_wcd_notify Add dummy function for swrm_wcd_notify to avoid compilation errors when configuration options 'CONFIG_SOUNDWIRE' and 'CONFIG_SOUNDWIRE_WCD_CTRL' are not selected for a target. Change-Id: Ie12d2df8777934494f0f8607d846e2d1ec49f2a6 Signed-off-by: Xiaoyu Ye --- include/soc/swr-wcd.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/include/soc/swr-wcd.h b/include/soc/swr-wcd.h index 041b901fd954..f6ff61a22fe2 100644 --- a/include/soc/swr-wcd.h +++ b/include/soc/swr-wcd.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* 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 @@ -30,6 +30,13 @@ struct swr_mstr_port { u8 *port; }; +#if IS_ENABLED(CONFIG_SOUNDWIRE_WCD_CTRL) extern int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data); - +#else /* CONFIG_SOUNDWIRE_WCD_CTRL */ +static inline int swrm_wcd_notify(struct platform_device *pdev, u32 id, + void *data) +{ + return 0; +} +#endif /* CONFIG_SOUNDWIRE_WCD_CTRL */ #endif /* _LINUX_SWR_WCD_H */ From dbe57dc5a7ee412c0146663ad52b571a1279dcdc Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 5 Oct 2017 15:22:00 -0700 Subject: [PATCH 055/276] dsp: remove wakelock.h includes These includes aren't used and the file will go away in the future, so just drop it. Change-Id: I9bfcd39fd775e16765c46ea99da087292fe2e461 Signed-off-by: Stephen Boyd --- dsp/codecs/audio_utils_aio.h | 1 - dsp/q6afe.c | 1 - dsp/usf.c | 1 - 3 files changed, 3 deletions(-) diff --git a/dsp/codecs/audio_utils_aio.h b/dsp/codecs/audio_utils_aio.h index 82374f9a17a9..e44f9d9c642a 100644 --- a/dsp/codecs/audio_utils_aio.h +++ b/dsp/codecs/audio_utils_aio.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 0289144006fe..3cdf2eccb547 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/dsp/usf.c b/dsp/usf.c index dfa2097ddf67..f75439de4e94 100644 --- a/dsp/usf.c +++ b/dsp/usf.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include "q6usm.h" From c08b14f78c7b0064c4012ad0d4cbdc133a5ffa22 Mon Sep 17 00:00:00 2001 From: Rohit Kumar Date: Fri, 6 Oct 2017 10:52:17 +0530 Subject: [PATCH 056/276] dsp: add quinary interface support in q6afe Add support for quinary interfaces in q6afe. Also, fix routing for quinary tdm rx port. Change-Id: I5db68da1cc8bc17bc5e7b743282abf83fb100bbc Signed-off-by: Rohit Kumar --- asoc/msm-pcm-routing-v2.c | 8 ++-- dsp/q6afe.c | 80 +++++++++++++++++++++++++++++++++++++++ include/dsp/q6afe-v2.h | 19 ++++++++++ 3 files changed, 103 insertions(+), 4 deletions(-) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 60119264f9b9..3338ac6834a5 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -16533,7 +16533,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, {"QUIN_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, {"QUIN_TDM_RX_0 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, - {"QUIN_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Port Mixer"}, + {"QUIN_TDM_RX_0", NULL, "QUIN_TDM_RX_0 Port Mixer"}, {"QUIN_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"QUIN_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, @@ -16555,7 +16555,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, {"QUIN_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, {"QUIN_TDM_RX_1 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, - {"QUIN_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Port Mixer"}, + {"QUIN_TDM_RX_1", NULL, "QUIN_TDM_RX_1 Port Mixer"}, {"QUIN_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"QUIN_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, @@ -16577,7 +16577,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, {"QUIN_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, {"QUIN_TDM_RX_2 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, - {"QUIN_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Port Mixer"}, + {"QUIN_TDM_RX_2", NULL, "QUIN_TDM_RX_2 Port Mixer"}, {"QUIN_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"QUIN_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, @@ -16599,7 +16599,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_1", "QUIN_TDM_TX_1"}, {"QUIN_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_2", "QUIN_TDM_TX_2"}, {"QUIN_TDM_RX_3 Port Mixer", "QUIN_TDM_TX_3", "QUIN_TDM_TX_3"}, - {"QUIN_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Port Mixer"}, + {"QUIN_TDM_RX_3", NULL, "QUIN_TDM_RX_3 Port Mixer"}, {"INT0_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"INT0_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"}, diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 0289144006fe..8e96f6fecbf7 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -495,6 +495,7 @@ int afe_get_port_type(u16 port_id) 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_QUINARY_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: @@ -527,6 +528,14 @@ int afe_get_port_type(u16 port_id) 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_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: case AFE_PORT_ID_USB_RX: case AFE_PORT_ID_INT0_MI2S_RX: case AFE_PORT_ID_INT1_MI2S_RX: @@ -566,6 +575,7 @@ int afe_get_port_type(u16 port_id) 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_QUINARY_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: @@ -598,6 +608,14 @@ int afe_get_port_type(u16 port_id) 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_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_7: case AFE_PORT_ID_USB_TX: case AFE_PORT_ID_INT0_MI2S_TX: case AFE_PORT_ID_INT1_MI2S_TX: @@ -685,6 +703,8 @@ int afe_sizeof_cfg_cmd(u16 port_id) case AFE_PORT_ID_TERTIARY_PCM_TX: case AFE_PORT_ID_QUATERNARY_PCM_RX: case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_PCM_TX: default: pr_debug("%s: default case 0x%x\n", __func__, port_id); ret_size = SIZEOF_CFG_CMD(afe_param_id_pcm_cfg); @@ -3071,6 +3091,8 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config, case AFE_PORT_ID_TERTIARY_PCM_TX: case AFE_PORT_ID_QUATERNARY_PCM_RX: case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_PCM_TX: cfg_type = AFE_PARAM_ID_PCM_CONFIG; break; case PRIMARY_I2S_RX: @@ -3288,6 +3310,10 @@ int afe_get_port_index(u16 port_id) return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX; case AFE_PORT_ID_QUATERNARY_PCM_TX: return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX; + case AFE_PORT_ID_QUINARY_PCM_RX: + return IDX_AFE_PORT_ID_QUINARY_PCM_RX; + case AFE_PORT_ID_QUINARY_PCM_TX: + return IDX_AFE_PORT_ID_QUINARY_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; @@ -3481,6 +3507,38 @@ int afe_get_port_index(u16 port_id) 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_QUINARY_TDM_RX: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_0; + case AFE_PORT_ID_QUINARY_TDM_TX: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_0; + case AFE_PORT_ID_QUINARY_TDM_RX_1: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_1; + case AFE_PORT_ID_QUINARY_TDM_TX_1: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_1; + case AFE_PORT_ID_QUINARY_TDM_RX_2: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_2; + case AFE_PORT_ID_QUINARY_TDM_TX_2: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_2; + case AFE_PORT_ID_QUINARY_TDM_RX_3: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_3; + case AFE_PORT_ID_QUINARY_TDM_TX_3: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_3; + case AFE_PORT_ID_QUINARY_TDM_RX_4: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_4; + case AFE_PORT_ID_QUINARY_TDM_TX_4: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_4; + case AFE_PORT_ID_QUINARY_TDM_RX_5: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_5; + case AFE_PORT_ID_QUINARY_TDM_TX_5: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_5; + case AFE_PORT_ID_QUINARY_TDM_RX_6: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_6; + case AFE_PORT_ID_QUINARY_TDM_TX_6: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_6; + case AFE_PORT_ID_QUINARY_TDM_RX_7: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_7; + case AFE_PORT_ID_QUINARY_TDM_TX_7: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_7; case AFE_PORT_ID_INT0_MI2S_RX: return IDX_AFE_PORT_ID_INT0_MI2S_RX; case AFE_PORT_ID_INT0_MI2S_TX: @@ -3597,6 +3655,8 @@ int afe_open(u16 port_id, case AFE_PORT_ID_TERTIARY_PCM_TX: case AFE_PORT_ID_QUATERNARY_PCM_RX: case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_PCM_TX: cfg_type = AFE_PARAM_ID_PCM_CONFIG; break; case SECONDARY_I2S_RX: @@ -3980,6 +4040,8 @@ int afe_port_group_set_param(u16 group_id, case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX: case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX: case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX: + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_RX: + case AFE_GROUP_DEVICE_ID_QUINARY_TDM_TX: cfg_type = AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG; break; default: @@ -5262,6 +5324,8 @@ int afe_validate_port(u16 port_id) case AFE_PORT_ID_TERTIARY_PCM_TX: case AFE_PORT_ID_QUATERNARY_PCM_RX: case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_PCM_TX: case SECONDARY_I2S_RX: case SECONDARY_I2S_TX: case MI2S_RX: @@ -5376,6 +5440,22 @@ int afe_validate_port(u16 port_id) 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_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: + case AFE_PORT_ID_QUINARY_TDM_TX_7: case AFE_PORT_ID_INT0_MI2S_RX: case AFE_PORT_ID_INT1_MI2S_RX: case AFE_PORT_ID_INT2_MI2S_RX: diff --git a/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h index 7df993e61c1c..8fb480b3bb6d 100644 --- a/include/dsp/q6afe-v2.h +++ b/include/dsp/q6afe-v2.h @@ -205,6 +205,25 @@ enum { IDX_AFE_PORT_ID_INT5_MI2S_TX, IDX_AFE_PORT_ID_INT6_MI2S_RX, IDX_AFE_PORT_ID_INT6_MI2S_TX, + /* IDX 143-> 160 */ + IDX_AFE_PORT_ID_QUINARY_PCM_RX, + IDX_AFE_PORT_ID_QUINARY_PCM_TX, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_0, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_0, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_1, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_1, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_2, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_2, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_3, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_3, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_4, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_4, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_5, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_5, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_6, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_6, + IDX_AFE_PORT_ID_QUINARY_TDM_RX_7, + IDX_AFE_PORT_ID_QUINARY_TDM_TX_7, AFE_MAX_PORTS }; From 7fccd584b649ce06d528d7d374698f5813a26c41 Mon Sep 17 00:00:00 2001 From: Rohit Kumar Date: Tue, 10 Oct 2017 19:32:44 +0530 Subject: [PATCH 057/276] soc: pinctrl-lpi: update sizeof lpi offset to 32bit Update sizeof lpi register offset to 32 bit to accommodate all register offset for sdm670. Change-Id: I9a3fdb56f9faf6c0aff627cbedaa54db606d2434 Signed-off-by: Rohit Kumar --- soc/pinctrl-lpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soc/pinctrl-lpi.c b/soc/pinctrl-lpi.c index 1e7ead817399..0fa7b60d068f 100644 --- a/soc/pinctrl-lpi.c +++ b/soc/pinctrl-lpi.c @@ -81,7 +81,7 @@ enum lpi_gpio_func_index { * @function: See lpi_gpio_functions[] */ struct lpi_gpio_pad { - u16 offset; + u32 offset; bool output_enabled; bool value; char __iomem *base; From 2e3e0f21804b74d85b97e77175b4172af767e790 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Wed, 11 Oct 2017 19:57:35 +0530 Subject: [PATCH 058/276] ASoC: codecs: sdm660_cdc: Fix pop noise issue at DMIC Pop noise occurs during switch from handset to speaker mode during voice call. This is because sufficient delay is not provided for tx to unmute. A delayed workqueue that will do unmute at the end solves the issue. CRs-Fixed: 2101423 Change-Id: I1ae7068b23158bb8428d5b96e9f65fb05a67ff7e Signed-off-by: Vatsal Bucha --- asoc/codecs/sdm660_cdc/msm-digital-cdc.c | 106 +++++++++-------------- asoc/codecs/sdm660_cdc/msm-digital-cdc.h | 7 ++ 2 files changed, 50 insertions(+), 63 deletions(-) diff --git a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c index 3df917f717b5..e7c7147c0f7b 100644 --- a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -55,6 +55,11 @@ static unsigned long tx_digital_gain_reg[] = { MSM89XX_CDC_CORE_TX5_VOL_CTL_GAIN, }; +#define SDM660_TX_UNMUTE_DELAY_MS 40 +static int tx_unmute_delay = SDM660_TX_UNMUTE_DELAY_MS; +module_param(tx_unmute_delay, int, 0664); +MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path"); + static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0); struct snd_soc_codec *registered_digcodec; @@ -924,6 +929,9 @@ static int msm_dig_cdc_codec_enable_dec(struct snd_soc_dapm_widget *w, /* enable HPF */ snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x00); + schedule_delayed_work( + &msm_dig_cdc->tx_mute_dwork[decimator - 1].dwork, + msecs_to_jiffies(tx_unmute_delay)); if (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq != CF_MIN_3DB_150HZ) { @@ -937,20 +945,14 @@ static int msm_dig_cdc_codec_enable_dec(struct snd_soc_dapm_widget *w, snd_soc_read(codec, tx_digital_gain_reg[w->shift + offset]) ); - if (pdata->lb_mode) { - pr_debug("%s: loopback mode unmute the DEC\n", - __func__); - snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); - } - snd_soc_update_bits(codec, tx_vol_ctl_reg, - 0x01, 0x00); - break; case SND_SOC_DAPM_PRE_PMD: snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01); msleep(20); snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08); cancel_delayed_work_sync(&tx_hpf_work[decimator - 1].dwork); + cancel_delayed_work_sync( + &msm_dig_cdc->tx_mute_dwork[decimator - 1].dwork); break; case SND_SOC_DAPM_POST_PMD: snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, @@ -1191,6 +1193,35 @@ int msm_dig_codec_info_create_codec_entry(struct snd_info_entry *codec_root, } EXPORT_SYMBOL(msm_dig_codec_info_create_codec_entry); +static void sdm660_tx_mute_update_callback(struct work_struct *work) +{ + struct tx_mute_work *tx_mute_dwork; + struct snd_soc_codec *codec = NULL; + struct msm_dig_priv *dig_cdc; + struct delayed_work *delayed_work; + u16 tx_vol_ctl_reg = 0; + u8 decimator = 0, i; + + delayed_work = to_delayed_work(work); + tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork); + dig_cdc = tx_mute_dwork->dig_cdc; + codec = dig_cdc->codec; + + for (i = 0; i < (NUM_DECIMATORS - 1); i++) { + if (dig_cdc->dec_active[i]) + decimator = i + 1; + if (decimator && decimator < NUM_DECIMATORS) { + /* unmute decimators corresponding to Tx DAI's*/ + tx_vol_ctl_reg = + MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG + + 32 * (decimator - 1); + snd_soc_update_bits(codec, tx_vol_ctl_reg, + 0x01, 0x00); + } + decimator = 0; + } +} + static int msm_dig_cdc_soc_probe(struct snd_soc_codec *codec) { struct msm_dig_priv *msm_dig_cdc = dev_get_drvdata(codec->dev); @@ -1207,6 +1238,10 @@ static int msm_dig_cdc_soc_probe(struct snd_soc_codec *codec) tx_hpf_work[i].decimator = i + 1; INIT_DELAYED_WORK(&tx_hpf_work[i].dwork, tx_hpf_corner_freq_callback); + msm_dig_cdc->tx_mute_dwork[i].dig_cdc = msm_dig_cdc; + msm_dig_cdc->tx_mute_dwork[i].decimator = i + 1; + INIT_DELAYED_WORK(&msm_dig_cdc->tx_mute_dwork[i].dwork, + sdm660_tx_mute_update_callback); } for (i = 0; i < MSM89XX_RX_MAX; i++) @@ -1891,63 +1926,8 @@ static const struct snd_kcontrol_new msm_dig_snd_controls[] = { MSM89XX_CDC_CORE_TX5_MUX_CTL, 3, 1, 0), }; -static int msm_dig_cdc_digital_mute(struct snd_soc_dai *dai, int mute) -{ - struct snd_soc_codec *codec = NULL; - u16 tx_vol_ctl_reg = 0; - u8 decimator = 0, i; - struct msm_dig_priv *dig_cdc; - - pr_debug("%s: Digital Mute val = %d\n", __func__, mute); - - if (!dai || !dai->codec) { - pr_err("%s: Invalid params\n", __func__); - return -EINVAL; - } - codec = dai->codec; - dig_cdc = snd_soc_codec_get_drvdata(codec); - - if (dai->id == AIF1_PB) { - dev_dbg(codec->dev, "%s: Not capture use case skip\n", - __func__); - return 0; - } - - mute = (mute) ? 1 : 0; - if (!mute) { - /* - * 15 ms is an emperical value for the mute time - * that was arrived by checking the pop level - * to be inaudible - */ - usleep_range(15000, 15010); - } - - if (dai->id == AIF3_SVA) { - snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG, 0x01, mute); - goto ret; - } - for (i = 0; i < (NUM_DECIMATORS - 1); i++) { - if (dig_cdc->dec_active[i]) - decimator = i + 1; - if (decimator && decimator < NUM_DECIMATORS) { - /* mute/unmute decimators corresponding to Tx DAI's */ - tx_vol_ctl_reg = - MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG + - 32 * (decimator - 1); - snd_soc_update_bits(codec, tx_vol_ctl_reg, - 0x01, mute); - } - decimator = 0; - } -ret: - return 0; -} - static struct snd_soc_dai_ops msm_dig_dai_ops = { .hw_params = msm_dig_cdc_hw_params, - .digital_mute = msm_dig_cdc_digital_mute, }; diff --git a/asoc/codecs/sdm660_cdc/msm-digital-cdc.h b/asoc/codecs/sdm660_cdc/msm-digital-cdc.h index f0e7a9cf9228..42c31d5089b2 100644 --- a/asoc/codecs/sdm660_cdc/msm-digital-cdc.h +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc.h @@ -32,6 +32,12 @@ enum { MSM89XX_RX_MAX, }; +struct tx_mute_work { + struct msm_dig_priv *dig_cdc; + u32 decimator; + struct delayed_work dwork; +}; + struct msm_dig_priv { struct snd_soc_codec *codec; u32 comp_enabled[MSM89XX_RX_MAX]; @@ -54,6 +60,7 @@ struct msm_dig_priv { int (*register_notifier)(void *handle, struct notifier_block *nblock, bool enable); + struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS]; }; struct dig_ctrl_platform_data { From 899b78bd181688a8f5557a456cd34f657b3f0f8c Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Thu, 7 Sep 2017 12:36:06 -0700 Subject: [PATCH 059/276] dsp: add support for aptX dual mono Send sync mode parameter to DSP for supporting aptX dual mono. Change-Id: I1c43b0dec6661a77644e28df45ca6c1a182745de Signed-off-by: Aniket Kumar Lata --- asoc/msm-dai-q6-v2.c | 8 ++++++++ dsp/q6afe.c | 18 ++++++++++++++++++ include/dsp/apr_audio-v2.h | 27 +++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 2802a4fa170a..53622e085465 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -2249,6 +2249,10 @@ static int msm_dai_q6_afe_enc_cfg_get(struct snd_kcontrol *kcontrol, sizeof(struct asm_aac_enc_cfg_v2_t)); break; case ENC_FMT_APTX: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_aptx_enc_cfg_t)); + break; case ENC_FMT_APTX_HD: memcpy(ucontrol->value.bytes.data + format_size, &dai_data->enc_config.data, @@ -2298,6 +2302,10 @@ static int msm_dai_q6_afe_enc_cfg_put(struct snd_kcontrol *kcontrol, sizeof(struct asm_aac_enc_cfg_v2_t)); break; case ENC_FMT_APTX: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_aptx_enc_cfg_t)); + break; case ENC_FMT_APTX_HD: memcpy(&dai_data->enc_config.data, ucontrol->value.bytes.data + format_size, diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 8e96f6fecbf7..2bdfd10fdc32 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -2895,6 +2895,24 @@ static int q6afe_send_enc_config(u16 port_id, goto exit; } + if (format == ASM_MEDIA_FMT_APTX) { + config.param.payload_size = + payload_size + sizeof(config.port.sync_mode_param); + pr_debug("%s: sending AFE_PARAM_ID_APTX_SYNC_MODE to DSP", + __func__); + config.pdata.param_id = AFE_PARAM_ID_APTX_SYNC_MODE; + config.pdata.param_size = sizeof(config.port.sync_mode_param); + config.port.sync_mode_param.sync_mode = + config.port.enc_blk_param.enc_blk_config.aptx_config. + aptx_v2_cfg.sync_mode; + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE_PARAM_ID_APTX_SYNC_MODE 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", diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index df635ccad1cd..be56d74eef43 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -3074,6 +3074,16 @@ struct afe_param_id_set_topology_cfg { u32 topology_id; } __packed; +#define AFE_PARAM_ID_APTX_SYNC_MODE 0x00013205 + +struct afe_param_id_aptx_sync_mode { + /* + * sync mode: 0x0 = stereo sync mode (default) + * 0x01 = dual mono sync mode + * 0x02 = dual mono with no sync on either L or R + */ + uint32_t sync_mode; +} __packed; /* * Generic encoder module ID. @@ -3305,6 +3315,21 @@ struct asm_custom_enc_cfg_t { uint8_t channel_mapping[8]; uint32_t custom_size; } __packed; + +struct asm_aptx_v2_enc_cfg_ext_t { + /* + * sync mode: 0x0 = stereo sync mode (default) + * 0x01 = dual mono sync mode + * 0x02 = dual mono with no sync on either L or R + */ + uint32_t sync_mode; +} __packed; + +struct asm_aptx_enc_cfg_t { + struct asm_custom_enc_cfg_t custom_cfg; + struct asm_aptx_v2_enc_cfg_ext_t aptx_v2_cfg; +} __packed; + #define ASM_MEDIA_FMT_CELT 0x00013221 struct asm_celt_specific_enc_cfg_t { /* @@ -3421,6 +3446,7 @@ union afe_enc_config_data { struct asm_aac_enc_cfg_v2_t aac_config; struct asm_custom_enc_cfg_t custom_config; struct asm_celt_enc_cfg_t celt_config; + struct asm_aptx_enc_cfg_t aptx_config; }; struct afe_enc_config { @@ -3461,6 +3487,7 @@ union afe_port_config { 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_param_id_aptx_sync_mode sync_mode_param; 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; From ded7966a3b973a51aa971efb8724e00e6add5b5d Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Thu, 12 Oct 2017 07:51:35 +0530 Subject: [PATCH 060/276] ASoC: codecs: sdm660_cdc: Fix kernel panic on sdm660-internal Kernel panic is observed during SSR. This is because ADSP does not get sufficient time to bring up LPASS after SSR. An increase in ADSP Ready timeout resolves the issue. CRs-Fixed: 2101404 Change-Id: Ia7d1ecde7a713b4d71b2a4ebb1f85a38b8fa28b5 Signed-off-by: Vatsal Bucha --- asoc/codecs/sdm660_cdc/msm-analog-cdc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c index ddef508cb682..7508f0363423 100644 --- a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -49,10 +49,10 @@ #define BUS_DOWN 1 /* - * 50 Milliseconds sufficient for DSP bring up in the lpass + * 200 Milliseconds sufficient for DSP bring up in the lpass * after Sub System Restart */ -#define ADSP_STATE_READY_TIMEOUT_MS 50 +#define ADSP_STATE_READY_TIMEOUT_MS 200 #define EAR_PMD 0 #define EAR_PMU 1 From 8e4368d3dddd841149cf845dde957e4acce9a73d Mon Sep 17 00:00:00 2001 From: Ben Romberger Date: Fri, 18 Aug 2017 15:54:18 -0700 Subject: [PATCH 061/276] ASoC: msm: qdsp6v2: Add S24_3LE support to DP driver Add support for HDMI and DP for S24_3LE bit format. This allows playback of 24bit audio on the external device. Change-Id: Ia6e1148b2e5d13cc0ff6f2372d61c83c3e704255 Signed-off-by: Ben Romberger --- asoc/codecs/msm_hdmi_codec_rx.c | 3 ++- asoc/msm-dai-q6-hdmi-v2.c | 8 ++++++-- asoc/sdm845.c | 10 ++++++++-- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/asoc/codecs/msm_hdmi_codec_rx.c b/asoc/codecs/msm_hdmi_codec_rx.c index 46cfe7dede26..3a48f530d1ef 100644 --- a/asoc/codecs/msm_hdmi_codec_rx.c +++ b/asoc/codecs/msm_hdmi_codec_rx.c @@ -489,7 +489,8 @@ static struct snd_soc_dai_driver msm_ext_disp_audio_codec_rx_dais[] = { .rates = 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_LE | + SNDRV_PCM_FMTBIT_S24_3LE, }, .ops = &msm_ext_disp_audio_codec_rx_dai_ops, }, diff --git a/asoc/msm-dai-q6-hdmi-v2.c b/asoc/msm-dai-q6-hdmi-v2.c index 9c8d20a9f2a7..212c4e3274fb 100644 --- a/asoc/msm-dai-q6-hdmi-v2.c +++ b/asoc/msm-dai-q6-hdmi-v2.c @@ -217,6 +217,7 @@ static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream, dai_data->port_config.hdmi_multi_ch.bit_width = 16; break; case SNDRV_PCM_FORMAT_S24_LE: + case SNDRV_PCM_FORMAT_S24_3LE: dai_data->port_config.hdmi_multi_ch.bit_width = 24; break; } @@ -435,7 +436,9 @@ static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = { SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, .channels_min = 2, .channels_max = 8, .rate_max = 192000, @@ -457,7 +460,8 @@ static struct snd_soc_dai_driver msm_dai_q6_display_port_rx_dai[] = { SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE, .channels_min = 2, .channels_max = 8, .rate_max = 192000, diff --git a/asoc/sdm845.c b/asoc/sdm845.c index 5721c323e0b5..0506636e8367 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -408,7 +408,8 @@ static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four", 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 *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE", + "S24_3LE"}; static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32", "KHZ_44P1", "KHZ_48", "KHZ_88P2", "KHZ_96", "KHZ_176P4", @@ -1420,10 +1421,12 @@ static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol, return idx; switch (ext_disp_rx_cfg[idx].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; @@ -1445,6 +1448,9 @@ static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol, return idx; switch (ucontrol->value.integer.value[0]) { + case 2: + ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_3LE; + break; case 1: ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE; break; From 88513a31e56deee44b87338eeb0a2446869675d4 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Thu, 12 Oct 2017 12:29:25 +0530 Subject: [PATCH 062/276] dsp: add support to set topology specific info in voice usecases Add support to set number of channels and channel mapping to DSP as per the info received from ACDB files along with topology for a device in voice usecases instead of reading from backend configuration. When topology specific channel info is not supported, send the default no of channels and channel mapping as per backend to support backward compatibility. CRs-Fixed: 2110934 Change-Id: Iad512474fc25c906d97513f6648cd8ba6244eda1 Signed-off-by: Aditya Bavanari --- dsp/q6voice.c | 133 +++++++++++++++++---- include/dsp/q6voice.h | 2 + include/uapi/linux/msm_audio_calibration.h | 8 ++ 3 files changed, 118 insertions(+), 25 deletions(-) diff --git a/dsp/q6voice.c b/dsp/q6voice.c index fa910caf9296..983e0a2388ca 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -2368,6 +2368,9 @@ static int voice_send_set_device_cmd(struct voice_data *v) &cvp_setdev_cmd.cvp_set_device_v2.tx_topology_id, &cvp_setdev_cmd.cvp_set_device_v2.rx_topology_id); + voice_set_topology_specific_info(v, CVP_VOC_RX_TOPOLOGY_CAL); + voice_set_topology_specific_info(v, CVP_VOC_TX_TOPOLOGY_CAL); + 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; @@ -2722,6 +2725,9 @@ static int voice_send_cvp_create_cmd(struct voice_data *v) &cvp_session_cmd.cvp_session.tx_topology_id, &cvp_session_cmd.cvp_session.rx_topology_id); + voice_set_topology_specific_info(v, CVP_VOC_RX_TOPOLOGY_CAL); + voice_set_topology_specific_info(v, CVP_VOC_TX_TOPOLOGY_CAL); + 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; @@ -3864,6 +3870,9 @@ static int voice_send_cvp_channel_info_v2(struct voice_data *v, VSS_PARAM_VOCPROC_RX_CHANNEL_INFO; channel_info->num_channels = v->dev_rx.no_of_channels; channel_info->bits_per_sample = v->dev_rx.bits_per_sample; + memcpy(&channel_info->channel_mapping, + v->dev_rx.channel_mapping, + VSS_NUM_CHANNELS_MAX * sizeof(uint8_t)); break; case TX_PATH: @@ -3871,6 +3880,9 @@ static int voice_send_cvp_channel_info_v2(struct voice_data *v, VSS_PARAM_VOCPROC_TX_CHANNEL_INFO; channel_info->num_channels = v->dev_tx.no_of_channels; channel_info->bits_per_sample = v->dev_tx.bits_per_sample; + memcpy(&channel_info->channel_mapping, + v->dev_tx.channel_mapping, + VSS_NUM_CHANNELS_MAX * sizeof(uint8_t)); break; case EC_REF_PATH: @@ -3878,6 +3890,9 @@ static int voice_send_cvp_channel_info_v2(struct voice_data *v, VSS_PARAM_VOCPROC_EC_REF_CHANNEL_INFO; channel_info->num_channels = v->dev_rx.no_of_channels; channel_info->bits_per_sample = v->dev_rx.bits_per_sample; + memcpy(&channel_info->channel_mapping, + v->dev_rx.channel_mapping, + VSS_NUM_CHANNELS_MAX * sizeof(uint8_t)); break; default: pr_err("%s: Invalid param type\n", @@ -3886,21 +3901,6 @@ static int voice_send_cvp_channel_info_v2(struct voice_data *v, goto done; } - if (channel_info->num_channels == NUM_CHANNELS_MONO) { - channel_info->channel_mapping[0] = PCM_CHANNEL_FC; - } else if (channel_info->num_channels == NUM_CHANNELS_STEREO) { - channel_info->channel_mapping[0] = PCM_CHANNEL_FL; - channel_info->channel_mapping[1] = PCM_CHANNEL_FR; - } else if (channel_info->num_channels == NUM_CHANNELS_QUAD && - param_type == TX_PATH) { - channel_info->channel_mapping[0] = PCM_CHANNEL_FL; - channel_info->channel_mapping[1] = PCM_CHANNEL_FR; - channel_info->channel_mapping[2] = PCM_CHANNEL_LS; - channel_info->channel_mapping[3] = PCM_CHANNEL_RS; - } else { - pr_warn("%s: Unsupported num channels: %d for path: %d\n", - __func__, channel_info->num_channels, param_type); - } v->cvp_state = CMD_STATUS_FAIL; v->async_err = 0; @@ -4110,16 +4110,9 @@ static int voice_send_cvp_mfc_config_v2(struct voice_data *v) mfc_config_info->num_channels = v->dev_rx.no_of_channels; mfc_config_info->bits_per_sample = 16; mfc_config_info->sample_rate = v->dev_rx.sample_rate; - - if (mfc_config_info->num_channels == NUM_CHANNELS_MONO) { - mfc_config_info->channel_type[0] = PCM_CHANNEL_FC; - } else if (mfc_config_info->num_channels == NUM_CHANNELS_STEREO) { - mfc_config_info->channel_type[0] = PCM_CHANNEL_FL; - mfc_config_info->channel_type[1] = PCM_CHANNEL_FR; - } else { - pr_warn("%s: Unsupported num channels: %d\n", - __func__, mfc_config_info->num_channels); - } + memcpy(&mfc_config_info->channel_type, + v->dev_rx.channel_mapping, + VSS_NUM_CHANNELS_MAX * sizeof(uint8_t)); v->cvp_state = CMD_STATUS_FAIL; v->async_err = 0; @@ -8183,6 +8176,96 @@ uint32_t voice_get_topology(uint32_t topology_idx) return topology; } +int voice_set_topology_specific_info(struct voice_data *v, + uint32_t topology_idx) +{ + struct cal_block_data *cal_block = NULL; + int ret = 0; + uint32_t topo_channels; + + if (common.cal_data[topology_idx] == NULL) { + pr_err("%s: cal type is NULL for cal index %x\n", + __func__, topology_idx); + ret = -EINVAL; + 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); + ret = -EINVAL; + goto unlock; + } + + if (topology_idx == CVP_VOC_RX_TOPOLOGY_CAL) { + topo_channels = ((struct audio_cal_info_voc_top *) + cal_block->cal_info)->num_channels; + if (topo_channels > 0) { + v->dev_rx.no_of_channels = topo_channels; + pr_debug("%s: Topology Rx no of channels: %d", + __func__, v->dev_rx.no_of_channels); + memcpy(&v->dev_rx.channel_mapping, + &((struct audio_cal_info_voc_top *) + cal_block->cal_info)->channel_mapping, + VSS_CHANNEL_MAPPING_SIZE); + } else { + pr_debug("%s: cal data is zero, default to Rx backend config\n", + __func__); + if (v->dev_rx.no_of_channels == NUM_CHANNELS_MONO) { + v->dev_rx.channel_mapping[0] = PCM_CHANNEL_FC; + } else if (v->dev_rx.no_of_channels == + NUM_CHANNELS_STEREO) { + v->dev_rx.channel_mapping[0] = PCM_CHANNEL_FL; + v->dev_rx.channel_mapping[1] = PCM_CHANNEL_FR; + } else { + pr_warn("%s: Unsupported Rx num channels: %d\n", + __func__, v->dev_rx.no_of_channels); + } + } + } else if (topology_idx == CVP_VOC_TX_TOPOLOGY_CAL) { + topo_channels = ((struct audio_cal_info_voc_top *) + cal_block->cal_info)->num_channels; + if (topo_channels > 0) { + v->dev_tx.no_of_channels = topo_channels; + pr_debug("%s: Topology Tx no of channels: %d", + __func__, v->dev_tx.no_of_channels); + memcpy(&v->dev_tx.channel_mapping, + &((struct audio_cal_info_voc_top *) + cal_block->cal_info)->channel_mapping, + VSS_CHANNEL_MAPPING_SIZE); + } else { + pr_debug("%s: cal data is zero, default to Tx backend config\n", + __func__); + if (v->dev_tx.no_of_channels == NUM_CHANNELS_MONO) { + v->dev_tx.channel_mapping[0] = PCM_CHANNEL_FC; + } else if (v->dev_tx.no_of_channels == + NUM_CHANNELS_STEREO) { + v->dev_tx.channel_mapping[0] = PCM_CHANNEL_FL; + v->dev_tx.channel_mapping[1] = PCM_CHANNEL_FR; + } else if (v->dev_tx.no_of_channels == + NUM_CHANNELS_QUAD) { + v->dev_tx.channel_mapping[0] = PCM_CHANNEL_FL; + v->dev_tx.channel_mapping[1] = PCM_CHANNEL_FR; + v->dev_tx.channel_mapping[2] = PCM_CHANNEL_LS; + v->dev_tx.channel_mapping[3] = PCM_CHANNEL_RS; + } else { + pr_warn("%s: Unsupported Tx num channels: %d\n", + __func__, v->dev_tx.no_of_channels); + } + } + } else { + pr_err("%s: topology index %x is invalid\n", + __func__, topology_idx); + } +unlock: + mutex_unlock(&common.cal_data[topology_idx]->lock); +done: + return ret; +} + static int get_cal_type_index(int32_t cal_type) { int ret = -EINVAL; diff --git a/include/dsp/q6voice.h b/include/dsp/q6voice.h index af7995a01cd9..a41a2db8f868 100644 --- a/include/dsp/q6voice.h +++ b/include/dsp/q6voice.h @@ -2063,6 +2063,8 @@ int voc_disable_topology(uint32_t session_id, uint32_t disable); int voc_set_device_config(uint32_t session_id, uint8_t path_dir, struct media_format_info *finfo); uint32_t voice_get_topology(uint32_t topology_idx); +int voice_set_topology_specific_info(struct voice_data *v, + 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); diff --git a/include/uapi/linux/msm_audio_calibration.h b/include/uapi/linux/msm_audio_calibration.h index 5a0b8603070d..1696ae5e9118 100644 --- a/include/uapi/linux/msm_audio_calibration.h +++ b/include/uapi/linux/msm_audio_calibration.h @@ -107,6 +107,8 @@ enum { #define AFE_SIDETONE_IIR_CAL_TYPE AFE_SIDETONE_IIR_CAL_TYPE +#define TOPOLOGY_SPECIFIC_CHANNEL_INFO + enum { VERSION_0_0, }; @@ -376,9 +378,15 @@ struct audio_cal_info_lsm { int32_t app_type; }; +#define VSS_NUM_CHANNELS_MAX 8 + struct audio_cal_info_voc_top { int32_t topology; int32_t acdb_id; +#ifdef TOPOLOGY_SPECIFIC_CHANNEL_INFO + uint32_t num_channels; + uint8_t channel_mapping[VSS_NUM_CHANNELS_MAX]; +#endif }; struct audio_cal_info_vocproc { From fb90eb6176ec661b489b75859b439bafe8644b04 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Tue, 10 Oct 2017 12:26:08 -0700 Subject: [PATCH 063/276] dsp: fix reset logic during ADSP SSR for lsm During ADSP SSR, unless all APR clients are de-registered, Glink channel will not be closed, which will cause Glink channel to remain in stale/unusable state after ADSP SSR. Fix this by adding APR reset logic in LSM driver. Change-Id: Idec26836cfa4ecbacc557996339facd84ac19668 Signed-off-by: Xiaoyu Ye --- dsp/q6lsm.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c index adc541ae0f37..848f2cd93868 100644 --- a/dsp/q6lsm.c +++ b/dsp/q6lsm.c @@ -148,6 +148,10 @@ static int q6lsm_callback(struct apr_client_data *data, void *priv) __func__, data->opcode, data->reset_event, data->reset_proc); + apr_reset(client->apr); + client->apr = NULL; + atomic_set(&client->cmd_state, CMD_STATE_CLEARED); + wake_up(&client->cmd_wait); 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); @@ -368,6 +372,11 @@ static int q6lsm_apr_send_pkt(struct lsm_client *client, void *handle, unsigned long flags = 0; struct apr_hdr *msg_hdr = (struct apr_hdr *) data; + if (!handle) { + pr_err("%s: handle is NULL\n", __func__); + return -EINVAL; + } + pr_debug("%s: enter wait %d\n", __func__, wait); if (wait) mutex_lock(&lsm_common.apr_lock); @@ -1341,6 +1350,10 @@ static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv) 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); + + apr_reset(lsm_common.apr); + lsm_common.apr = NULL; + atomic_set(&lsm_common.apr_users, 0); lsm_common.common_client[sid].lsm_cal_phy_addr = 0; cal_utils_clear_cal_block_q6maps(LSM_MAX_CAL_IDX, lsm_common.cal_data); From 091ff790319eff74b8cf64b8ec94d263a6f24fd1 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Wed, 11 Oct 2017 18:35:07 +0530 Subject: [PATCH 064/276] asoc: sdm660: update cpu_dai_name for quin_tdm Update cpu_dai_name of quin_tdm from tdm group id to port id for sdm670 internal and external codec machine driver to fix soundcard registration issue. Change-Id: I2f7ed4ea983c1d457c4d2940c33f5bb7440ddee6 Signed-off-by: Rohit kumar --- asoc/sdm660-ext-dai-links.c | 4 ++-- asoc/sdm660-internal.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c index 4777a5ff3ee1..a33113550f4c 100644 --- a/asoc/sdm660-ext-dai-links.c +++ b/asoc/sdm660-ext-dai-links.c @@ -1573,7 +1573,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { { .name = LPASS_BE_QUIN_TDM_RX_0, .stream_name = "Quinary TDM0 Playback", - .cpu_dai_name = "msm-dai-q6-tdm.37184", + .cpu_dai_name = "msm-dai-q6-tdm.36928", .platform_name = "msm-pcm-routing", .codec_name = "msm-stub-codec.1", .codec_dai_name = "msm-stub-rx", @@ -1587,7 +1587,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { { .name = LPASS_BE_QUIN_TDM_TX_0, .stream_name = "Quinary TDM0 Capture", - .cpu_dai_name = "msm-dai-q6-tdm.37185", + .cpu_dai_name = "msm-dai-q6-tdm.36929", .platform_name = "msm-pcm-routing", .codec_name = "msm-stub-codec.1", .codec_dai_name = "msm-stub-tx", diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c index 713da55cb0a9..a1536fefe635 100644 --- a/asoc/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -2612,7 +2612,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { { .name = LPASS_BE_QUIN_TDM_RX_0, .stream_name = "Quinary TDM0 Playback", - .cpu_dai_name = "msm-dai-q6-tdm.37184", + .cpu_dai_name = "msm-dai-q6-tdm.36928", .platform_name = "msm-pcm-routing", .codec_name = "msm-stub-codec.1", .codec_dai_name = "msm-stub-rx", @@ -2626,7 +2626,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { { .name = LPASS_BE_QUIN_TDM_TX_0, .stream_name = "Quinary TDM0 Capture", - .cpu_dai_name = "msm-dai-q6-tdm.37185", + .cpu_dai_name = "msm-dai-q6-tdm.36929", .platform_name = "msm-pcm-routing", .codec_name = "msm-stub-codec.1", .codec_dai_name = "msm-stub-tx", From 5e7ef9c9228aa09223f0189b5a80f373149da0ef Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 16 Oct 2017 19:40:38 -0700 Subject: [PATCH 065/276] ASoC: add support to configure clock frequency for all TDM interfaces Enable all five TDM interfaces to configure clock frequency during the runtime. Change-Id: I066ea2034509dee2350205ee887dd3eeb6f5389b Signed-off-by: Xiaoyu Ye --- asoc/msm-dai-q6-v2.c | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 2802a4fa170a..dd7aebc4f844 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -6535,26 +6535,12 @@ static int msm_dai_q6_tdm_set_sysclk(struct snd_soc_dai *dai, struct msm_dai_q6_tdm_dai_data *dai_data = dev_get_drvdata(dai->dev); - switch (dai->id) { - 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_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 ((dai->id >= AFE_PORT_ID_PRIMARY_TDM_RX) && + (dai->id <= AFE_PORT_ID_QUINARY_TDM_TX_7)) { dai_data->clk_set.clk_freq_in_hz = freq; - break; - default: + } else { + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); return -EINVAL; } From f74efd8eb1c494d3fa00e6a529357c3fb8099206 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 27 Sep 2017 11:24:55 +0800 Subject: [PATCH 066/276] audio-lnx: Add support to build sdm670 machine driver as module When CONFIG_SND_SOC_EXT_CODEC/CONFIG_SND_SOC_INT_CODEC is set to m not y, "ifdef CONFIG_SND_SOC_EXT_CODEC/CONFIG_SND_SOC_INT_CODEC" would be false which would cause init functions as dummy functions. Change to "if IS_ENABLED" and When CONFIG_SND_SOC_EXT_CODEC/ CONFIG_SND_SOC_INT_CODEC is set to y/m, both would set the condition to true. Change Makefile to compile machine driver for internal/external codec as one module. Change-Id: Ib72f95a24e8a4e657a1e9efc655f92a397b44d32 Signed-off-by: Meng Wang --- asoc/Makefile | 16 +++------------- asoc/sdm660-external.h | 2 +- asoc/sdm660-internal.h | 4 ++-- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/asoc/Makefile b/asoc/Makefile index cc28148dcf3e..4d3839de9556 100644 --- a/asoc/Makefile +++ b/asoc/Makefile @@ -15,19 +15,9 @@ obj-$(CONFIG_SND_SOC_CPE) += snd-soc-cpe.o snd-soc-msm8998-objs := msm8998.o obj-$(CONFIG_SND_SOC_MACHINE_MSM8998) += snd-soc-msm8998.o -# for SDM660 sound card driver -snd-soc-sdm660-common-objs := sdm660-common.o -obj-$(CONFIG_SND_SOC_SDM670) += snd-soc-sdm660-common.o - -# for SDM660 sound card driver -snd-soc-int-codec-objs := sdm660-internal.o -obj-$(CONFIG_SND_SOC_INT_CODEC) += snd-soc-sdm660-common.o -obj-$(CONFIG_SND_SOC_INT_CODEC) += snd-soc-int-codec.o - -# for SDM660 sound card driver -snd-soc-ext-codec-objs := sdm660-external.o sdm660-ext-dai-links.o -obj-$(CONFIG_SND_SOC_EXT_CODEC) += snd-soc-sdm660-common.o -obj-$(CONFIG_SND_SOC_EXT_CODEC) += snd-soc-ext-codec.o +# for SDM670 sound card driver +snd-soc-sdm670-objs := sdm660-common.o sdm660-internal.o sdm660-external.o sdm660-ext-dai-links.o +obj-$(CONFIG_SND_SOC_SDM670) += snd-soc-sdm670.o # for SDM845 sound card driver snd-soc-sdm845-objs := sdm845.o diff --git a/asoc/sdm660-external.h b/asoc/sdm660-external.h index 0648c014b6d2..b81e158be5f7 100644 --- a/asoc/sdm660-external.h +++ b/asoc/sdm660-external.h @@ -32,7 +32,7 @@ int msm_ext_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); int msm_snd_card_tavil_late_probe(struct snd_soc_card *card); int msm_snd_card_tasha_late_probe(struct snd_soc_card *card); -#ifdef CONFIG_SND_SOC_EXT_CODEC +#if IS_ENABLED(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(struct platform_device *pdev); diff --git a/asoc/sdm660-internal.h b/asoc/sdm660-internal.h index ccc62b8f33dc..6918231b8b74 100644 --- a/asoc/sdm660-internal.h +++ b/asoc/sdm660-internal.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, The Linux Foundation. All rights reserved. +/* 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 @@ -15,7 +15,7 @@ #include -#ifdef CONFIG_SND_SOC_INT_CODEC +#if IS_ENABLED(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, From 3b19449547a5010304c8f01a0e7d949f2c981d47 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 27 Sep 2017 12:20:22 +0800 Subject: [PATCH 067/276] audio-lnx: asoc: codecs: Fix rmmod issues with wcd9335 Remove any child devices associated with this codec driver so that rmmod and insmod again will run without issues. Clear MBHC setting during tasha_remove. Change-Id: Icad9b2ad21601525c68c5d151cd664aa6060e911 Signed-off-by: Meng Wang --- asoc/codecs/wcd9335.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/wcd9335.c b/asoc/codecs/wcd9335.c index 19877b96b0ed..8ade82b7bb21 100644 --- a/asoc/codecs/wcd9335.c +++ b/asoc/codecs/wcd9335.c @@ -87,6 +87,8 @@ #define TASHA_NUM_INTERPOLATORS 9 #define TASHA_NUM_DECIMATORS 9 +#define WCD9335_CHILD_DEVICES_MAX 6 + #define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE)) #define TASHA_MAD_AUDIO_FIRMWARE_PATH "wcd9335/wcd9335_mad_audio.bin" #define TASHA_CPE_SS_ERR_STATUS_MEM_ACCESS (1 << 0) @@ -829,6 +831,10 @@ struct tasha_priv { u32 ref_count; /* Lock to protect mclk enablement */ struct mutex mclk_lock; + + struct platform_device *pdev_child_devices + [WCD9335_CHILD_DEVICES_MAX]; + int child_count; }; static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec, @@ -13576,11 +13582,14 @@ static int tasha_codec_remove(struct snd_soc_codec *codec) struct wcd9xxx *control; control = dev_get_drvdata(codec->dev->parent); + control->num_rx_port = 0; + control->num_tx_port = 0; control->rx_chs = NULL; control->tx_chs = NULL; tasha_cleanup_irqs(tasha); /* Cleanup MBHC */ + wcd_mbhc_deinit(&tasha->mbhc); /* Cleanup resmgr */ return 0; @@ -13920,6 +13929,7 @@ static void tasha_add_child_devices(struct work_struct *work) } platdata = &tasha->swr_plat_data; + tasha->child_count = 0; for_each_child_of_node(wcd9xxx->dev->of_node, node) { if (!strcmp(node->name, "swr_master")) @@ -13980,6 +13990,11 @@ static void tasha_add_child_devices(struct work_struct *work) tasha->nr = ctrl_num; tasha->swr_ctrl_data = swr_ctrl_data; } + + if (tasha->child_count < WCD9335_CHILD_DEVICES_MAX) + tasha->pdev_child_devices[tasha->child_count++] = pdev; + else + goto err; } return; @@ -14179,17 +14194,25 @@ static int tasha_probe(struct platform_device *pdev) static int tasha_remove(struct platform_device *pdev) { struct tasha_priv *tasha; + int count = 0; tasha = platform_get_drvdata(pdev); + if (!tasha) + return -EINVAL; + + for (count = 0; count < tasha->child_count && + count < WCD9335_CHILD_DEVICES_MAX; count++) + platform_device_unregister(tasha->pdev_child_devices[count]); + mutex_destroy(&tasha->codec_mutex); clk_put(tasha->wcd_ext_clk); if (tasha->wcd_native_clk) clk_put(tasha->wcd_native_clk); mutex_destroy(&tasha->mclk_lock); - devm_kfree(&pdev->dev, tasha); - snd_soc_unregister_codec(&pdev->dev); mutex_destroy(&tasha->sb_clk_gear_lock); + snd_soc_unregister_codec(&pdev->dev); + devm_kfree(&pdev->dev, tasha); return 0; } From 921b95f9a22630ab45a328dc66f310b28e82fb61 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 27 Sep 2017 15:29:03 +0800 Subject: [PATCH 068/276] audio-lnx: soc: pinctrl-lpi: fix rmmod issues Deregister lpi with audio notifier when removing driver to avoid crash when rmmod and insmod again. Change-Id: Ibcaf272e6405ef8403b3ac66af5572050fa1092e Signed-off-by: Meng Wang --- soc/pinctrl-lpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/soc/pinctrl-lpi.c b/soc/pinctrl-lpi.c index 1e7ead817399..836ab49f6893 100644 --- a/soc/pinctrl-lpi.c +++ b/soc/pinctrl-lpi.c @@ -622,6 +622,7 @@ static int lpi_pinctrl_remove(struct platform_device *pdev) { struct lpi_gpio_state *state = platform_get_drvdata(pdev); + audio_notifier_deregister("lpi_tlmm"); gpiochip_remove(&state->chip); return 0; } From e0fe5a1740bd2129329d07128f4b4e4bb3e20201 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Tue, 17 Oct 2017 22:28:08 -0700 Subject: [PATCH 069/276] ASoC: sdm845: update MI2S supported sampling rates Update MI2S supported sampling rates in machine driver to match the CPU DAI driver. Change-Id: If6145e9f18182ab4621ffcd14716039fcc1bef6f Signed-off-by: Xiaoyu Ye --- asoc/sdm845.c | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index 5721c323e0b5..010ce41c9edb 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -434,9 +434,9 @@ 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 char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_11P025", "KHZ_16", + "KHZ_22P05", "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"}; @@ -2206,26 +2206,32 @@ static int mi2s_get_sample_rate_val(int sample_rate) case SAMPLING_RATE_8KHZ: sample_rate_val = 0; break; - case SAMPLING_RATE_16KHZ: + case SAMPLING_RATE_11P025KHZ: sample_rate_val = 1; break; - case SAMPLING_RATE_32KHZ: + case SAMPLING_RATE_16KHZ: sample_rate_val = 2; break; - case SAMPLING_RATE_44P1KHZ: + case SAMPLING_RATE_22P05KHZ: sample_rate_val = 3; break; - case SAMPLING_RATE_48KHZ: + case SAMPLING_RATE_32KHZ: sample_rate_val = 4; break; - case SAMPLING_RATE_96KHZ: + case SAMPLING_RATE_44P1KHZ: sample_rate_val = 5; break; - case SAMPLING_RATE_192KHZ: + case SAMPLING_RATE_48KHZ: sample_rate_val = 6; break; + case SAMPLING_RATE_96KHZ: + sample_rate_val = 7; + break; + case SAMPLING_RATE_192KHZ: + sample_rate_val = 8; + break; default: - sample_rate_val = 4; + sample_rate_val = 6; break; } return sample_rate_val; @@ -2240,21 +2246,27 @@ static int mi2s_get_sample_rate(int value) sample_rate = SAMPLING_RATE_8KHZ; break; case 1: - sample_rate = SAMPLING_RATE_16KHZ; + sample_rate = SAMPLING_RATE_11P025KHZ; break; case 2: - sample_rate = SAMPLING_RATE_32KHZ; + sample_rate = SAMPLING_RATE_16KHZ; break; case 3: - sample_rate = SAMPLING_RATE_44P1KHZ; + sample_rate = SAMPLING_RATE_22P05KHZ; break; case 4: - sample_rate = SAMPLING_RATE_48KHZ; + sample_rate = SAMPLING_RATE_32KHZ; break; case 5: - sample_rate = SAMPLING_RATE_96KHZ; + sample_rate = SAMPLING_RATE_44P1KHZ; break; case 6: + sample_rate = SAMPLING_RATE_48KHZ; + break; + case 7: + sample_rate = SAMPLING_RATE_96KHZ; + break; + case 8: sample_rate = SAMPLING_RATE_192KHZ; break; default: From 04d19310186883d767451d765cc83d9f17d08f79 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Wed, 11 Oct 2017 20:08:44 -0700 Subject: [PATCH 070/276] ASoC: sdm845: update TDM configurations for TX and RX Update TDM configurations for primary, secondary, and tertiary TDM interfaces. Change-Id: I1729ad4d8563063802dfae9128e290d58ad43497 Signed-off-by: Xiaoyu Ye --- asoc/sdm845.c | 277 ++++++++++++-------------------------------------- 1 file changed, 66 insertions(+), 211 deletions(-) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index 5721c323e0b5..72948823db92 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -70,7 +70,6 @@ #define WCN_CDC_SLIM_TX_CH_MAX 3 #define TDM_CHANNEL_MAX 8 -#define TDM_SLOT_OFFSET_MAX 8 #define MSM_HIFI_ON 1 @@ -311,17 +310,6 @@ static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_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 */ -}; /* Default configuration of slimbus channels */ static struct dev_config slim_rx_cfg[] = { @@ -431,8 +419,8 @@ 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"}; + "KHZ_48", "KHZ_176P4", + "KHZ_352P8"}; 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", @@ -1614,23 +1602,14 @@ static int tdm_get_sample_rate(int value) sample_rate = SAMPLING_RATE_32KHZ; break; case 3: - sample_rate = SAMPLING_RATE_44P1KHZ; + sample_rate = SAMPLING_RATE_48KHZ; break; case 4: - sample_rate = SAMPLING_RATE_48KHZ; + sample_rate = SAMPLING_RATE_176P4KHZ; 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; @@ -1668,26 +1647,17 @@ static int tdm_get_sample_rate_val(int sample_rate) case SAMPLING_RATE_32KHZ: sample_rate_val = 2; break; - case SAMPLING_RATE_44P1KHZ: + case SAMPLING_RATE_48KHZ: sample_rate_val = 3; break; - case SAMPLING_RATE_48KHZ: + case SAMPLING_RATE_176P4KHZ: 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; + sample_rate_val = 5; break; default: - sample_rate_val = 4; + sample_rate_val = 3; break; } return sample_rate_val; @@ -4491,15 +4461,46 @@ static int sdm845_tdm_snd_hw_params(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; - int channels, slot_width, slots; + int slot_width = 32; + int channels, slots; unsigned int slot_mask, rate, clk_freq; unsigned int slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; - slot_width = 32; pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + slots = tdm_rx_cfg[TDM_PRI][TDM_0].channels; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + slots = tdm_rx_cfg[TDM_SEC][TDM_0].channels; + break; + case AFE_PORT_ID_TERTIARY_TDM_RX: + slots = tdm_rx_cfg[TDM_TERT][TDM_0].channels; + break; + case AFE_PORT_ID_QUATERNARY_TDM_RX: slots = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + slots = tdm_tx_cfg[TDM_PRI][TDM_0].channels; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + slots = tdm_tx_cfg[TDM_SEC][TDM_0].channels; + break; + case AFE_PORT_ID_TERTIARY_TDM_TX: + slots = tdm_tx_cfg[TDM_TERT][TDM_0].channels; + break; + case AFE_PORT_ID_QUATERNARY_TDM_TX: + slots = tdm_tx_cfg[TDM_QUAT][TDM_0].channels; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /*2 slot config - bits 0 and 1 set for the first two slots */ slot_mask = 0x0000FFFF >> (16-slots); channels = slots; @@ -4523,7 +4524,6 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, goto end; } } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - slots = tdm_tx_cfg[TDM_QUAT][TDM_0].channels; /*2 slot config - bits 0 and 1 set for the first two slots */ slot_mask = 0x0000FFFF >> (16-slots); channels = slots; @@ -4568,14 +4568,19 @@ static int sdm845_tdm_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; struct snd_soc_card *card = rtd->card; struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; - ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); - if (ret) - pr_err("%s: TDM TLMM pinctrl set failed with %d\n", - __func__, ret); + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX)) { + ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } return ret; } @@ -4584,15 +4589,19 @@ static void sdm845_tdm_snd_shutdown(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; struct snd_soc_card *card = rtd->card; struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; - ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); - if (ret) - pr_err("%s: TDM TLMM pinctrl set failed with %d\n", - __func__, ret); - + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX)) { + ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } } static struct snd_soc_ops sdm845_tdm_be_ops = { @@ -4726,157 +4735,6 @@ static struct snd_soc_ops msm_aux_pcm_be_ops = { .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 err; - } - - 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 err; - } - } 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 err; - } - - 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 err; - } - } -err: - return ret; -} - static struct snd_soc_ops msm_be_ops = { .hw_params = msm_snd_hw_params, }; @@ -4889,9 +4747,6 @@ 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[] = { @@ -5670,7 +5525,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .dpcm_playback = 1, .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, .be_hw_params_fixup = msm_be_hw_params_fixup, - .ops = &msm_tdm_be_ops, + .ops = &sdm845_tdm_be_ops, .ignore_suspend = 1, }, { @@ -5684,7 +5539,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .dpcm_capture = 1, .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, .be_hw_params_fixup = msm_be_hw_params_fixup, - .ops = &msm_tdm_be_ops, + .ops = &sdm845_tdm_be_ops, .ignore_suspend = 1, }, { @@ -5698,7 +5553,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .dpcm_playback = 1, .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, .be_hw_params_fixup = msm_be_hw_params_fixup, - .ops = &msm_tdm_be_ops, + .ops = &sdm845_tdm_be_ops, .ignore_suspend = 1, }, { @@ -5712,7 +5567,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .dpcm_capture = 1, .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, .be_hw_params_fixup = msm_be_hw_params_fixup, - .ops = &msm_tdm_be_ops, + .ops = &sdm845_tdm_be_ops, .ignore_suspend = 1, }, { @@ -5726,7 +5581,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .dpcm_playback = 1, .id = MSM_BACKEND_DAI_TERT_TDM_RX_0, .be_hw_params_fixup = msm_be_hw_params_fixup, - .ops = &msm_tdm_be_ops, + .ops = &sdm845_tdm_be_ops, .ignore_suspend = 1, }, { @@ -5740,7 +5595,7 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .dpcm_capture = 1, .id = MSM_BACKEND_DAI_TERT_TDM_TX_0, .be_hw_params_fixup = msm_be_hw_params_fixup, - .ops = &msm_tdm_be_ops, + .ops = &sdm845_tdm_be_ops, .ignore_suspend = 1, }, { From c444ff7b6a6cd3efaa983e51c1c8f9d3735fe384 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 18 Oct 2017 10:52:07 +0800 Subject: [PATCH 071/276] audio-lnx: asoc: sdm660: fix rmmod issue with audio notifier Deregister sound card with audio notifier when removing driver to avoid crash. Change-Id: I4f22fefce519e8dab25901337fdf2e62044090ed Signed-off-by: Meng Wang --- asoc/sdm660-common.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c index e6b82dfda7a2..7142de513e95 100644 --- a/asoc/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "msm-pcm-routing-v2.h" #include "sdm660-common.h" #include "sdm660-internal.h" @@ -3370,6 +3371,10 @@ static int msm_asoc_machine_remove(struct platform_device *pdev) gpio_free(pdata->us_euro_gpio); gpio_free(pdata->hph_en1_gpio); gpio_free(pdata->hph_en0_gpio); + + if (pdata->snd_card_val != INT_SND_CARD) + audio_notifier_deregister("sdm660"); + snd_soc_unregister_card(card); return 0; } From 431f0b1a3a03be7d53851473b841bcf056694fbe Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 18 Oct 2017 10:53:57 +0800 Subject: [PATCH 072/276] audio-lnx: asoc: sdm660: fix rmmod issue Remove msm_free_auxdev_mem to avoid kernel panic. Change-Id: I0a60cc33acf942c33273d86d9ef24dff6bd3a1b6 Signed-off-by: Meng Wang --- asoc/sdm660-common.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c index 7142de513e95..34fcdf0150ac 100644 --- a/asoc/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -3135,20 +3135,6 @@ static int msm_init_wsa_dev(struct platform_device *pdev, 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) { int count; @@ -3367,7 +3353,6 @@ static int msm_asoc_machine_remove(struct platform_device *pdev) 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); From c4ef3b5ecaa4161cb7455dfabb77e28ee6d6f188 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 18 Oct 2017 10:57:15 +0800 Subject: [PATCH 073/276] audio-lnx: asoc: sdm660: fix rmmod issue with gpio_free Check if GPIO is valid before free to avoid crash. Change-Id: Ie052e5ff1776ad57ae9c1d0f00873ba29e11f78f Signed-off-by: Meng Wang --- asoc/sdm660-common.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c index 34fcdf0150ac..1874cfa02860 100644 --- a/asoc/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -3353,9 +3353,18 @@ static int msm_asoc_machine_remove(struct platform_device *pdev) if (pdata->snd_card_val == INT_SND_CARD) mutex_destroy(&pdata->cdc_int_mclk0_mutex); - gpio_free(pdata->us_euro_gpio); - gpio_free(pdata->hph_en1_gpio); - gpio_free(pdata->hph_en0_gpio); + if (gpio_is_valid(pdata->us_euro_gpio)) { + gpio_free(pdata->us_euro_gpio); + pdata->us_euro_gpio = 0; + } + if (gpio_is_valid(pdata->hph_en1_gpio)) { + gpio_free(pdata->hph_en1_gpio); + pdata->hph_en1_gpio = 0; + } + if (gpio_is_valid(pdata->hph_en0_gpio)) { + gpio_free(pdata->hph_en0_gpio); + pdata->hph_en0_gpio = 0; + } if (pdata->snd_card_val != INT_SND_CARD) audio_notifier_deregister("sdm660"); From 3f38876247d008d8475da30317b14b743b3da9d1 Mon Sep 17 00:00:00 2001 From: Walter Yang Date: Wed, 25 Oct 2017 11:50:43 +0800 Subject: [PATCH 074/276] ASoC: wcd934x: reset ASRC after playback Reset ASRC after audio playback is completed to clear the FIFO and avoid any noise being generated. CRs-Fixed: 2129994 Change-Id: Ie45796e1bd68d5a8bd790490a65520358f26b811 Signed-off-by: Walter Yang --- asoc/codecs/wcd934x/wcd934x.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 7fd1e63dd6fe..855ca3d4c3a7 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -2879,7 +2879,7 @@ static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, int asrc_in, int event) { struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); - u16 cfg_reg, ctl_reg, clk_reg, asrc_ctl, mix_ctl_reg; + u16 cfg_reg, ctl_reg, clk_reg, asrc_ctl, mix_ctl_reg, paired_reg; int asrc, ret = 0; u8 main_sr, mix_sr, asrc_mode = 0; @@ -2888,6 +2888,7 @@ static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, cfg_reg = WCD934X_CDC_RX1_RX_PATH_CFG0; ctl_reg = WCD934X_CDC_RX1_RX_PATH_CTL; clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; asrc_ctl = WCD934X_MIXING_ASRC0_CTL1; asrc = ASRC0; break; @@ -2895,6 +2896,7 @@ static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, cfg_reg = WCD934X_CDC_RX3_RX_PATH_CFG0; ctl_reg = WCD934X_CDC_RX3_RX_PATH_CTL; clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; asrc_ctl = WCD934X_MIXING_ASRC0_CTL1; asrc = ASRC0; break; @@ -2902,6 +2904,7 @@ static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, cfg_reg = WCD934X_CDC_RX2_RX_PATH_CFG0; ctl_reg = WCD934X_CDC_RX2_RX_PATH_CTL; clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; asrc_ctl = WCD934X_MIXING_ASRC1_CTL1; asrc = ASRC1; break; @@ -2909,6 +2912,7 @@ static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, cfg_reg = WCD934X_CDC_RX4_RX_PATH_CFG0; ctl_reg = WCD934X_CDC_RX4_RX_PATH_CTL; clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL; asrc_ctl = WCD934X_MIXING_ASRC1_CTL1; asrc = ASRC1; break; @@ -2916,6 +2920,7 @@ static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, cfg_reg = WCD934X_CDC_RX7_RX_PATH_CFG0; ctl_reg = WCD934X_CDC_RX7_RX_PATH_CTL; clk_reg = WCD934X_MIXING_ASRC2_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC3_CLK_RST_CTL; asrc_ctl = WCD934X_MIXING_ASRC2_CTL1; asrc = ASRC2; break; @@ -2923,6 +2928,7 @@ static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, cfg_reg = WCD934X_CDC_RX8_RX_PATH_CFG0; ctl_reg = WCD934X_CDC_RX8_RX_PATH_CTL; clk_reg = WCD934X_MIXING_ASRC3_CLK_RST_CTL; + paired_reg = WCD934X_MIXING_ASRC2_CLK_RST_CTL; asrc_ctl = WCD934X_MIXING_ASRC3_CTL1; asrc = ASRC3; break; @@ -2936,6 +2942,13 @@ static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, switch (event) { case SND_SOC_DAPM_PRE_PMU: if (tavil->asrc_users[asrc] == 0) { + if ((snd_soc_read(codec, clk_reg) & 0x02) || + (snd_soc_read(codec, paired_reg) & 0x02)) { + snd_soc_update_bits(codec, clk_reg, + 0x02, 0x00); + snd_soc_update_bits(codec, paired_reg, + 0x02, 0x00); + } snd_soc_update_bits(codec, cfg_reg, 0x80, 0x80); snd_soc_update_bits(codec, clk_reg, 0x01, 0x01); main_sr = snd_soc_read(codec, ctl_reg) & 0x0F; @@ -2955,7 +2968,7 @@ static int tavil_codec_enable_asrc(struct snd_soc_codec *codec, tavil->asrc_users[asrc] = 0; snd_soc_update_bits(codec, asrc_ctl, 0x07, 0x00); snd_soc_update_bits(codec, cfg_reg, 0x80, 0x00); - snd_soc_update_bits(codec, clk_reg, 0x01, 0x00); + snd_soc_update_bits(codec, clk_reg, 0x03, 0x02); } break; }; From 523325316e7b2a8858dba93fc104c890d84ccc11 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Thu, 12 Oct 2017 08:58:30 +0530 Subject: [PATCH 075/276] ASoC: codecs: sdm660_cdc: Fix mute if compander is disabled Compander disable sequence does not get called after headphone playback on FM. This results in mute at one channel. Compander clock should be disabled at the end to resolve the mute. CRs-Fixed: 2102126 Change-Id: Ia47fb1c393db255b52a965173a769896768f245e Signed-off-by: Vatsal Bucha --- asoc/codecs/sdm660_cdc/msm-digital-cdc.c | 82 ++++++++++++------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c index 3df917f717b5..eeaddf6ed5c8 100644 --- a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -211,60 +211,60 @@ static int msm_dig_cdc_codec_config_compander(struct snd_soc_codec *codec, int interp_n, int event) { struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); + int comp_ch_bits_set = 0x03; dev_dbg(codec->dev, "%s: event %d shift %d, enabled %d\n", __func__, event, interp_n, dig_cdc->comp_enabled[interp_n]); - /* compander is not enabled */ - if (!dig_cdc->comp_enabled[interp_n]) + /* compander is invalid */ + if (dig_cdc->comp_enabled[interp_n] != COMPANDER_1 && + dig_cdc->comp_enabled[interp_n]) { + dev_dbg(codec->dev, "%s: Invalid compander %d\n", __func__, + dig_cdc->comp_enabled[interp_n]); return 0; + } - switch (dig_cdc->comp_enabled[interp_n]) { - case COMPANDER_1: - if (SND_SOC_DAPM_EVENT_ON(event)) { - /* Enable Compander Clock */ - snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x09); - snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x01); - snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_COMP0_B1_CTL, - 1 << interp_n, 1 << interp_n); - snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x01); - snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0x50); - /* add sleep for compander to settle */ - usleep_range(1000, 1100); - snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x28); - snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0xB0); + if (SND_SOC_DAPM_EVENT_ON(event)) { + /* Enable Compander Clock */ + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x09); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x01); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL, + 1 << interp_n, 1 << interp_n); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x01); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0x50); + /* add sleep for compander to settle */ + usleep_range(1000, 1100); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x28); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0xB0); - /* Enable Compander GPIO */ - if (dig_cdc->codec_hph_comp_gpio) - dig_cdc->codec_hph_comp_gpio(1, codec); - } else if (SND_SOC_DAPM_EVENT_OFF(event)) { - /* Disable Compander GPIO */ - if (dig_cdc->codec_hph_comp_gpio) - dig_cdc->codec_hph_comp_gpio(0, codec); + /* Enable Compander GPIO */ + if (dig_cdc->codec_hph_comp_gpio) + dig_cdc->codec_hph_comp_gpio(1, codec); + } else if (SND_SOC_DAPM_EVENT_OFF(event)) { + /* Disable Compander GPIO */ + if (dig_cdc->codec_hph_comp_gpio) + dig_cdc->codec_hph_comp_gpio(0, codec); + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL, + 1 << interp_n, 0); + comp_ch_bits_set = snd_soc_read(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL); + if ((comp_ch_bits_set & 0x03) == 0x00) { snd_soc_update_bits(codec, MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x05); - snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_COMP0_B1_CTL, - 1 << interp_n, 0); - snd_soc_update_bits(codec, + snd_soc_update_bits(codec, MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x00); } - break; - default: - dev_dbg(codec->dev, "%s: Invalid compander %d\n", __func__, - dig_cdc->comp_enabled[interp_n]); - break; - }; - + } return 0; } From f5ebc68d4cba3c1019cb0f02465c102b878444fc Mon Sep 17 00:00:00 2001 From: Banajit Goswami Date: Fri, 20 Oct 2017 22:29:42 -0700 Subject: [PATCH 076/276] asoc: wcd9xxx-core: avoid slimbus read/write during ADSP SSR This reverts commit 40bc50d768038c04f8c1 ("mfd: wcd9xxx-core: Synchronize codec down and reset calls from slimbus."). This is to make sure that the dev_up flag is set right after device_down is called from Slimbus after ADSP SSR. This flag helps make sure that all read/write calls over slimbus are blocked until ADSP and Slimbus are fully up post-SSR. Change-Id: I5595a23a0b51f2ca392ddfb5773b057204185b54 Signed-off-by: Banajit Goswami --- asoc/codecs/core.h | 2 +- asoc/codecs/wcd9xxx-core.c | 49 +++++++++++++------------------------- 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/asoc/codecs/core.h b/asoc/codecs/core.h index b994010f5222..b4c1be40ff31 100644 --- a/asoc/codecs/core.h +++ b/asoc/codecs/core.h @@ -350,7 +350,7 @@ struct wcd9xxx { int (*post_reset)(struct wcd9xxx *wcd9xxx); void *ssr_priv; - unsigned long dev_up; + bool dev_up; u32 num_of_supplies; struct regulator_bulk_data *supplies; diff --git a/asoc/codecs/wcd9xxx-core.c b/asoc/codecs/wcd9xxx-core.c index 7f74aef8f0c0..49be43a3e12f 100644 --- a/asoc/codecs/wcd9xxx-core.c +++ b/asoc/codecs/wcd9xxx-core.c @@ -228,7 +228,7 @@ static int wcd9xxx_slim_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg, if (!wcd9xxx->dev_up) { dev_dbg_ratelimited( - wcd9xxx->dev, "%s: No read allowed. dev_up = %lu\n", + wcd9xxx->dev, "%s: No read allowed. dev_up = %d\n", __func__, wcd9xxx->dev_up); return 0; } @@ -268,7 +268,7 @@ static int wcd9xxx_slim_write_device(struct wcd9xxx *wcd9xxx, if (!wcd9xxx->dev_up) { dev_dbg_ratelimited( - wcd9xxx->dev, "%s: No write allowed. dev_up = %lu\n", + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", __func__, wcd9xxx->dev_up); return 0; } @@ -345,7 +345,7 @@ int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg, if (!wcd9xxx->dev_up) { dev_dbg_ratelimited( - wcd9xxx->dev, "%s: No write allowed. dev_up = %lu\n", + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", __func__, wcd9xxx->dev_up); ret = 0; goto done; @@ -426,7 +426,7 @@ int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx, if (!wcd9xxx->dev_up) { dev_dbg_ratelimited( - wcd9xxx->dev, "%s: No write allowed. dev_up = %lu\n", + wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n", __func__, wcd9xxx->dev_up); return 0; } @@ -1484,27 +1484,12 @@ static int wcd9xxx_slim_device_reset(struct slim_device *sldev) return -EINVAL; } - /* - * Wait for 500 ms for device down to complete. Observed delay - * of ~200ms for device down to complete after being called, - * due to context switch issue. - */ - ret = wait_on_bit_timeout(&wcd9xxx->dev_up, 0, - TASK_INTERRUPTIBLE, - msecs_to_jiffies(500)); - if (ret) - pr_err("%s: slim device down not complete in 500 msec\n", - __func__); - - mutex_lock(&wcd9xxx->reset_lock); - - dev_info(wcd9xxx->dev, "%s: device reset, dev_up = %lu\n", - __func__, wcd9xxx->dev_up); - if (wcd9xxx->dev_up) { - mutex_unlock(&wcd9xxx->reset_lock); + dev_info(wcd9xxx->dev, "%s: device reset, dev_up = %d\n", + __func__, wcd9xxx->dev_up); + if (wcd9xxx->dev_up) return 0; - } + mutex_lock(&wcd9xxx->reset_lock); ret = wcd9xxx_reset(wcd9xxx->dev); if (ret) dev_err(wcd9xxx->dev, "%s: Resetting Codec failed\n", __func__); @@ -1522,8 +1507,8 @@ static int wcd9xxx_slim_device_up(struct slim_device *sldev) pr_err("%s: wcd9xxx is NULL\n", __func__); return -EINVAL; } - dev_info(wcd9xxx->dev, "%s: slim device up, dev_up = %lu\n", - __func__, wcd9xxx->dev_up); + dev_info(wcd9xxx->dev, "%s: slim device up, dev_up = %d\n", + __func__, wcd9xxx->dev_up); if (wcd9xxx->dev_up) return 0; @@ -1545,20 +1530,18 @@ static int wcd9xxx_slim_device_down(struct slim_device *sldev) return -EINVAL; } - mutex_lock(&wcd9xxx->reset_lock); - - dev_info(wcd9xxx->dev, "%s: device down, dev_up = %lu\n", - __func__, wcd9xxx->dev_up); - if (!wcd9xxx->dev_up) { - mutex_unlock(&wcd9xxx->reset_lock); + dev_info(wcd9xxx->dev, "%s: device down, dev_up = %d\n", + __func__, wcd9xxx->dev_up); + if (!wcd9xxx->dev_up) return 0; - } + wcd9xxx->dev_up = false; + + mutex_lock(&wcd9xxx->reset_lock); if (wcd9xxx->dev_down) wcd9xxx->dev_down(wcd9xxx); wcd9xxx_irq_exit(&wcd9xxx->core_res); wcd9xxx_reset_low(wcd9xxx->dev); - wcd9xxx->dev_up = false; mutex_unlock(&wcd9xxx->reset_lock); return 0; From d0ac8ed9fa46cfddc008e3c6b6313cc9f60401da Mon Sep 17 00:00:00 2001 From: Banajit Goswami Date: Sat, 21 Oct 2017 01:12:47 -0700 Subject: [PATCH 077/276] asoc: wcd934x: set bus down flag right when notification reaches Set bus_down_in_recovery flag as soon as possible when BUS down notification reaches codec driver. This is to avoid any unwanted slimbus transaction while BUS is still not recovered. Change-Id: I77917975372bf51e6aa881cbfc63c479478db375 Signed-off-by: Banajit Goswami --- asoc/codecs/wcd934x/wcd934x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 7fd1e63dd6fe..522e56aae72f 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -9131,13 +9131,13 @@ static int tavil_device_down(struct wcd9xxx *wcd9xxx) codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv); priv = snd_soc_codec_get_drvdata(codec); + for (count = 0; count < NUM_CODEC_DAIS; count++) + priv->dai[count].bus_down_in_recovery = true; if (priv->swr.ctrl_data) swrm_wcd_notify(priv->swr.ctrl_data[0].swr_pdev, SWR_DEVICE_DOWN, NULL); tavil_dsd_reset(priv->dsd_config); snd_soc_card_change_online_state(codec->component.card, 0); - for (count = 0; count < NUM_CODEC_DAIS; count++) - priv->dai[count].bus_down_in_recovery = true; wcd_dsp_ssr_event(priv->wdsp_cntl, WCD_CDC_DOWN_EVENT); wcd_resmgr_set_sido_input_src_locked(priv->resmgr, SIDO_SOURCE_INTERNAL); From 270bf76431b011e996a9653ef4d0524f64cf8a31 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Fri, 22 Sep 2017 16:50:24 +0800 Subject: [PATCH 078/276] ASoC: codecs: add mixer ctls to control boost level Add new mixer ctls to control max boost state level for speaker protection V3. Change-Id: If967de46955f6dc8692986503daea68ce105f642 Signed-off-by: Xiaojun Sang --- asoc/codecs/msm_sdw/msm_sdw_cdc.c | 75 ++++++++++++++++++++++++++++++ asoc/codecs/wcd9335.c | 76 +++++++++++++++++++++++++++++++ asoc/codecs/wcd934x/wcd934x.c | 72 +++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+) diff --git a/asoc/codecs/msm_sdw/msm_sdw_cdc.c b/asoc/codecs/msm_sdw/msm_sdw_cdc.c index 05d1d801ce74..5649167ee6bb 100644 --- a/asoc/codecs/msm_sdw/msm_sdw_cdc.c +++ b/asoc/codecs/msm_sdw/msm_sdw_cdc.c @@ -934,6 +934,66 @@ static int msm_sdw_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, return 0; } +static int msm_sdw_spkr_left_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, MSM_SDW_BOOST0_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_sdw_spkr_left_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, MSM_SDW_BOOST0_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int msm_sdw_spkr_right_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, MSM_SDW_BOOST1_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int msm_sdw_spkr_right_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, MSM_SDW_BOOST1_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + static int msm_sdw_vi_feed_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1465,8 +1525,15 @@ static const char * const msm_sdw_ear_spkr_pa_gain_text[] = { "G_4_DB", "G_5_DB", "G_6_DB" }; +static const char * const msm_sdw_speaker_boost_stage_text[] = { + "NO_MAX_STATE", "MAX_STATE_1", "MAX_STATE_2" +}; + static SOC_ENUM_SINGLE_EXT_DECL(msm_sdw_ear_spkr_pa_gain_enum, msm_sdw_ear_spkr_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(msm_sdw_spkr_boost_stage_enum, + msm_sdw_speaker_boost_stage_text); + /* RX4 MIX1 */ static const struct soc_enum rx4_mix1_inp1_chain_enum = SOC_ENUM_SINGLE(MSM_SDW_TOP_RX7_PATH_INPUT0_MUX, @@ -1568,6 +1635,14 @@ static const struct snd_kcontrol_new msm_sdw_snd_controls[] = { SOC_ENUM_EXT("EAR SPKR PA Gain", msm_sdw_ear_spkr_pa_gain_enum, msm_sdw_ear_spkr_pa_gain_get, msm_sdw_ear_spkr_pa_gain_put), + SOC_ENUM_EXT("SPKR Left Boost Max State", + msm_sdw_spkr_boost_stage_enum, + msm_sdw_spkr_left_boost_stage_get, + msm_sdw_spkr_left_boost_stage_put), + SOC_ENUM_EXT("SPKR Right Boost Max State", + msm_sdw_spkr_boost_stage_enum, + msm_sdw_spkr_right_boost_stage_get, + msm_sdw_spkr_right_boost_stage_put), SOC_SINGLE_SX_TLV("RX4 Digital Volume", MSM_SDW_RX7_RX_VOL_CTL, 0, -84, 40, digital_gain), SOC_SINGLE_SX_TLV("RX5 Digital Volume", MSM_SDW_RX8_RX_VOL_CTL, diff --git a/asoc/codecs/wcd9335.c b/asoc/codecs/wcd9335.c index 8ade82b7bb21..a4a838f9a6b9 100644 --- a/asoc/codecs/wcd9335.c +++ b/asoc/codecs/wcd9335.c @@ -8659,6 +8659,66 @@ static int tasha_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, return 0; } +static int tasha_spkr_left_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, WCD9335_CDC_BOOST0_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tasha_spkr_left_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, WCD9335_CDC_BOOST0_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int tasha_spkr_right_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, WCD9335_CDC_BOOST1_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tasha_spkr_right_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, WCD9335_CDC_BOOST1_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + static int tasha_config_compander(struct snd_soc_codec *codec, int interp_n, int event) { @@ -8953,6 +9013,10 @@ static const char * const tasha_ear_spkr_pa_gain_text[] = { "G_5_DB", "G_6_DB" }; +static const char * const tasha_speaker_boost_stage_text[] = { + "NO_MAX_STATE", "MAX_STATE_1", "MAX_STATE_2" +}; + static const struct soc_enum tasha_ear_pa_gain_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_pa_gain_text), tasha_ear_pa_gain_text); @@ -8961,6 +9025,10 @@ static const struct soc_enum tasha_ear_spkr_pa_gain_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_spkr_pa_gain_text), tasha_ear_spkr_pa_gain_text); +static const struct soc_enum tasha_spkr_boost_stage_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_speaker_boost_stage_text), + tasha_speaker_boost_stage_text); + static const struct snd_kcontrol_new tasha_analog_gain_controls[] = { SOC_ENUM_EXT("EAR PA Gain", tasha_ear_pa_gain_enum, tasha_ear_pa_gain_get, tasha_ear_pa_gain_put), @@ -8968,6 +9036,14 @@ static const struct snd_kcontrol_new tasha_analog_gain_controls[] = { SOC_ENUM_EXT("EAR SPKR PA Gain", tasha_ear_spkr_pa_gain_enum, tasha_ear_spkr_pa_gain_get, tasha_ear_spkr_pa_gain_put), + SOC_ENUM_EXT("SPKR Left Boost Max State", tasha_spkr_boost_stage_enum, + tasha_spkr_left_boost_stage_get, + tasha_spkr_left_boost_stage_put), + + SOC_ENUM_EXT("SPKR Right Boost Max State", tasha_spkr_boost_stage_enum, + tasha_spkr_right_boost_stage_get, + tasha_spkr_right_boost_stage_put), + SOC_SINGLE_TLV("HPHL Volume", WCD9335_HPH_L_EN, 0, 20, 1, line_gain), SOC_SINGLE_TLV("HPHR Volume", WCD9335_HPH_R_EN, 0, 20, 1, diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 7fd1e63dd6fe..d6eb22d57951 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -5577,6 +5577,66 @@ static int tavil_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol, return 0; } +static int tavil_spkr_left_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, WCD934X_CDC_BOOST0_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tavil_spkr_left_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, WCD934X_CDC_BOOST0_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + +static int tavil_spkr_right_boost_stage_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max = 0; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + bst_state_max = snd_soc_read(codec, WCD934X_CDC_BOOST1_BOOST_CTL); + bst_state_max = (bst_state_max & 0x0c) >> 2; + ucontrol->value.integer.value[0] = bst_state_max; + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + return 0; +} + +static int tavil_spkr_right_boost_stage_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + u8 bst_state_max; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + bst_state_max = ucontrol->value.integer.value[0] << 2; + snd_soc_update_bits(codec, WCD934X_CDC_BOOST1_BOOST_CTL, + 0x0c, bst_state_max); + + return 0; +} + static int tavil_rx_hph_mode_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -5655,9 +5715,15 @@ static const char * const tavil_ear_spkr_pa_gain_text[] = { "G_4_DB", "G_5_DB", "G_6_DB" }; +static const char * const tavil_speaker_boost_stage_text[] = { + "NO_MAX_STATE", "MAX_STATE_1", "MAX_STATE_2" +}; + static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_pa_gain_enum, tavil_ear_pa_gain_text); static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_spkr_pa_gain_enum, tavil_ear_spkr_pa_gain_text); +static SOC_ENUM_SINGLE_EXT_DECL(tavil_spkr_boost_stage_enum, + tavil_speaker_boost_stage_text); static SOC_ENUM_SINGLE_EXT_DECL(amic_pwr_lvl_enum, amic_pwr_lvl_text); static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text); static SOC_ENUM_SINGLE_EXT_DECL(asrc_mode_enum, asrc_mode_text); @@ -5713,6 +5779,12 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = { tavil_ear_pa_gain_get, tavil_ear_pa_gain_put), SOC_ENUM_EXT("EAR SPKR PA Gain", tavil_ear_spkr_pa_gain_enum, tavil_ear_spkr_pa_gain_get, tavil_ear_spkr_pa_gain_put), + SOC_ENUM_EXT("SPKR Left Boost Max State", tavil_spkr_boost_stage_enum, + tavil_spkr_left_boost_stage_get, + tavil_spkr_left_boost_stage_put), + SOC_ENUM_EXT("SPKR Right Boost Max State", tavil_spkr_boost_stage_enum, + tavil_spkr_right_boost_stage_get, + tavil_spkr_right_boost_stage_put), SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 20, 1, line_gain), SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 20, 1, line_gain), SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER, From ac83456508c603eafe95158e44185b7f4286a5d8 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Tue, 3 Oct 2017 18:26:08 -0700 Subject: [PATCH 079/276] ASoC: sdxpoorwills: enable audio compilation Enable compilation of audio drivers for SDXPOORWILLS. Change-Id: I2a8d949f98ab6fb56111dffdab51d9123a41c3c1 Signed-off-by: Xiaoyu Ye --- Makefile | 9 +++++++- config/sdxpoorwillsauto.conf | 29 ++++++++++++++++++++++++ config/sdxpoorwillsautoconf.h | 42 +++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 config/sdxpoorwillsauto.conf create mode 100644 config/sdxpoorwillsautoconf.h diff --git a/Makefile b/Makefile index ff83693730d3..de65a18f0478 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,10 @@ ifeq ($(CONFIG_ARCH_SDM670), y) include $(srctree)/techpack/audio/config/sdm670auto.conf export endif +ifeq ($(CONFIG_ARCH_SDXPOORWILLS), y) +include $(srctree)/techpack/audio/config/sdxpoorwillsauto.conf +export +endif # Use USERINCLUDE when you must reference the UAPI directories only. USERINCLUDE += \ @@ -26,7 +30,10 @@ ifeq ($(CONFIG_ARCH_SDM670), y) LINUXINCLUDE += \ -include $(srctree)/techpack/audio/config/sdm670autoconf.h endif - +ifeq ($(CONFIG_ARCH_SDXPOORWILLS), y) +LINUXINCLUDE += \ + -include $(srctree)/techpack/audio/config/sdxpoorwillsautoconf.h +endif obj-y += asoc/ obj-y += dsp/ diff --git a/config/sdxpoorwillsauto.conf b/config/sdxpoorwillsauto.conf new file mode 100644 index 000000000000..d9dd081e876a --- /dev/null +++ b/config/sdxpoorwillsauto.conf @@ -0,0 +1,29 @@ +CONFIG_PINCTRL_WCD=y +CONFIG_SND_SOC_WCD934X=y +CONFIG_AUDIO_EXT_CLK=y +CONFIG_SND_SOC_WCD9XXX_V2=y +CONFIG_SND_SOC_WCD_MBHC=y +CONFIG_SND_SOC_WSA881X=y +CONFIG_SND_SOC_WCD_DSP_MGR=y +CONFIG_SND_SOC_WCD934X=y +CONFIG_SND_SOC_WCD934X_MBHC=y +CONFIG_SND_SOC_WCD934X_DSD=y +CONFIG_MSM_QDSP6V2_CODECS=y +CONFIG_MSM_QDSP6_APRV3_GLINK=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_MSM_ADSP_LOADER=y +CONFIG_REGMAP_SWR=y +CONFIG_SND_SOC_MSM_HOSTLESS_PCM=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_SND_SOC_POORWILLS=y +CONFIG_SOUNDWIRE=y +CONFIG_SOUNDWIRE_WCD_CTRL=y +CONFIG_SND_SOC_QDSP6V2=y +CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y +CONFIG_MSM_CDC_PINCTRL=y +CONFIG_WCD9XXX_CODEC_CORE=y +CONFIG_SND_SOC_WCD_MBHC_ADC=y +CONFIG_QTI_PP=y +CONFIG_SND_HWDEP=y +CONFIG_SND_SOC_MACHINE_SDXPOORWILLS=y +CONFIG_SND_SOC_MSM_STUB=y diff --git a/config/sdxpoorwillsautoconf.h b/config/sdxpoorwillsautoconf.h new file mode 100644 index 000000000000..9fc6258fe1b3 --- /dev/null +++ b/config/sdxpoorwillsautoconf.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 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. + */ + +#define CONFIG_PINCTRL_WCD 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_AUDIO_EXT_CLK 1 +#define CONFIG_SND_SOC_WCD9XXX_V2 1 +#define CONFIG_SND_SOC_WCD_MBHC 1 +#define CONFIG_SND_SOC_WSA881X 1 +#define CONFIG_SND_SOC_WCD_DSP_MGR 1 +#define CONFIG_SND_SOC_WCD934X 1 +#define CONFIG_SND_SOC_WCD934X_MBHC 1 +#define CONFIG_SND_SOC_WCD934X_DSD 1 +#define CONFIG_MSM_QDSP6V2_CODECS 1 +#define CONFIG_MSM_QDSP6_APRV3_GLINK 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_ADSP_LOADER 1 +#define CONFIG_REGMAP_SWR 1 +#define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_SND_SOC_POORWILLS 1 +#define CONFIG_SOUNDWIRE 1 +#define CONFIG_SOUNDWIRE_WCD_CTRL 1 +#define CONFIG_SND_SOC_QDSP6V2 1 +#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 +#define CONFIG_MSM_CDC_PINCTRL 1 +#define CONFIG_WCD9XXX_CODEC_CORE 1 +#define CONFIG_SND_SOC_WCD_MBHC_ADC 1 +#define CONFIG_QTI_PP 1 +#define CONFIG_SND_HWDEP 1 +#define CONFIG_SND_SOC_MACHINE_SDXPOORWILLS 1 +#define CONFIG_SND_SOC_MSM_STUB 1 From ffade12722bdfffdcb09397fd129e9737d37e9e7 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Fri, 22 Sep 2017 17:05:02 +0800 Subject: [PATCH 080/276] ASoC: wsa: add mixer ctl to set boost value Add a new mixer ctl to set WSA boost value for speaker protection v3. Boost value for different stages are packed together. Change-Id: Ie80d7bea4d3af00b74935f8b680e72ee967717f6 Signed-off-by: Xiaojun Sang --- asoc/codecs/wsa881x.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/asoc/codecs/wsa881x.c b/asoc/codecs/wsa881x.c index bff0ad539792..0755cdcab8b6 100644 --- a/asoc/codecs/wsa881x.c +++ b/asoc/codecs/wsa881x.c @@ -676,6 +676,36 @@ static int wsa881x_set_visense(struct snd_kcontrol *kcontrol, return 0; } +static int wsa881x_set_boost_level(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 wsa_boost_level = 0; + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", + __func__, ucontrol->value.integer.value[0]); + + wsa_boost_level = ucontrol->value.integer.value[0]; + snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1, + 0xff, wsa_boost_level); + + return 0; +} + +static int wsa881x_get_boost_level(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + u8 wsa_boost_level = 0; + + wsa_boost_level = snd_soc_read(codec, WSA881X_BOOST_PRESET_OUT1); + ucontrol->value.integer.value[0] = wsa_boost_level; + dev_dbg(codec->dev, "%s: boost level = 0x%x\n", __func__, + wsa_boost_level); + + return 0; +} + static const struct snd_kcontrol_new wsa881x_snd_controls[] = { SOC_SINGLE_EXT("COMP Switch", SND_SOC_NOPM, 0, 1, 0, wsa881x_get_compander, wsa881x_set_compander), @@ -685,6 +715,9 @@ static const struct snd_kcontrol_new wsa881x_snd_controls[] = { SOC_SINGLE_EXT("VISENSE Switch", SND_SOC_NOPM, 0, 1, 0, wsa881x_get_visense, wsa881x_set_visense), + + SOC_SINGLE_EXT("Boost Level", SND_SOC_NOPM, 0, 0xff, 0, + wsa881x_get_boost_level, wsa881x_set_boost_level), }; static const struct snd_kcontrol_new swr_dac_port[] = { From bd59f792295ca0dd9fec3cbbc110f27d7850324b Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Fri, 22 Sep 2017 17:54:48 +0800 Subject: [PATCH 081/276] uapi: extend struct of spk prot for ADSP SPv3 To support speaker protection v3, add sp version and limiter threshold in spk_prot_cfg struct. Change-Id: Ia4c89934e00bfa8c0e0c6823f48d56baf6759f14 Signed-off-by: Xiaojun Sang --- include/uapi/linux/msm_audio_calibration.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/uapi/linux/msm_audio_calibration.h b/include/uapi/linux/msm_audio_calibration.h index 1696ae5e9118..29fdf8f966e6 100644 --- a/include/uapi/linux/msm_audio_calibration.h +++ b/include/uapi/linux/msm_audio_calibration.h @@ -108,6 +108,7 @@ enum { #define AFE_SIDETONE_IIR_CAL_TYPE AFE_SIDETONE_IIR_CAL_TYPE #define TOPOLOGY_SPECIFIC_CHANNEL_INFO +#define MSM_SPKR_PROT_SPV3 enum { VERSION_0_0, @@ -301,6 +302,10 @@ struct audio_cal_info_spk_prot_cfg { * 1 - Start calib * 2 - Disable spk prot */ +#ifdef MSM_SPKR_PROT_SPV3 + uint32_t sp_version; + int32_t limiter_th[SP_V2_NUM_MAX_SPKRS]; +#endif }; struct audio_cal_info_sp_th_vi_ftm_cfg { From fbddd2f25683815fd3caa5ef0fc82c66b609678b Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Thu, 28 Sep 2017 18:21:21 +0800 Subject: [PATCH 082/276] ASoC: add support for speaker protection 3.0 Add a new AFE API to send limiter threshold to ADSP. Change-Id: Ibcd795d7c0edb4f081636c621e51e5a1eaa036b1 Signed-off-by: Xiaojun Sang --- dsp/q6afe.c | 26 ++++++++++++++++++++++++++ include/dsp/apr_audio-v2.h | 8 +++++++- include/dsp/q6afe-v2.h | 2 +- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index b6a7aa329447..435cfd7f383f 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -1021,6 +1021,7 @@ static int afe_spk_prot_prepare(int src_port, int dst_port, int param_id, } switch (param_id) { case AFE_PARAM_ID_FBSP_MODE_RX_CFG: + case AFE_PARAM_ID_SP_RX_LIMITER_TH: config.pdata.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX; break; case AFE_PARAM_ID_FEEDBACK_PATH_CFG: @@ -1205,6 +1206,7 @@ static void afe_send_cal_spkr_prot_tx(int port_id) static void afe_send_cal_spkr_prot_rx(int port_id) { union afe_spkr_prot_config afe_spk_config; + union afe_spkr_prot_config afe_spk_limiter_config; if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL) goto done; @@ -1226,6 +1228,30 @@ static void afe_send_cal_spkr_prot_rx(int port_id) &afe_spk_config)) pr_err("%s: RX MODE_VI_PROC_CFG failed\n", __func__); + + if (afe_spk_config.mode_rx_cfg.mode == + Q6AFE_MSM_SPKR_PROCESSING) { + if (this_afe.prot_cfg.sp_version >= + AFE_API_VERSION_SUPPORT_SPV3) { + afe_spk_limiter_config.limiter_th_cfg. + minor_version = 1; + afe_spk_limiter_config.limiter_th_cfg. + lim_thr_per_calib_q27[SP_V2_SPKR_1] = + this_afe.prot_cfg.limiter_th[SP_V2_SPKR_1]; + afe_spk_limiter_config.limiter_th_cfg. + lim_thr_per_calib_q27[SP_V2_SPKR_2] = + this_afe.prot_cfg.limiter_th[SP_V2_SPKR_2]; + if (afe_spk_prot_prepare(port_id, 0, + AFE_PARAM_ID_SP_RX_LIMITER_TH, + &afe_spk_limiter_config)) + pr_err("%s: SP_RX_LIMITER_TH failed.\n", + __func__); + } else { + pr_debug("%s: SPv3 failed to apply on AFE API version=%d.\n", + __func__, + this_afe.prot_cfg.sp_version); + } + } } mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock); done: diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index be56d74eef43..ad0aa44a64bd 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -8895,7 +8895,7 @@ struct cmd_set_topologies { #define AFE_MODULE_FB_SPKR_PROT_RX 0x0001021C #define AFE_MODULE_FB_SPKR_PROT_V2_RX 0x0001025F - +#define AFE_PARAM_ID_SP_RX_LIMITER_TH 0x000102B1 #define AFE_PARAM_ID_FBSP_MODE_RX_CFG 0x0001021D #define AFE_PARAM_ID_FBSP_PTONE_RAMP_CFG 0x00010260 @@ -9123,6 +9123,11 @@ struct afe_sp_ex_vi_get_param_resp { struct afe_sp_ex_vi_ftm_params param; } __packed; +struct afe_sp_rx_limiter_th_param { + uint32_t minor_version; + uint32_t lim_thr_per_calib_q27[SP_V2_NUM_MAX_SPKR]; +} __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; @@ -9132,6 +9137,7 @@ union afe_spkr_prot_config { 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; + struct afe_sp_rx_limiter_th_param limiter_th_cfg; } __packed; struct afe_spkr_prot_config_command { diff --git a/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h index 8fb480b3bb6d..6fb9c442ec30 100644 --- a/include/dsp/q6afe-v2.h +++ b/include/dsp/q6afe-v2.h @@ -41,7 +41,7 @@ #define AFE_CLK_VERSION_V1 1 #define AFE_CLK_VERSION_V2 2 - +#define AFE_API_VERSION_SUPPORT_SPV3 2 typedef int (*routing_cb)(int port); enum { From 2e6b7a35c5bc6b433762484fcccb92f29f6f3d0d Mon Sep 17 00:00:00 2001 From: kunleiz Date: Wed, 11 Oct 2017 15:04:02 +0800 Subject: [PATCH 083/276] ASoC: msm: qdsp6v2: Remove unused FE DAIs Clean up unused FE DAIs and remove duplicate routings from dapm routing map for MI2S_RX_VOICE Mixer. CRs-Fixed: 2001153 Change-Id: I2c9bd1255331d83b1901915259fe61d287f682fc Signed-off-by: kunleiz --- asoc/msm-dai-fe.c | 127 --------- asoc/msm-pcm-routing-v2.c | 586 +------------------------------------- asoc/msm-pcm-routing-v2.h | 4 - asoc/msm-qti-pp-config.c | 2 +- 4 files changed, 2 insertions(+), 717 deletions(-) diff --git a/asoc/msm-dai-fe.c b/asoc/msm-dai-fe.c index 89a9cc24a2e0..df24802c3404 100644 --- a/asoc/msm-dai-fe.c +++ b/asoc/msm-dai-fe.c @@ -154,31 +154,6 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .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", @@ -746,31 +721,6 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .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", @@ -2013,58 +1963,6 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .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", @@ -2335,31 +2233,6 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .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 = { diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 3338ac6834a5..4ef62d4954ed 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -643,9 +643,6 @@ static struct msm_pcm_routing_fdai_data {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* MULTIMEDIA20 */ - {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, - {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, - /* CS_VOICE */ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* VOIP */ @@ -658,15 +655,9 @@ static struct msm_pcm_routing_fdai_data {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* VOICE_STUB */ - {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, - {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, - /* VOLTE */ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* DTMF_RX */ - {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, - {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, - /* VOICE2 */ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* QCHAT */ @@ -700,9 +691,6 @@ static struct msm_pcm_routing_fdai_data {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* VOICE2_STUB */ - {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, - {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, - /* VOWLAN */ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* VOICEMMODE1 */ @@ -1273,18 +1261,6 @@ 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; @@ -7987,21 +7963,9 @@ static const struct snd_kcontrol_new mmul20_mixer_controls[] = { }; 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), @@ -8017,21 +7981,9 @@ static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = { }; 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), @@ -8041,21 +7993,9 @@ static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = { }; 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), @@ -8071,12 +8011,6 @@ static const struct snd_kcontrol_new sec_mi2s_rx_voice_mixer_controls[] = { }; 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), @@ -8086,15 +8020,9 @@ static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = { 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), @@ -8110,12 +8038,6 @@ static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = { }; 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), @@ -8125,15 +8047,9 @@ static const struct snd_kcontrol_new slimbus_6_rx_voice_mixer_controls[] = { 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), @@ -8149,12 +8065,6 @@ static const struct snd_kcontrol_new slimbus_6_rx_voice_mixer_controls[] = { }; 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), @@ -8164,15 +8074,9 @@ static const struct snd_kcontrol_new usb_audio_rx_voice_mixer_controls[] = { 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), @@ -8188,12 +8092,6 @@ static const struct snd_kcontrol_new usb_audio_rx_voice_mixer_controls[] = { }; 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), @@ -8203,12 +8101,6 @@ static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = { 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), @@ -8224,24 +8116,12 @@ static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = { }; 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), @@ -8257,12 +8137,6 @@ static const struct snd_kcontrol_new mi2s_rx_voice_mixer_controls[] = { }; 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), @@ -8272,15 +8146,9 @@ static const struct snd_kcontrol_new pri_mi2s_rx_voice_mixer_controls[] = { 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), @@ -8332,12 +8200,6 @@ static const struct snd_kcontrol_new int4_mi2s_rx_voice_mixer_controls[] = { }; 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), @@ -8347,15 +8209,9 @@ static const struct snd_kcontrol_new tert_mi2s_rx_voice_mixer_controls[] = { 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), @@ -8371,12 +8227,6 @@ static const struct snd_kcontrol_new tert_mi2s_rx_voice_mixer_controls[] = { }; 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), @@ -8386,15 +8236,9 @@ static const struct snd_kcontrol_new quat_mi2s_rx_voice_mixer_controls[] = { 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), @@ -8410,12 +8254,6 @@ static const struct snd_kcontrol_new quat_mi2s_rx_voice_mixer_controls[] = { }; 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), @@ -8425,15 +8263,9 @@ static const struct snd_kcontrol_new quin_mi2s_rx_voice_mixer_controls[] = { 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), @@ -8449,12 +8281,6 @@ static const struct snd_kcontrol_new quin_mi2s_rx_voice_mixer_controls[] = { }; 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), @@ -8464,15 +8290,9 @@ static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = { 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), @@ -8488,12 +8308,6 @@ static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = { }; 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), @@ -8503,15 +8317,9 @@ static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = { 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), @@ -8527,24 +8335,12 @@ static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = { }; 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), @@ -8560,24 +8356,12 @@ static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = { }; 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), @@ -8593,24 +8377,12 @@ static const struct snd_kcontrol_new tert_aux_pcm_rx_voice_mixer_controls[] = { }; 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), @@ -8626,24 +8398,12 @@ static const struct snd_kcontrol_new quat_aux_pcm_rx_voice_mixer_controls[] = { }; static const struct snd_kcontrol_new quin_aux_pcm_rx_voice_mixer_controls[] = { - SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_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_QUIN_AUXPCM_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8659,21 +8419,9 @@ static const struct snd_kcontrol_new quin_aux_pcm_rx_voice_mixer_controls[] = { }; 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), @@ -8698,12 +8446,6 @@ static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = { }; 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), @@ -8713,15 +8455,9 @@ static const struct snd_kcontrol_new slimbus_7_rx_voice_mixer_controls[] = { 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), @@ -8737,12 +8473,6 @@ static const struct snd_kcontrol_new slimbus_7_rx_voice_mixer_controls[] = { }; 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), @@ -8752,15 +8482,9 @@ static const struct snd_kcontrol_new slimbus_8_rx_voice_mixer_controls[] = { 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), @@ -8817,201 +8541,6 @@ static const struct snd_kcontrol_new slimbus_3_rx_mixer_controls[] = { 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("QUIN_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_QUIN_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("QUIN_AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_QUIN_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("QUIN_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_QUIN_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("QUIN_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_QUIN_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, @@ -12896,14 +12425,6 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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_OUT("MM_UL20", "MultiMedia20 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), @@ -13855,21 +13376,9 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_NOPM, 0, 0, quat_tdm_rx_2_voice_mixer_controls, ARRAY_SIZE(quat_tdm_rx_2_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)), @@ -15471,20 +14980,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUIN_AUX_PCM_RX", NULL, "QUIN_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"}, @@ -15492,19 +14987,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15512,10 +14999,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15526,10 +15009,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15540,10 +15019,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15554,10 +15029,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15567,10 +15038,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15578,10 +15045,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15592,10 +15055,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15604,10 +15063,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15616,10 +15071,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15628,10 +15079,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, {"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX_Voice Mixer"}, - {"QUIN_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"}, - {"QUIN_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"}, - {"QUIN_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"}, - {"QUIN_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"}, {"QUIN_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, {"QUIN_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, {"QUIN_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, @@ -15640,10 +15087,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, {"QUIN_AUX_PCM_RX", NULL, "QUIN_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"}, @@ -15652,22 +15095,15 @@ static const struct snd_soc_dapm_route intercon[] = { {"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_Voice Mixer", "DTMF", "DTMF_DL_HL"}, {"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"}, @@ -15691,11 +15127,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15705,11 +15137,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15718,11 +15146,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -16743,10 +16167,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -16757,10 +16177,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index 8f91e4ff13d9..5360b5b5f770 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -211,14 +211,11 @@ enum { MSM_FRONTEND_DAI_MULTIMEDIA18, MSM_FRONTEND_DAI_MULTIMEDIA19, MSM_FRONTEND_DAI_MULTIMEDIA20, - 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, @@ -230,7 +227,6 @@ enum { 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, diff --git a/asoc/msm-qti-pp-config.c b/asoc/msm-qti-pp-config.c index 0d1018236aec..00a29d71a55b 100644 --- a/asoc/msm-qti-pp-config.c +++ b/asoc/msm-qti-pp-config.c @@ -27,7 +27,7 @@ /* EQUALIZER */ /* Equal to Frontend after last of the MULTIMEDIA SESSIONS */ -#define MAX_EQ_SESSIONS MSM_FRONTEND_DAI_CS_VOICE +#define MAX_EQ_SESSIONS (MSM_FRONTEND_DAI_MULTIMEDIA20 + 1) enum { EQ_BAND1 = 0, From 34699612450254233b7d686303f6e59e5f58a2fa Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Mon, 16 Oct 2017 15:35:56 +0530 Subject: [PATCH 084/276] asoc: sdm660: update cpu dai id for quinary mi2s interface Update quinary mi2s cpu dai id to 4 for sdm670. This is required for sending correct afe port id for quin mi2s and keep all MI2S interfaces in sequence. CRs-Fixed: 2133890 Change-Id: I0ab35f2d97858f355da577076a5a1e3f438a1bab Signed-off-by: Rohit kumar --- asoc/sdm660-ext-dai-links.c | 4 ++-- asoc/sdm660-internal.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c index a33113550f4c..33e8e0aa2a89 100644 --- a/asoc/sdm660-ext-dai-links.c +++ b/asoc/sdm660-ext-dai-links.c @@ -1720,7 +1720,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { { .name = LPASS_BE_QUIN_MI2S_RX, .stream_name = "Quinary MI2S Playback", - .cpu_dai_name = "msm-dai-q6-mi2s.5", + .cpu_dai_name = "msm-dai-q6-mi2s.4", .platform_name = "msm-pcm-routing", .codec_name = "msm-stub-codec.1", .codec_dai_name = "msm-stub-rx", @@ -1735,7 +1735,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { { .name = LPASS_BE_QUIN_MI2S_TX, .stream_name = "Quinary MI2S Capture", - .cpu_dai_name = "msm-dai-q6-mi2s.5", + .cpu_dai_name = "msm-dai-q6-mi2s.4", .platform_name = "msm-pcm-routing", .codec_name = "msm-stub-codec.1", .codec_dai_name = "msm-stub-tx", diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c index a1536fefe635..20e49bcb1b44 100644 --- a/asoc/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -2759,7 +2759,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { { .name = LPASS_BE_QUIN_MI2S_RX, .stream_name = "Quinary MI2S Playback", - .cpu_dai_name = "msm-dai-q6-mi2s.5", + .cpu_dai_name = "msm-dai-q6-mi2s.4", .platform_name = "msm-pcm-routing", .codec_name = "msm-stub-codec.1", .codec_dai_name = "msm-stub-rx", @@ -2774,7 +2774,7 @@ static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = { { .name = LPASS_BE_QUIN_MI2S_TX, .stream_name = "Quinary MI2S Capture", - .cpu_dai_name = "msm-dai-q6-mi2s.5", + .cpu_dai_name = "msm-dai-q6-mi2s.4", .platform_name = "msm-pcm-routing", .codec_name = "msm-stub-codec.1", .codec_dai_name = "msm-stub-tx", From 75f7ab723ecf539091ffdb8a791f77d8cce8d51a Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Mon, 16 Oct 2017 15:37:05 +0530 Subject: [PATCH 085/276] asoc: dai-q6: update quinary mi2s dai id to 4 Update quinary mi2s dai id to 4 and secondary mi2s playback sd1 dai id to 5. Also update index of mi2s_config_controls for primary, secondary, tertiary and quaternary mi2s. CRs-Fixed: 2133890 Change-Id: I00997b79a148cf50b2a3f9acf6e44fe603e5c944 Signed-off-by: Rohit kumar --- asoc/msm-dai-q6-v2.c | 32 ++++++++++++++++---------------- asoc/msm-dai-q6-v2.h | 6 +++--- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index cfe47ed6fae0..2ccab3fbb664 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -3690,13 +3690,13 @@ static int msm_dai_q6_dai_mi2s_probe(struct snd_soc_dai *dai) 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) + if (dai->id == MSM_SEC_MI2S) ctrl = &mi2s_config_controls[6]; - if (dai->id == MSM_QUAT_MI2S) + if (dai->id == MSM_TERT_MI2S) ctrl = &mi2s_config_controls[7]; + if (dai->id == MSM_QUAT_MI2S) + ctrl = &mi2s_config_controls[8]; if (dai->id == MSM_QUIN_MI2S) ctrl = &mi2s_config_controls[9]; if (dai->id == MSM_SENARY_MI2S) @@ -4276,18 +4276,6 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .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", @@ -4313,6 +4301,18 @@ static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = { .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, + }, { .capture = { .stream_name = "Senary_mi2s Capture", diff --git a/asoc/msm-dai-q6-v2.h b/asoc/msm-dai-q6-v2.h index b1d76bf73f51..b3f457e9e544 100644 --- a/asoc/msm-dai-q6-v2.h +++ b/asoc/msm-dai-q6-v2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, 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 @@ -25,8 +25,8 @@ #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_QUIN_MI2S 4 +#define MSM_SEC_MI2S_SD1 5 #define MSM_SENARY_MI2S 6 #define MSM_INT0_MI2S 7 #define MSM_INT1_MI2S 8 From b5c3cc7f582f19f994b654d63678cc3557ea4d7a Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Mon, 23 Oct 2017 16:17:49 +0530 Subject: [PATCH 086/276] ASoC: wcd: Fix pop noise when device switch Pop is observed during switch from playback in native mode to voice call and vice-versa. This is resolved by mclk reset to non-native mode before hph path powers up. CRs-Fixed: 2117960 Change-Id: Ic554c3b2af24dac3ae94b5a239559e5d11e98271 Signed-off-by: Vatsal Bucha --- asoc/codecs/sdm660_cdc/msm-digital-cdc.c | 6 +++ asoc/sdm660-common.h | 3 ++ asoc/sdm660-internal.c | 52 +++++++++--------------- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c index e7c7147c0f7b..b23816e76d74 100644 --- a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -84,6 +84,12 @@ static int msm_digcdc_clock_control(bool flag) if (flag) { mutex_lock(&pdata->cdc_int_mclk0_mutex); if (atomic_read(&pdata->int_mclk0_enabled) == false) { + if (pdata->native_clk_set) + pdata->digital_cdc_core_clk.clk_freq_in_hz = + NATIVE_MCLK_RATE; + else + pdata->digital_cdc_core_clk.clk_freq_in_hz = + DEFAULT_MCLK_RATE; pdata->digital_cdc_core_clk.enable = 1; ret = afe_set_lpass_clock_v2( AFE_PORT_ID_INT0_MI2S_RX, diff --git a/asoc/sdm660-common.h b/asoc/sdm660-common.h index 2c8e29e8792f..030f5219a963 100644 --- a/asoc/sdm660-common.h +++ b/asoc/sdm660-common.h @@ -17,6 +17,9 @@ #include #include "codecs/wcd-mbhc-v2.h" +#define DEFAULT_MCLK_RATE 9600000 +#define NATIVE_MCLK_RATE 11289600 + #define SAMPLING_RATE_8KHZ 8000 #define SAMPLING_RATE_11P025KHZ 11025 #define SAMPLING_RATE_16KHZ 16000 diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c index a1536fefe635..cfc368b4491c 100644 --- a/asoc/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -24,9 +24,6 @@ #define __CHIPSET__ "SDM660 " #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 @@ -439,7 +436,7 @@ static int int_mi2s_ch_put(struct snd_kcontrol *kcontrol, 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), + msm_int_mclk0_event, SND_SOC_DAPM_PRE_PMU | 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), @@ -730,6 +727,8 @@ static int msm_int_enable_dig_cdc_clk(struct snd_soc_codec *codec, 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.clk_freq_in_hz = + DEFAULT_MCLK_RATE; pdata->digital_cdc_core_clk.enable = 0; ret = afe_set_lpass_clock_v2( AFE_PORT_ID_INT0_MI2S_RX, @@ -738,6 +737,7 @@ static int msm_int_enable_dig_cdc_clk(struct snd_soc_codec *codec, pr_err("%s: failed to disable CCLK\n", __func__); atomic_set(&pdata->int_mclk0_enabled, false); + atomic_set(&pdata->int_mclk0_rsc_ref, 0); } mutex_unlock(&pdata->cdc_int_mclk0_mutex); } @@ -959,6 +959,16 @@ static int msm_int_mclk0_event(struct snd_soc_dapm_widget *w, 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_cdc_pinctrl_select_active_state(pdata->pdm_gpio_p); + if (ret < 0) { + pr_err("%s: gpio set cannot be activated %s\n", + __func__, "int_pdm"); + return ret; + } + msm_int_enable_dig_cdc_clk(codec, 1, true); + msm_anlg_cdc_mclk_enable(codec, 1, true); + break; case SND_SOC_DAPM_POST_PMD: pr_debug("%s: mclk_res_ref = %d\n", __func__, atomic_read(&pdata->int_mclk0_rsc_ref)); @@ -968,12 +978,10 @@ static int msm_int_mclk0_event(struct snd_soc_dapm_widget *w, __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*/ - msm_anlg_cdc_mclk_enable(codec, 0, true); - msm_int_enable_dig_cdc_clk(codec, 0, true); - } + pr_debug("%s: disabling MCLK\n", __func__); + /* disable the codec mclk config*/ + msm_anlg_cdc_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); @@ -1158,19 +1166,6 @@ static int msm_int_mi2s_snd_startup(struct snd_pcm_substream *substream) __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_cdc_pinctrl_select_active_state(pdata->pdm_gpio_p); - if (ret < 0) { - pr_err("%s: gpio set cannot be activated %s\n", - __func__, "int_pdm"); - return ret; - } - msm_anlg_cdc_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); @@ -1181,9 +1176,6 @@ static int msm_int_mi2s_snd_startup(struct snd_pcm_substream *substream) 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); @@ -1192,12 +1184,6 @@ static void msm_int_mi2s_snd_shutdown(struct snd_pcm_substream *substream) 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) @@ -3070,6 +3056,8 @@ static void msm_disable_int_mclk0(struct work_struct *work) && atomic_read(&pdata->int_mclk0_rsc_ref) == 0) { pr_debug("Disable the mclk\n"); pdata->digital_cdc_core_clk.enable = 0; + pdata->digital_cdc_core_clk.clk_freq_in_hz = + DEFAULT_MCLK_RATE; ret = afe_set_lpass_clock_v2( AFE_PORT_ID_INT0_MI2S_RX, &pdata->digital_cdc_core_clk); From 65273f0cec3c40a18e3f6c2cdcf49f088ad248b3 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Wed, 4 Oct 2017 20:38:49 +0530 Subject: [PATCH 087/276] ASoC: Remove excess logs in compress driver Excess logs are printed in msm_compr_pointer during ADSP SSR. This causes failure of some interrupts to occur which results in SSR failure. Logs can be reduced by applying ratelimit. CRs-Fixed: 2128011 Change-Id: I0d3d82bf52ea307cdf719f97c4907a2d376580c1 Signed-off-by: Vatsal Bucha --- asoc/msm-compress-q6-v2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index cd9c28195b87..f0aa4eed7493 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -2610,8 +2610,8 @@ static int msm_compr_pointer(struct snd_compr_stream *cstream, 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\n", - __func__); + pr_err_ratelimited("%s Got RESET EVENTS notification, return error\n", + __func__); if (cstream->direction == SND_COMPRESS_PLAYBACK) runtime->total_bytes_transferred = tstamp.copied_total; else From 0c203301967352793c09ad02591a3906ebe94818 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Wed, 25 Oct 2017 22:59:48 -0700 Subject: [PATCH 088/276] dsp: sdxpoorwills: fix compilation issue Variable 'vm_page_prot' has different types in different ARM platforms. Using macro "pgprot_val" to handle it to fix compilation issue for sdxpoorwills targets. Change-Id: Ic7b8de9860759cc56812e7a3ffb1aaea2b6779f9 Signed-off-by: Xiaoyu Ye --- dsp/msm_audio_ion.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dsp/msm_audio_ion.c b/dsp/msm_audio_ion.c index 24df1aa06714..2d84ddf12fa8 100644 --- a/dsp/msm_audio_ion.c +++ b/dsp/msm_audio_ion.c @@ -314,7 +314,7 @@ int msm_audio_ion_mmap(struct audio_buffer *ab, vma, (unsigned int)addr, len, (unsigned int)vma->vm_start, (unsigned int)vma->vm_end, - (unsigned long)vma->vm_page_prot.pgprot); + (unsigned long)pgprot_val(vma->vm_page_prot)); remap_pfn_range(vma, addr, page_to_pfn(page), len, vma->vm_page_prot); addr += len; @@ -337,7 +337,7 @@ int msm_audio_ion_mmap(struct audio_buffer *ab, pr_debug("vma=%pK, vm_start=%x vm_end=%x vm_pgoff=%ld vm_page_prot=%lu\n", vma, (unsigned int)vma->vm_start, (unsigned int)vma->vm_end, vma->vm_pgoff, - (unsigned long)vma->vm_page_prot.pgprot); + (unsigned long)pgprot_val(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", From d97fc93e4cffa81b09de693a2819afb3d2e0a762 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Tue, 31 Oct 2017 18:44:16 -0700 Subject: [PATCH 089/276] ASoC: sdm845: fix clock refcount for MI2S/AUXPCM shutdown For MI2S and AUXPCM shutdown, if clock disable is failed, do not bother to re-increment clock reference. Otherwise, next time when startup is called, clock enable will not be called again, so use case will not work. Change-Id: I5ce4590d2a2030f9d00f1dba9577396f7685a87b Signed-off-by: Banajit Goswami Signed-off-by: Xiaoyu Ye --- asoc/sdm845.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index f8c044401617..ed854e217ea2 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -4153,7 +4153,6 @@ static void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream) 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); @@ -4715,11 +4714,9 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) 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) { + 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); From cf6eb87234595948f7b5f09322c86fd0873a2d30 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Mon, 16 Oct 2017 15:38:41 +0530 Subject: [PATCH 090/276] dsp: q6audio: add support for quinary interfaces Add support for quinary pcm and tdm interfaces in q6audio. CRs-Fixed: 2133890 Change-Id: I59ca2965f5d38d30bea5a00f34898af5c948ac16 Signed-off-by: Rohit kumar --- dsp/q6audio-v2.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/dsp/q6audio-v2.c b/dsp/q6audio-v2.c index 6e31c366c798..088d9b279759 100644 --- a/dsp/q6audio-v2.c +++ b/dsp/q6audio-v2.c @@ -40,6 +40,10 @@ int q6audio_get_port_index(u16 port_id) return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX; case AFE_PORT_ID_QUATERNARY_PCM_TX: return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX; + case AFE_PORT_ID_QUINARY_PCM_RX: + return IDX_AFE_PORT_ID_QUINARY_PCM_RX; + case AFE_PORT_ID_QUINARY_PCM_TX: + return IDX_AFE_PORT_ID_QUINARY_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; @@ -95,6 +99,10 @@ int q6audio_get_port_index(u16 port_id) 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_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 AUDIO_PORT_ID_I2S_RX: return IDX_AUDIO_PORT_ID_I2S_RX; case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: @@ -227,6 +235,38 @@ int q6audio_get_port_index(u16 port_id) 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_QUINARY_TDM_RX: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_0; + case AFE_PORT_ID_QUINARY_TDM_TX: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_0; + case AFE_PORT_ID_QUINARY_TDM_RX_1: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_1; + case AFE_PORT_ID_QUINARY_TDM_TX_1: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_1; + case AFE_PORT_ID_QUINARY_TDM_RX_2: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_2; + case AFE_PORT_ID_QUINARY_TDM_TX_2: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_2; + case AFE_PORT_ID_QUINARY_TDM_RX_3: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_3; + case AFE_PORT_ID_QUINARY_TDM_TX_3: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_3; + case AFE_PORT_ID_QUINARY_TDM_RX_4: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_4; + case AFE_PORT_ID_QUINARY_TDM_TX_4: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_4; + case AFE_PORT_ID_QUINARY_TDM_RX_5: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_5; + case AFE_PORT_ID_QUINARY_TDM_TX_5: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_5; + case AFE_PORT_ID_QUINARY_TDM_RX_6: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_6; + case AFE_PORT_ID_QUINARY_TDM_TX_6: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_6; + case AFE_PORT_ID_QUINARY_TDM_RX_7: + return IDX_AFE_PORT_ID_QUINARY_TDM_RX_7; + case AFE_PORT_ID_QUINARY_TDM_TX_7: + return IDX_AFE_PORT_ID_QUINARY_TDM_TX_7; case AFE_PORT_ID_SENARY_MI2S_TX: return IDX_AFE_PORT_ID_SENARY_MI2S_TX; case AFE_PORT_ID_USB_RX: @@ -286,6 +326,10 @@ int q6audio_get_port_id(u16 port_id) return AFE_PORT_ID_QUATERNARY_PCM_RX; case AFE_PORT_ID_QUATERNARY_PCM_TX: return AFE_PORT_ID_QUATERNARY_PCM_TX; + case AFE_PORT_ID_QUINARY_PCM_RX: + return AFE_PORT_ID_QUINARY_PCM_RX; + case AFE_PORT_ID_QUINARY_PCM_TX: + return AFE_PORT_ID_QUINARY_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; @@ -342,6 +386,10 @@ int q6audio_get_port_id(u16 port_id) return AFE_PORT_ID_TERTIARY_MI2S_RX; case AFE_PORT_ID_TERTIARY_MI2S_TX: return AFE_PORT_ID_TERTIARY_MI2S_TX; + case AFE_PORT_ID_QUINARY_MI2S_RX: + return AFE_PORT_ID_QUINARY_MI2S_RX; + case AFE_PORT_ID_QUINARY_MI2S_TX: + return AFE_PORT_ID_QUINARY_MI2S_TX; case AUDIO_PORT_ID_I2S_RX: return AUDIO_PORT_ID_I2S_RX; case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1: @@ -474,6 +522,38 @@ int q6audio_get_port_id(u16 port_id) 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_QUINARY_TDM_RX: + return AFE_PORT_ID_QUINARY_TDM_RX; + case AFE_PORT_ID_QUINARY_TDM_TX: + return AFE_PORT_ID_QUINARY_TDM_TX; + case AFE_PORT_ID_QUINARY_TDM_RX_1: + return AFE_PORT_ID_QUINARY_TDM_RX_1; + case AFE_PORT_ID_QUINARY_TDM_TX_1: + return AFE_PORT_ID_QUINARY_TDM_TX_1; + case AFE_PORT_ID_QUINARY_TDM_RX_2: + return AFE_PORT_ID_QUINARY_TDM_RX_2; + case AFE_PORT_ID_QUINARY_TDM_TX_2: + return AFE_PORT_ID_QUINARY_TDM_TX_2; + case AFE_PORT_ID_QUINARY_TDM_RX_3: + return AFE_PORT_ID_QUINARY_TDM_RX_3; + case AFE_PORT_ID_QUINARY_TDM_TX_3: + return AFE_PORT_ID_QUINARY_TDM_TX_3; + case AFE_PORT_ID_QUINARY_TDM_RX_4: + return AFE_PORT_ID_QUINARY_TDM_RX_4; + case AFE_PORT_ID_QUINARY_TDM_TX_4: + return AFE_PORT_ID_QUINARY_TDM_TX_4; + case AFE_PORT_ID_QUINARY_TDM_RX_5: + return AFE_PORT_ID_QUINARY_TDM_RX_5; + case AFE_PORT_ID_QUINARY_TDM_TX_5: + return AFE_PORT_ID_QUINARY_TDM_TX_5; + case AFE_PORT_ID_QUINARY_TDM_RX_6: + return AFE_PORT_ID_QUINARY_TDM_RX_6; + case AFE_PORT_ID_QUINARY_TDM_TX_6: + return AFE_PORT_ID_QUINARY_TDM_TX_6; + case AFE_PORT_ID_QUINARY_TDM_RX_7: + return AFE_PORT_ID_QUINARY_TDM_RX_7; + case AFE_PORT_ID_QUINARY_TDM_TX_7: + return AFE_PORT_ID_QUINARY_TDM_TX_7; case AFE_PORT_ID_SENARY_MI2S_TX: return AFE_PORT_ID_SENARY_MI2S_TX; case AFE_PORT_ID_USB_RX: @@ -549,12 +629,16 @@ int q6audio_is_digital_pcm_interface(u16 port_id) case AFE_PORT_ID_TERTIARY_PCM_TX: case AFE_PORT_ID_QUATERNARY_PCM_RX: case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_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_QUINARY_MI2S_TX: + case AFE_PORT_ID_QUINARY_MI2S_RX: case AFE_PORT_ID_QUATERNARY_MI2S_RX: case AFE_PORT_ID_QUATERNARY_MI2S_TX: case AFE_PORT_ID_PRIMARY_MI2S_RX: @@ -627,6 +711,22 @@ int q6audio_is_digital_pcm_interface(u16 port_id) 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_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: + case AFE_PORT_ID_QUINARY_TDM_TX_7: case AFE_PORT_ID_SENARY_MI2S_TX: case AFE_PORT_ID_INT0_MI2S_RX: case AFE_PORT_ID_INT0_MI2S_TX: @@ -665,6 +765,8 @@ int q6audio_validate_port(u16 port_id) case AFE_PORT_ID_TERTIARY_PCM_TX: case AFE_PORT_ID_QUATERNARY_PCM_RX: case AFE_PORT_ID_QUATERNARY_PCM_TX: + case AFE_PORT_ID_QUINARY_PCM_RX: + case AFE_PORT_ID_QUINARY_PCM_TX: case SECONDARY_I2S_RX: case SECONDARY_I2S_TX: case MI2S_RX: @@ -712,6 +814,8 @@ int q6audio_validate_port(u16 port_id) case AFE_PORT_ID_SPDIF_RX: 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_SECONDARY_MI2S_RX_SD1: case AFE_PORT_ID_PRIMARY_TDM_RX: case AFE_PORT_ID_PRIMARY_TDM_TX: @@ -777,6 +881,22 @@ int q6audio_validate_port(u16 port_id) 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_QUINARY_TDM_RX: + case AFE_PORT_ID_QUINARY_TDM_TX: + case AFE_PORT_ID_QUINARY_TDM_RX_1: + case AFE_PORT_ID_QUINARY_TDM_TX_1: + case AFE_PORT_ID_QUINARY_TDM_RX_2: + case AFE_PORT_ID_QUINARY_TDM_TX_2: + case AFE_PORT_ID_QUINARY_TDM_RX_3: + case AFE_PORT_ID_QUINARY_TDM_TX_3: + case AFE_PORT_ID_QUINARY_TDM_RX_4: + case AFE_PORT_ID_QUINARY_TDM_TX_4: + case AFE_PORT_ID_QUINARY_TDM_RX_5: + case AFE_PORT_ID_QUINARY_TDM_TX_5: + case AFE_PORT_ID_QUINARY_TDM_RX_6: + case AFE_PORT_ID_QUINARY_TDM_TX_6: + case AFE_PORT_ID_QUINARY_TDM_RX_7: + case AFE_PORT_ID_QUINARY_TDM_TX_7: case AFE_PORT_ID_SENARY_MI2S_TX: case AFE_PORT_ID_USB_RX: case AFE_PORT_ID_USB_TX: From 7ab92dd2952d59f3c15c856effe94bba1858e3ff Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Tue, 26 Sep 2017 12:48:31 +0800 Subject: [PATCH 091/276] ASoC: dsp: add spin lock to protect ac There is a possible use after free for the ac variable that causes kernel panic. Add spinlock to fix race condition to avoid this issue. Change-Id: I71638c269f572ff1eaad87682fa000cd1dad407d Signed-off-by: Meng Wang --- dsp/q6asm.c | 111 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 85 insertions(+), 26 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 0a4216a0e28c..5272927d7929 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -91,8 +91,13 @@ struct asm_mmap { }; static struct asm_mmap this_mmap; + +struct audio_session { + struct audio_client *ac; + spinlock_t session_lock; +}; /* session id: 0 reserved */ -static struct audio_client *session[ASM_ACTIVE_STREAMS_ALLOWED + 1]; +static struct audio_session session[ASM_ACTIVE_STREAMS_ALLOWED + 1]; struct asm_buffer_node { struct list_head list; @@ -530,8 +535,8 @@ 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; + if (!(session[n].ac)) { + session[n].ac = ac; return n; } } @@ -544,7 +549,7 @@ 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) + if (session[n].ac == ac) return 1; } return 0; @@ -552,12 +557,21 @@ static bool q6asm_is_valid_audio_client(struct audio_client *ac) static void q6asm_session_free(struct audio_client *ac) { + int session_id; + pr_debug("%s: sessionid[%d]\n", __func__, ac->session); + session_id = ac->session; rtac_remove_popp_from_adm_devices(ac->session); - session[ac->session] = 0; + spin_lock_bh(&(session[session_id].session_lock)); + session[ac->session].ac = NULL; ac->session = 0; ac->perf_mode = LEGACY_PCM_MODE; ac->fptr_cache_ops = NULL; + ac->cb = NULL; + ac->priv = NULL; + kfree(ac); + ac = NULL; + spin_unlock_bh(&(session[session_id].session_lock)); } static uint32_t q6asm_get_next_buf(struct audio_client *ac, @@ -1071,8 +1085,6 @@ void q6asm_audio_client_free(struct audio_client *ac) pr_debug("%s: APR De-Register\n", __func__); /*done:*/ - kfree(ac); - ac = NULL; mutex_unlock(&session_lock); } @@ -1295,11 +1307,11 @@ struct audio_client *q6asm_get_audio_client(int session_id) goto err; } - if (!session[session_id]) { + if (!(session[session_id].ac)) { pr_err("%s: session not active: %d\n", __func__, session_id); goto err; } - return session[session_id]; + return session[session_id].ac; err: return NULL; } @@ -1510,6 +1522,8 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) struct audio_client *ac = NULL; struct audio_port_data *port; + int session_id; + if (!data) { pr_err("%s: Invalid CB\n", __func__); return 0; @@ -1551,12 +1565,20 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) return 0; } asm_token.token = data->token; - ac = q6asm_get_audio_client(asm_token._token.session_id); + session_id = asm_token._token.session_id; + + if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) + spin_lock(&(session[session_id].session_lock)); + + ac = q6asm_get_audio_client(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); + __func__, session_id); + if ((session_id > 0 && + session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) + spin_unlock(&(session[session_id].session_lock)); return 0; } @@ -1607,6 +1629,9 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) __func__, payload[0]); break; } + if ((session_id > 0 && + session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) + spin_unlock(&(session[session_id].session_lock)); return 0; } @@ -1641,6 +1666,9 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) if (ac->cb) ac->cb(data->opcode, data->token, data->payload, ac->priv); + if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) + spin_unlock(&(session[session_id].session_lock)); + return 0; } @@ -1708,6 +1736,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) struct msm_adsp_event_data *pp_event_package = NULL; uint32_t payload_size = 0; + int session_id; + if (ac == NULL) { pr_err("%s: ac NULL\n", __func__); return -EINVAL; @@ -1716,17 +1746,21 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) 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) { + session_id = ac->session; + if (ac->session <= 0 || ac->session > ASM_ACTIVE_STREAMS_ALLOWED) { pr_err("%s: Session ID is invalid, session = %d\n", __func__, ac->session); return -EINVAL; } + spin_lock(&(session[session_id].session_lock)); + + if (!q6asm_is_valid_audio_client(ac)) { + pr_err("%s: audio client pointer is invalid, ac = %pK\n", + __func__, ac); + spin_unlock(&(session[session_id].session_lock)); + return -EINVAL; + } payload = data->payload; asm_token.token = data->token; @@ -1737,7 +1771,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) } if (data->opcode == RESET_EVENTS) { + spin_unlock(&(session[session_id].session_lock)); mutex_lock(&ac->cmd_lock); + spin_lock(&(session[session_id].session_lock)); atomic_set(&ac->reset, 1); if (ac->apr == NULL) { ac->apr = ac->apr2; @@ -1758,6 +1794,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) wake_up(&ac->time_wait); wake_up(&ac->cmd_wait); wake_up(&ac->mem_wait); + spin_unlock(&(session[session_id].session_lock)); mutex_unlock(&ac->cmd_lock); return 0; } @@ -1772,6 +1809,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) { if (payload == NULL) { pr_err("%s: payload is null\n", __func__); + spin_unlock(&(session[session_id].session_lock)); return -EINVAL; } dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n", @@ -1797,6 +1835,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) ret = q6asm_is_valid_session(data, priv); if (ret != 0) { pr_err("%s: session invalid %d\n", __func__, ret); + spin_unlock(&(session[session_id].session_lock)); return ret; } case ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2: @@ -1836,6 +1875,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) payload[1]); wake_up(&ac->cmd_wait); } + spin_unlock( + &(session[session_id].session_lock)); return 0; } if ((is_adsp_reg_event(payload[0]) >= 0) || @@ -1866,6 +1907,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) atomic_set(&ac->mem_state, payload[1]); wake_up(&ac->mem_wait); } + spin_unlock( + &(session[session_id].session_lock)); return 0; } if (atomic_read(&ac->mem_state) && wakeup_flag) { @@ -1913,6 +1956,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) __func__, payload[0]); break; } + + spin_unlock(&(session[session_id].session_lock)); return 0; } @@ -1927,6 +1972,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (port->buf == NULL) { pr_err("%s: Unexpected Write Done\n", __func__); + spin_unlock( + &(session[session_id].session_lock)); return -EINVAL; } spin_lock_irqsave(&port->dsp_lock, dsp_flags); @@ -1941,6 +1988,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) __func__, payload[0], payload[1]); spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); + spin_unlock( + &(session[session_id].session_lock)); return -EINVAL; } port->buf[buf_index].used = 1; @@ -2011,6 +2060,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (ac->io_mode & SYNC_IO_MODE) { if (port->buf == NULL) { pr_err("%s: Unexpected Write Done\n", __func__); + spin_unlock( + &(session[session_id].session_lock)); return -EINVAL; } spin_lock_irqsave(&port->dsp_lock, dsp_flags); @@ -2085,8 +2136,10 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) pr_debug("%s: ASM_STREAM_EVENT payload[0][0x%x] payload[1][0x%x]", __func__, payload[0], payload[1]); i = is_adsp_raise_event(data->opcode); - if (i < 0) + if (i < 0) { + spin_unlock(&(session[session_id].session_lock)); return 0; + } /* repack payload for asm_stream_pp_event * package is composed of event type + size + actual payload @@ -2095,8 +2148,10 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) pp_event_package = kzalloc(payload_size + sizeof(struct msm_adsp_event_data), GFP_ATOMIC); - if (!pp_event_package) + if (!pp_event_package) { + spin_unlock(&(session[session_id].session_lock)); return -ENOMEM; + } pp_event_package->event_type = i; pp_event_package->payload_len = payload_size; @@ -2105,6 +2160,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) ac->cb(data->opcode, data->token, (void *)pp_event_package, ac->priv); kfree(pp_event_package); + spin_unlock(&(session[session_id].session_lock)); return 0; case ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2: pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n", @@ -2130,7 +2186,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (ac->cb) ac->cb(data->opcode, data->token, data->payload, ac->priv); - + spin_unlock(&(session[session_id].session_lock)); return 0; } @@ -8976,7 +9032,7 @@ int q6asm_get_apr_service_id(int session_id) return -EINVAL; } - return ((struct apr_svc *)session[session_id]->apr)->id; + return ((struct apr_svc *)(session[session_id].ac)->apr)->id; } int q6asm_get_asm_topology(int session_id) @@ -8987,12 +9043,12 @@ int q6asm_get_asm_topology(int session_id) pr_err("%s: invalid session_id = %d\n", __func__, session_id); goto done; } - if (session[session_id] == NULL) { + if (session[session_id].ac == NULL) { pr_err("%s: session not created for session id = %d\n", __func__, session_id); goto done; } - topology = session[session_id]->topology; + topology = (session[session_id].ac)->topology; done: return topology; } @@ -9005,12 +9061,12 @@ int q6asm_get_asm_app_type(int session_id) pr_err("%s: invalid session_id = %d\n", __func__, session_id); goto done; } - if (session[session_id] == NULL) { + if (session[session_id].ac == NULL) { pr_err("%s: session not created for session id = %d\n", __func__, session_id); goto done; } - app_type = session[session_id]->app_type; + app_type = (session[session_id].ac)->app_type; done: return app_type; } @@ -9356,7 +9412,10 @@ static int __init q6asm_init(void) pr_debug("%s:\n", __func__); - memset(session, 0, sizeof(session)); + memset(session, 0, sizeof(struct audio_session) * + (ASM_ACTIVE_STREAMS_ALLOWED + 1)); + for (lcnt = 0; lcnt <= ASM_ACTIVE_STREAMS_ALLOWED; lcnt++) + spin_lock_init(&(session[lcnt].session_lock)); set_custom_topology = 1; /*setup common client used for cal mem map */ From 53feb105225fe3aab258d27e09234e5ae7601fe2 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Wed, 11 Oct 2017 20:03:20 +0530 Subject: [PATCH 092/276] ASoC: codecs: sdm660_cdc: Remove lb_mode mixer var from sdm660 lb_mode var is redundant for sdm660_cdc. Loopback works fine without this var also. CRs-Fixed: 2101423 Change-Id: I727da02660a0f1d6ee5582fc2f2c0a8f6cd59bc1 Signed-off-by: Vatsal Bucha --- asoc/codecs/sdm660_cdc/msm-analog-cdc.c | 46 ------------------------- 1 file changed, 46 deletions(-) diff --git a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c index 623303c3adca..db09290577b0 100644 --- a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -1579,43 +1579,6 @@ static int msm_anlg_cdc_ear_pa_boost_set(struct snd_kcontrol *kcontrol, return 0; } -static int msm_anlg_cdc_loopback_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct msm_asoc_mach_data *pdata = NULL; - - pdata = snd_soc_card_get_drvdata(codec->component.card); - dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", - __func__, ucontrol->value.integer.value[0]); - - return pdata->lb_mode; -} - -static int msm_anlg_cdc_loopback_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct msm_asoc_mach_data *pdata = NULL; - - pdata = snd_soc_card_get_drvdata(codec->component.card); - dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", - __func__, ucontrol->value.integer.value[0]); - - switch (ucontrol->value.integer.value[0]) { - case 0: - pdata->lb_mode = false; - break; - case 1: - pdata->lb_mode = true; - break; - default: - return -EINVAL; - } - - return 0; -} - static int msm_anlg_cdc_pa_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1910,13 +1873,6 @@ static int msm_anlg_cdc_ext_spk_boost_set(struct snd_kcontrol *kcontrol, return 0; } - -static const char * const msm_anlg_cdc_loopback_mode_ctrl_text[] = { - "DISABLE", "ENABLE"}; -static const struct soc_enum msm_anlg_cdc_loopback_mode_ctl_enum[] = { - SOC_ENUM_SINGLE_EXT(2, msm_anlg_cdc_loopback_mode_ctrl_text), -}; - static const char * const msm_anlg_cdc_ear_pa_boost_ctrl_text[] = { "DISABLE", "ENABLE"}; static const struct soc_enum msm_anlg_cdc_ear_pa_boost_ctl_enum[] = { @@ -1980,8 +1936,6 @@ static const struct snd_kcontrol_new msm_anlg_cdc_snd_controls[] = { SOC_ENUM_EXT("Ext Spk Boost", msm_anlg_cdc_ext_spk_boost_ctl_enum[0], msm_anlg_cdc_ext_spk_boost_get, msm_anlg_cdc_ext_spk_boost_set), - SOC_ENUM_EXT("LOOPBACK Mode", msm_anlg_cdc_loopback_mode_ctl_enum[0], - msm_anlg_cdc_loopback_mode_get, msm_anlg_cdc_loopback_mode_put), SOC_SINGLE_TLV("ADC1 Volume", MSM89XX_PMIC_ANALOG_TX_1_EN, 3, 8, 0, analog_gain), SOC_SINGLE_TLV("ADC2 Volume", MSM89XX_PMIC_ANALOG_TX_2_EN, 3, From 7c4681c10e11bb865ce1d69fca015145eee110c2 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Thu, 12 Oct 2017 09:14:03 +0530 Subject: [PATCH 093/276] ASoC: wcd: set analog gain mode to manual when compander is off If compander DRE is not turned on, ensure analog gain mode is set to manual mode. This also resolves low volume at one channel. CRs-Fixed: 2102126 Change-Id: Id29b7055fe4f7fa7b3853c26770df38d10539d8c Signed-off-by: Vatsal Bucha --- asoc/codecs/sdm660_cdc/msm-analog-cdc.c | 13 +++++++++++++ asoc/codecs/sdm660_cdc/msm-analog-cdc.h | 1 + asoc/codecs/sdm660_cdc/msm-digital-cdc.c | 7 +++++++ asoc/codecs/sdm660_cdc/msm-digital-cdc.h | 2 ++ 4 files changed, 23 insertions(+) diff --git a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c index 623303c3adca..c292b9116f50 100644 --- a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -2620,6 +2620,18 @@ static int msm_anlg_cdc_codec_enable_micbias(struct snd_soc_dapm_widget *w, return 0; } +static void set_compander_mode(void *handle, int val) +{ + struct sdm660_cdc_priv *handle_cdc = handle; + struct snd_soc_codec *codec = handle_cdc->codec; + + if (get_codec_version(handle_cdc) >= DIANGU) { + snd_soc_update_bits(codec, + MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, + 0x08, val); + }; +} + static void update_clkdiv(void *handle, int val) { struct sdm660_cdc_priv *handle_cdc = handle; @@ -4653,6 +4665,7 @@ static int msm_anlg_cdc_probe(struct platform_device *pdev) BLOCKING_INIT_NOTIFIER_HEAD(&sdm660_cdc->notifier_mbhc); sdm660_cdc->dig_plat_data.handle = (void *) sdm660_cdc; + sdm660_cdc->dig_plat_data.set_compander_mode = set_compander_mode; sdm660_cdc->dig_plat_data.update_clkdiv = update_clkdiv; sdm660_cdc->dig_plat_data.get_cdc_version = get_cdc_version; sdm660_cdc->dig_plat_data.register_notifier = diff --git a/asoc/codecs/sdm660_cdc/msm-analog-cdc.h b/asoc/codecs/sdm660_cdc/msm-analog-cdc.h index fffdf3156255..137fa9d94414 100644 --- a/asoc/codecs/sdm660_cdc/msm-analog-cdc.h +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc.h @@ -168,6 +168,7 @@ struct msm_dig_ctrl_data { struct msm_dig_ctrl_platform_data { void *handle; + void (*set_compander_mode)(void *handle, int val); void (*update_clkdiv)(void *handle, int val); int (*get_cdc_version)(void *handle); int (*register_notifier)(void *handle, diff --git a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c index 4ab203750303..00a969935d1a 100644 --- a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -231,6 +231,12 @@ static int msm_dig_cdc_codec_config_compander(struct snd_soc_codec *codec, } if (SND_SOC_DAPM_EVENT_ON(event)) { + /* compander is not enabled */ + if (!dig_cdc->comp_enabled[interp_n]) { + dig_cdc->set_compander_mode(dig_cdc->handle, 0x00); + return 0; + }; + dig_cdc->set_compander_mode(dig_cdc->handle, 0x08); /* Enable Compander Clock */ snd_soc_update_bits(codec, MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x09); @@ -2080,6 +2086,7 @@ static int msm_dig_cdc_probe(struct platform_device *pdev) msm_dig_cdc->dig_base, &msm_digital_regmap_config); msm_dig_cdc->update_clkdiv = pdata->update_clkdiv; + msm_dig_cdc->set_compander_mode = pdata->set_compander_mode; msm_dig_cdc->get_cdc_version = pdata->get_cdc_version; msm_dig_cdc->handle = pdata->handle; msm_dig_cdc->register_notifier = pdata->register_notifier; diff --git a/asoc/codecs/sdm660_cdc/msm-digital-cdc.h b/asoc/codecs/sdm660_cdc/msm-digital-cdc.h index 42c31d5089b2..cbb8a8138db8 100644 --- a/asoc/codecs/sdm660_cdc/msm-digital-cdc.h +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc.h @@ -55,6 +55,7 @@ struct msm_dig_priv { u32 mute_mask; int dapm_bias_off; void *handle; + void (*set_compander_mode)(void *handle, int val); void (*update_clkdiv)(void *handle, int val); int (*get_cdc_version)(void *handle); int (*register_notifier)(void *handle, @@ -65,6 +66,7 @@ struct msm_dig_priv { struct dig_ctrl_platform_data { void *handle; + void (*set_compander_mode)(void *handle, int val); void (*update_clkdiv)(void *handle, int val); int (*get_cdc_version)(void *handle); int (*register_notifier)(void *handle, From 49cade5974ec3673b3d5a17e9591fd8b6a2b21d1 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Thu, 12 Oct 2017 08:24:04 +0530 Subject: [PATCH 094/276] ASoC: codecs: sdm660_cdc: Fix HPH CnP issue on sdm660-internal Pop is observed after start playback on hph. This is because both compander channels are not enabled at the same time which differs from ideal sequence. Amplitude of pop gets reduced after corresponding change is made. CRs-Fixed: 2101404 Change-Id: Iba2b8a809fd3b53f0c180ce28ac5668d7635fb24 Signed-off-by: Vatsal Bucha --- asoc/codecs/sdm660_cdc/msm-digital-cdc.c | 32 +++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c index 00a969935d1a..4b875ccec969 100644 --- a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -217,6 +217,7 @@ static int msm_dig_cdc_codec_config_compander(struct snd_soc_codec *codec, { struct msm_dig_priv *dig_cdc = snd_soc_codec_get_drvdata(codec); int comp_ch_bits_set = 0x03; + int comp_ch_value; dev_dbg(codec->dev, "%s: event %d shift %d, enabled %d\n", __func__, event, interp_n, @@ -236,15 +237,40 @@ static int msm_dig_cdc_codec_config_compander(struct snd_soc_codec *codec, dig_cdc->set_compander_mode(dig_cdc->handle, 0x00); return 0; }; + comp_ch_value = snd_soc_read(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL); + if (interp_n == 0) { + if (comp_ch_value & 0x02) { + dev_dbg(codec->dev, + "%s comp ch 1 already enabled\n", + __func__); + return 0; + } + } + if (interp_n == 1) { + if (comp_ch_value & 0x01) { + dev_dbg(codec->dev, + "%s comp ch 0 already enabled\n", + __func__); + return 0; + } + } dig_cdc->set_compander_mode(dig_cdc->handle, 0x08); /* Enable Compander Clock */ snd_soc_update_bits(codec, MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x09); snd_soc_update_bits(codec, MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x01); - snd_soc_update_bits(codec, - MSM89XX_CDC_CORE_COMP0_B1_CTL, - 1 << interp_n, 1 << interp_n); + if (dig_cdc->comp_enabled[MSM89XX_RX1]) { + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL, + 0x02, 0x02); + } + if (dig_cdc->comp_enabled[MSM89XX_RX2]) { + snd_soc_update_bits(codec, + MSM89XX_CDC_CORE_COMP0_B1_CTL, + 0x01, 0x01); + } snd_soc_update_bits(codec, MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x01); snd_soc_update_bits(codec, From 8da46e21bcb99022d44a14b658c594c049af3b0c Mon Sep 17 00:00:00 2001 From: Banajit Goswami Date: Fri, 27 Oct 2017 01:26:50 -0700 Subject: [PATCH 095/276] ipc: add api to vote for upgrading thread priority For some time critical tasks, the thread priority might need to be upgraded to Real-Time Thread priority level. Expose an interface from APR layer so that clients can vote for a priority upgrade whenever needed. Change-Id: Ieb4afa914905750eccdf7672020a8751fdcf6462 Signed-off-by: Banajit Goswami --- include/ipc/apr.h | 4 ++- include/ipc/apr_tal.h | 3 +- ipc/apr.c | 82 +++++++++++++++++++++++++++++++++++++++++++ ipc/apr_tal_glink.c | 32 +++++++++++++++++ 4 files changed, 119 insertions(+), 2 deletions(-) diff --git a/include/ipc/apr.h b/include/ipc/apr.h index 29deb3ca5ac7..e49c5d378ce8 100644 --- a/include/ipc/apr.h +++ b/include/ipc/apr.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved. +/* 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 @@ -187,4 +187,6 @@ 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); +int apr_start_rx_rt(void *handle); +int apr_end_rx_rt(void *handle); #endif diff --git a/include/ipc/apr_tal.h b/include/ipc/apr_tal.h index 26d1a4c56ebc..08f739394dc9 100644 --- a/include/ipc/apr_tal.h +++ b/include/ipc/apr_tal.h @@ -73,7 +73,8 @@ int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data, 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); - +int apr_tal_start_rx_rt(struct apr_svc_ch_dev *apr_ch); +int apr_tal_end_rx_rt(struct apr_svc_ch_dev *apr_ch); struct apr_svc_ch_dev { void *handle; diff --git a/ipc/apr.c b/ipc/apr.c index 8fc62f406cb7..1ddd8b9a7a62 100644 --- a/ipc/apr.c +++ b/ipc/apr.c @@ -735,6 +735,88 @@ static void apr_reset_deregister(struct work_struct *work) kfree(apr_reset); } +int apr_start_rx_rt(void *handle) +{ + int rc = 0; + struct apr_svc *svc = handle; + uint16_t dest_id = 0; + uint16_t client_id = 0; + + if (!svc) { + pr_err("%s: Invalid APR handle\n", __func__); + return -EINVAL; + } + + mutex_lock(&svc->m_lock); + dest_id = svc->dest_id; + client_id = svc->client_id; + + if ((client_id >= APR_CLIENT_MAX) || (dest_id >= APR_DEST_MAX)) { + pr_err("%s: %s invalid. client_id = %u, dest_id = %u\n", + __func__, + client_id >= APR_CLIENT_MAX ? "Client ID" : "Dest ID", + client_id, dest_id); + rc = -EINVAL; + goto exit; + } + + if (!client[dest_id][client_id].handle) { + pr_err("%s: Client handle is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + rc = apr_tal_start_rx_rt(client[dest_id][client_id].handle); + if (rc) + pr_err("%s: failed to set RT thread priority for APR RX. rc = %d\n", + __func__, rc); + +exit: + mutex_unlock(&svc->m_lock); + return rc; +} + +int apr_end_rx_rt(void *handle) +{ + int rc = 0; + struct apr_svc *svc = handle; + uint16_t dest_id = 0; + uint16_t client_id = 0; + + if (!svc) { + pr_err("%s: Invalid APR handle\n", __func__); + return -EINVAL; + } + + mutex_lock(&svc->m_lock); + dest_id = svc->dest_id; + client_id = svc->client_id; + + if ((client_id >= APR_CLIENT_MAX) || (dest_id >= APR_DEST_MAX)) { + pr_err("%s: %s invalid. client_id = %u, dest_id = %u\n", + __func__, + client_id >= APR_CLIENT_MAX ? "Client ID" : "Dest ID", + client_id, dest_id); + rc = -EINVAL; + goto exit; + } + + if (!client[dest_id][client_id].handle) { + pr_err("%s: Client handle is NULL\n", __func__); + rc = -EINVAL; + goto exit; + } + + rc = apr_tal_end_rx_rt(client[dest_id][client_id].handle); + if (rc) + pr_err("%s: failed to reset RT thread priority for APR RX. rc = %d\n", + __func__, rc); + +exit: + mutex_unlock(&svc->m_lock); + return rc; +} + int apr_deregister(void *handle) { struct apr_svc *svc = handle; diff --git a/ipc/apr_tal_glink.c b/ipc/apr_tal_glink.c index 7de7ddafdb54..b4b473d7b90b 100644 --- a/ipc/apr_tal_glink.c +++ b/ipc/apr_tal_glink.c @@ -357,6 +357,38 @@ struct apr_svc_ch_dev *apr_tal_open(uint32_t clnt, uint32_t dest, uint32_t dl, return rc ? NULL : apr_ch; } +int apr_tal_start_rx_rt(struct apr_svc_ch_dev *apr_ch) +{ + int rc = 0; + + if (!apr_ch || !apr_ch->handle) { + rc = -EINVAL; + goto exit; + } + + mutex_lock(&apr_ch->m_lock); + rc = glink_start_rx_rt(apr_ch->handle); + mutex_unlock(&apr_ch->m_lock); +exit: + return rc; +} + +int apr_tal_end_rx_rt(struct apr_svc_ch_dev *apr_ch) +{ + int rc = 0; + + if (!apr_ch || !apr_ch->handle) { + rc = -EINVAL; + goto exit; + } + + mutex_lock(&apr_ch->m_lock); + rc = glink_end_rx_rt(apr_ch->handle); + mutex_unlock(&apr_ch->m_lock); +exit: + return rc; +} + int apr_tal_close(struct apr_svc_ch_dev *apr_ch) { int rc; From 992a0cf22fe8f2b7b53e6dcc251e7bcf1f2ac174 Mon Sep 17 00:00:00 2001 From: Banajit Goswami Date: Fri, 27 Oct 2017 01:31:19 -0700 Subject: [PATCH 096/276] asoc: vote for Glink Rx thread priority upgrade For Low-latency audio playback usecase, vote for a priority upgrade for Glink Rx thread, to avoid any performance issue. Change-Id: I8332c80eedd7325700e695f341fc4b92f65fd77c Signed-off-by: Banajit Goswami --- asoc/msm-pcm-q6-v2.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 4910decf6d8b..17cd5fcb9481 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -614,8 +614,15 @@ 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; + struct msm_plat_data *pdata; int ret = 0; + 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; + } prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL); if (prtd == NULL) return -ENOMEM; @@ -693,6 +700,10 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) prtd->reset_event = false; runtime->private_data = prtd; msm_adsp_init_mixer_ctl_pp_event_queue(soc_prtd); + /* Vote to update the Rx thread priority to RT Thread for playback */ + if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && + (pdata->perf_mode == LOW_LATENCY_PCM_MODE)) + apr_start_rx_rt(prtd->audio_client->apr); return 0; } @@ -800,6 +811,7 @@ 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; + struct msm_plat_data *pdata; uint32_t timeout; int dir = 0; int ret = 0; @@ -809,6 +821,16 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream) if (prtd->audio_client) { dir = IN; + /* + * Unvote to downgrade the Rx thread priority from + * RT Thread for Low-Latency use case. + */ + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (pdata) { + if (pdata->perf_mode == LOW_LATENCY_PCM_MODE) + apr_end_rx_rt(prtd->audio_client->apr); + } /* determine timeout length */ if (runtime->frame_bits == 0 || runtime->rate == 0) { timeout = CMD_EOS_MIN_TIMEOUT_LENGTH; From bd0b78ffd2219ec245c8a559f39fd8d3b031c59a Mon Sep 17 00:00:00 2001 From: Yunfei Zhang Date: Tue, 7 Nov 2017 16:30:13 +0800 Subject: [PATCH 097/276] ASoC: qdsp6v2: Add Audio EC Ref routing to LSM FEs Add routings between LSM FEs and Audio EC Ref, or AFE port for external Audio EC Ref will not be enabled during DSP SVA. Change-Id: Ic70ee9479cd384d0226ac0e92a1824f92ec2d946 Signed-off-by: Yunfei Zhang --- asoc/msm-pcm-routing-v2.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 3338ac6834a5..fa4c96e28d96 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -15825,6 +15825,16 @@ static const struct snd_soc_dapm_route intercon[] = { {"AUDIO_REF_EC_UL19 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"AUDIO_REF_EC_UL19 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"LSM1_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM2_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM3_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM4_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM5_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM6_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM7_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"LSM8_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, + {"MM_UL1", NULL, "AUDIO_REF_EC_UL1 MUX"}, {"MM_UL2", NULL, "AUDIO_REF_EC_UL2 MUX"}, {"MM_UL3", NULL, "AUDIO_REF_EC_UL3 MUX"}, From 6d42b0e51ed7f8e51397d89af66c1453beba51cd Mon Sep 17 00:00:00 2001 From: Tanya Dixit Date: Wed, 1 Nov 2017 12:41:24 +0530 Subject: [PATCH 098/276] dsp: add mutex unlock to properly release lock Add mutex unlock in function audio_effects_shared_ioctl at appropriate place to prevent use after free. CRs-Fixed: 2123291 Change-Id: Ie0d321dc8cc20a295d102a44faea7e5710834932 Signed-off-by: Tanya Dixit --- dsp/codecs/audio_hwacc_effects.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dsp/codecs/audio_hwacc_effects.c b/dsp/codecs/audio_hwacc_effects.c index 9444aa7c6259..ad23af2901a8 100644 --- a/dsp/codecs/audio_hwacc_effects.c +++ b/dsp/codecs/audio_hwacc_effects.c @@ -161,7 +161,6 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned int cmd, pr_err("%s: Read buffer Allocation failed rc = %d\n", __func__, rc); rc = -ENOMEM; - mutex_unlock(&effects->lock); goto readbuf_fail; } atomic_set(&effects->out_count, effects->config.output.num_buf); @@ -176,7 +175,6 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned int cmd, if (rc < 0) { pr_err("%s: pcm read block config failed\n", __func__); rc = -EINVAL; - mutex_unlock(&effects->lock); goto cfg_fail; } pr_debug("%s: dec: sample_rate: %d, num_channels: %d, bit_width: %d\n", @@ -191,7 +189,6 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned int cmd, pr_err("%s: pcm write format block config failed\n", __func__); rc = -EINVAL; - mutex_unlock(&effects->lock); goto cfg_fail; } @@ -325,6 +322,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned int cmd, readbuf_fail: q6asm_audio_client_buf_free_contiguous(IN, effects->ac); + mutex_unlock(&effects->lock); return rc; cfg_fail: q6asm_audio_client_buf_free_contiguous(IN, @@ -332,6 +330,7 @@ static int audio_effects_shared_ioctl(struct file *file, unsigned int cmd, q6asm_audio_client_buf_free_contiguous(OUT, effects->ac); effects->buf_alloc = 0; + mutex_unlock(&effects->lock); return rc; } From 767473c5f3bfba2abbcfcbede7abdb105f9082af Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Wed, 6 Sep 2017 17:40:11 +0530 Subject: [PATCH 099/276] ASoC: wcd: Fix pop noise in HPH plus LO4 concurrent usecase Pop sound happens after start playback on HPH when ultrasound playback on LO4 is enabled. This is because CnP is sequence dependent and enabling design blocks in specified order is causing minimum CnP. CRs-Fixed: 2100346 Change-Id: If10f9013cc1a2aa61fe9c719d3190f2df0b00950 Signed-off-by: Vatsal Bucha --- asoc/codecs/wcd9335.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/asoc/codecs/wcd9335.c b/asoc/codecs/wcd9335.c index 8ade82b7bb21..a0a4b770ff8d 100644 --- a/asoc/codecs/wcd9335.c +++ b/asoc/codecs/wcd9335.c @@ -3874,6 +3874,8 @@ static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0); } set_bit(HPH_PA_DELAY, &tasha->status_mask); + if (!(strcmp(w->name, "HPHR PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x40, 0x40); break; case SND_SOC_DAPM_POST_PMU: if (!(strcmp(w->name, "ANC HPHR PA"))) { @@ -3926,7 +3928,8 @@ static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, WCD_EVENT_PRE_HPHR_PA_OFF, &tasha->mbhc); tasha_codec_hph_post_pa_config(tasha, hph_mode, event); - if (!(strcmp(w->name, "ANC HPHR PA"))) + if (!(strcmp(w->name, "ANC HPHR PA")) || + !(strcmp(w->name, "HPHR PA"))) snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x40, 0x00); break; case SND_SOC_DAPM_POST_PMD: @@ -3967,6 +3970,8 @@ static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, (test_bit(HPH_PA_DELAY, &tasha->status_mask))) { snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0); } + if (!(strcmp(w->name, "HPHL PA"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x80, 0x80); set_bit(HPH_PA_DELAY, &tasha->status_mask); break; case SND_SOC_DAPM_POST_PMU: @@ -4021,7 +4026,8 @@ static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, WCD_EVENT_PRE_HPHL_PA_OFF, &tasha->mbhc); tasha_codec_hph_post_pa_config(tasha, hph_mode, event); - if (!(strcmp(w->name, "ANC HPHL PA"))) + if (!(strcmp(w->name, "ANC HPHL PA")) || + !(strcmp(w->name, "HPHL PA"))) snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x80, 0x00); break; case SND_SOC_DAPM_POST_PMD: @@ -4366,6 +4372,9 @@ static int tasha_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, ((hph_mode == CLS_H_LOHIFI) ? CLS_H_HIFI : hph_mode)); + if (!(strcmp(w->name, "RX INT2 DAC"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x10, 0x10); + tasha_codec_hph_mode_config(codec, event, hph_mode); if (tasha->anc_func) @@ -4388,6 +4397,8 @@ static int tasha_codec_hphr_dac_event(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, 0x03, 0x00); } + if (!(strcmp(w->name, "RX INT2 DAC"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x10, 0x00); break; case SND_SOC_DAPM_POST_PMD: /* 1000us required as per HW requirement */ @@ -4446,6 +4457,9 @@ static int tasha_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, ((hph_mode == CLS_H_LOHIFI) ? CLS_H_HIFI : hph_mode)); + if (!(strcmp(w->name, "RX INT1 DAC"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x20, 0x20); + tasha_codec_hph_mode_config(codec, event, hph_mode); if (tasha->anc_func) @@ -4475,6 +4489,8 @@ static int tasha_codec_hphl_dac_event(struct snd_soc_dapm_widget *w, } break; case SND_SOC_DAPM_PRE_PMD: + if (!(strcmp(w->name, "RX INT1 DAC"))) + snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x20, 0x00); if ((hph_mode == CLS_H_LP) && (TASHA_IS_1_1(wcd9xxx))) { snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL, @@ -10891,12 +10907,12 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { 0, 0, tasha_codec_ear_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, WCD9335_ANA_HPH, - 5, 0, tasha_codec_hphl_dac_event, + SND_SOC_DAPM_DAC_E("RX INT1 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_hphl_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, WCD9335_ANA_HPH, - 4, 0, tasha_codec_hphr_dac_event, + SND_SOC_DAPM_DAC_E("RX INT2 DAC", NULL, SND_SOC_NOPM, + 0, 0, tasha_codec_hphr_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_DAC_E("RX INT3 DAC", NULL, SND_SOC_NOPM, @@ -10911,11 +10927,11 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = { SND_SOC_DAPM_DAC_E("RX INT6 DAC", NULL, SND_SOC_NOPM, 0, 0, tasha_codec_lineout_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_PGA_E("HPHL PA", WCD9335_ANA_HPH, 7, 0, NULL, 0, + SND_SOC_DAPM_PGA_E("HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0, tasha_codec_enable_hphl_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_PGA_E("HPHR PA", WCD9335_ANA_HPH, 6, 0, NULL, 0, + SND_SOC_DAPM_PGA_E("HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0, tasha_codec_enable_hphr_pa, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD), From 725bf2d5bd320655fcbba3b8729730d4c5872e5b Mon Sep 17 00:00:00 2001 From: Haynes Mathew George Date: Wed, 18 Oct 2017 14:40:24 -0700 Subject: [PATCH 100/276] asoc: sdm845: update and affine qos value Fix qos value to 300 micro seconds to prevent core from going into low power mode when low latency session is active. Also pin this qos to core 1 & core 2 where audio and glink threads are running to let other cores to go to low power modes if possible. Change-Id: I46d356c384fd8d02a9a3b336a113200171bf25a0 Signed-off-by: Haynes Mathew George Signed-off-by: Dhananjay Kumar Signed-off-by: Aniket Kumar Lata --- asoc/sdm845.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index 76f5a603d99a..0b9d5d071d72 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,7 @@ #define TDM_CHANNEL_MAX 8 #define MSM_HIFI_ON 1 +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ enum { SLIM_RX_0 = 0, @@ -2626,21 +2628,6 @@ static int msm_hifi_put(struct snd_kcontrol *kcontrol, return 0; } -static s32 msm_qos_value(struct snd_pcm_runtime *runtime) -{ - s32 usecs; - - if (!runtime->rate) - return -EINVAL; - - /* take 75% of period time as the deadline */ - usecs = (750000 / runtime->rate) * runtime->period_size; - usecs += ((750000 % runtime->rate) * runtime->period_size) / - runtime->rate; - - return usecs; -} - static int msm_qos_ctl_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -2680,7 +2667,7 @@ static int msm_qos_ctl_put(struct snd_kcontrol *kcontrol, pr_err("%s: runtime is null\n", __func__); return -EINVAL; } - usecs = msm_qos_value(substream->runtime); + usecs = MSM_LL_QOS_VALUE; if (usecs >= 0) pm_qos_add_request(&substream->latency_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, usecs); @@ -4628,6 +4615,30 @@ static struct snd_soc_ops sdm845_tdm_be_ops = { .shutdown = sdm845_tdm_snd_shutdown }; +static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) +{ + cpumask_t mask; + + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); /* affine to core 1 */ + cpumask_set_cpu(2, &mask); /* affine to core 2 */ + cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask); + + substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + MSM_LL_QOS_VALUE); + return 0; +} + +static struct snd_soc_ops msm_fe_qos_ops = { + .prepare = msm_fe_qos_prepare, +}; + static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) { int ret = 0; @@ -4998,6 +5009,7 @@ static struct snd_soc_dai_link msm_common_dai_links[] = { /* this dainlink has playback support */ .ignore_pmdown_time = 1, .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, }, { .name = "Listen 1 Audio Service", @@ -5065,6 +5077,7 @@ static struct snd_soc_dai_link msm_common_dai_links[] = { .ignore_pmdown_time = 1, /* this dainlink has playback support */ .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, }, /* HDMI Hostless */ { From a98442349023e7cd8d3f937a0c6b3c7b16d8a77c Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Fri, 27 Oct 2017 18:41:55 -0700 Subject: [PATCH 101/276] asoc: codecs: add support for TX hybrid mode Add support for analog mic TX hybrid mode to allow audio catpure in high performance at the same time achieving lower power. CRs-Fixed: 2136385 Change-Id: I08b90a26d6b6e4761ff17dec84bf58f8d437d908 Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd934x/wcd934x.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 66c7830e2a3f..8c546aa5948e 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -115,6 +115,7 @@ static const struct snd_kcontrol_new name##_mux = \ #define WCD934X_AMIC_PWR_LEVEL_LP 0 #define WCD934X_AMIC_PWR_LEVEL_DEFAULT 1 #define WCD934X_AMIC_PWR_LEVEL_HP 2 +#define WCD934X_AMIC_PWR_LEVEL_HYBRID 3 #define WCD934X_AMIC_PWR_LVL_MASK 0x60 #define WCD934X_AMIC_PWR_LVL_SHIFT 0x5 @@ -122,6 +123,7 @@ static const struct snd_kcontrol_new name##_mux = \ #define WCD934X_DEC_PWR_LVL_LP 0x02 #define WCD934X_DEC_PWR_LVL_HP 0x04 #define WCD934X_DEC_PWR_LVL_DF 0x00 +#define WCD934X_DEC_PWR_LVL_HYBRID WCD934X_DEC_PWR_LVL_DF #define WCD934X_STRING_LEN 100 #define WCD934X_CDC_SIDETONE_IIR_COEFF_MAX 5 @@ -4001,6 +4003,7 @@ static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w, WCD934X_DEC_PWR_LVL_HP); break; case WCD934X_AMIC_PWR_LEVEL_DEFAULT: + case WCD934X_AMIC_PWR_LEVEL_HYBRID: default: snd_soc_update_bits(codec, dec_cfg_reg, WCD934X_DEC_PWR_LVL_MASK, @@ -5634,7 +5637,7 @@ static const char * const rx_cf_text[] = { }; static const char * const amic_pwr_lvl_text[] = { - "LOW_PWR", "DEFAULT", "HIGH_PERF" + "LOW_PWR", "DEFAULT", "HIGH_PERF", "HYBRID" }; static const char * const hph_idle_detect_text[] = { From 67ac8b96af5d3bfb470143574e8536f3df4ab7f1 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Wed, 8 Nov 2017 20:32:38 -0800 Subject: [PATCH 102/276] asoc: codecs: modify gnd mic swap threshold for usb-c analog To reduce time of usb-c analog headset detection, reduce gnd mic swap threshold if usb-c is enabled. Use existing threshold value otherwise. CRs-Fixed: 2137077 Change-Id: Ic1d1f3a00c9034caaac45f2c80f018546a69ce59 Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd-mbhc-adc.c | 10 +++++----- asoc/codecs/wcd-mbhc-legacy.c | 8 ++++---- asoc/codecs/wcd-mbhc-v2.c | 2 ++ asoc/codecs/wcd-mbhc-v2.h | 2 ++ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/asoc/codecs/wcd-mbhc-adc.c b/asoc/codecs/wcd-mbhc-adc.c index bcb616e7d700..920796f2e39d 100644 --- a/asoc/codecs/wcd-mbhc-adc.c +++ b/asoc/codecs/wcd-mbhc-adc.c @@ -622,7 +622,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) do { cross_conn = wcd_check_cross_conn(mbhc); try++; - } while (try < GND_MIC_SWAP_THRESHOLD); + } while (try < mbhc->swap_thr); if (cross_conn > 0) { plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP; @@ -714,10 +714,10 @@ static void wcd_correct_swch_plug(struct work_struct *work) pt_gnd_mic_swap_cnt++; no_gnd_mic_swap_cnt = 0; if (pt_gnd_mic_swap_cnt < - GND_MIC_SWAP_THRESHOLD) { + mbhc->swap_thr) { continue; } else if (pt_gnd_mic_swap_cnt > - GND_MIC_SWAP_THRESHOLD) { + mbhc->swap_thr) { /* * This is due to GND/MIC switch didn't * work, Report unsupported plug. @@ -735,14 +735,14 @@ static void wcd_correct_swch_plug(struct work_struct *work) plug_type = wcd_mbhc_get_plug_from_adc( mbhc, output_mv); if ((no_gnd_mic_swap_cnt < - GND_MIC_SWAP_THRESHOLD) && + mbhc->swap_thr) && (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) { continue; } else { no_gnd_mic_swap_cnt = 0; } } - if ((pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) && + if ((pt_gnd_mic_swap_cnt == mbhc->swap_thr) && (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) { /* * if switch is toggled, check again, diff --git a/asoc/codecs/wcd-mbhc-legacy.c b/asoc/codecs/wcd-mbhc-legacy.c index a72f64b40861..9f6a6254986f 100644 --- a/asoc/codecs/wcd-mbhc-legacy.c +++ b/asoc/codecs/wcd-mbhc-legacy.c @@ -498,7 +498,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) do { cross_conn = wcd_check_cross_conn(mbhc); try++; - } while (try < GND_MIC_SWAP_THRESHOLD); + } while (try < mbhc->swap_thr); /* * Check for cross connection 4 times. @@ -599,10 +599,10 @@ static void wcd_correct_swch_plug(struct work_struct *work) pt_gnd_mic_swap_cnt++; no_gnd_mic_swap_cnt = 0; if (pt_gnd_mic_swap_cnt < - GND_MIC_SWAP_THRESHOLD) { + mbhc->swap_thr) { continue; } else if (pt_gnd_mic_swap_cnt > - GND_MIC_SWAP_THRESHOLD) { + mbhc->swap_thr) { /* * This is due to GND/MIC switch didn't * work, Report unsupported plug. @@ -626,7 +626,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) no_gnd_mic_swap_cnt = 0; } } - if ((pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) && + if ((pt_gnd_mic_swap_cnt == mbhc->swap_thr) && (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) { /* * if switch is toggled, check again, diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index d7c59211658a..ab26cf38e6c8 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -1707,6 +1707,7 @@ int wcd_mbhc_start(struct wcd_mbhc *mbhc, struct wcd_mbhc_config *mbhc_cfg) if (mbhc_cfg->enable_usbc_analog) { dev_dbg(mbhc->codec->dev, "%s: usbc analog enabled\n", __func__); + mbhc->swap_thr = GND_MIC_USBC_SWAP_THRESHOLD; rc = wcd_mbhc_init_gpio(mbhc, mbhc_cfg, "qcom,usbc-analog-en1-gpio", &config->usbc_en1_gpio, @@ -1901,6 +1902,7 @@ int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec, mbhc->extn_cable_hph_rem = false; mbhc->hph_type = WCD_MBHC_HPH_NONE; mbhc->wcd_mbhc_regs = wcd_mbhc_regs; + mbhc->swap_thr = GND_MIC_SWAP_THRESHOLD; if (mbhc->intr_ids == NULL) { pr_err("%s: Interrupt mapping not provided\n", __func__); diff --git a/asoc/codecs/wcd-mbhc-v2.h b/asoc/codecs/wcd-mbhc-v2.h index 0c57eee8a454..bdae77630f02 100644 --- a/asoc/codecs/wcd-mbhc-v2.h +++ b/asoc/codecs/wcd-mbhc-v2.h @@ -142,6 +142,7 @@ do { \ #define SPECIAL_HS_DETECT_TIME_MS (2 * 1000) #define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250 #define GND_MIC_SWAP_THRESHOLD 4 +#define GND_MIC_USBC_SWAP_THRESHOLD 2 #define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100 #define HS_VREF_MIN_VAL 1400 #define FW_READ_ATTEMPTS 15 @@ -524,6 +525,7 @@ struct wcd_mbhc { bool gnd_swh; /*track GND switch NC / NO */ u32 hs_thr; u32 hph_thr; + u32 swap_thr; u32 moist_vref; u32 moist_iref; u32 moist_rref; From a2c40fc9c6520704a8cb0462f3c83e4b15ed4920 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 13 Nov 2017 23:44:32 -0800 Subject: [PATCH 103/276] dsp: codecs: add support to enable multiaac 5.1 channel playback Channel number of multiaac audio decoder is hard-coded to two during the configuration. Modify it to accept different channel numbers to enable 5.1 channel playback. Change-Id: Ibc33ddc7f2ac810a6a34cdbd7a832e58531dfa4f Signed-off-by: Xiaoyu Ye --- dsp/codecs/audio_multi_aac.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dsp/codecs/audio_multi_aac.c b/dsp/codecs/audio_multi_aac.c index 5d407e0c5e61..a1e50bc1f14b 100644 --- a/dsp/codecs/audio_multi_aac.c +++ b/dsp/codecs/audio_multi_aac.c @@ -111,7 +111,14 @@ static long audio_ioctl_shared(struct file *file, unsigned int cmd, pr_err("cmd media format block failed\n"); break; } - rc = q6asm_set_encdec_chan_map(audio->ac, 2); + + /* Fall back to the default number of channels + * if aac_cfg.ch_cfg is not between 1-6 + */ + if ((aac_cfg.ch_cfg == 0) || (aac_cfg.ch_cfg > 6)) + aac_cfg.ch_cfg = 2; + + rc = q6asm_set_encdec_chan_map(audio->ac, aac_cfg.ch_cfg); if (rc < 0) { pr_err("%s: cmd set encdec_chan_map failed\n", __func__); From fa83ed2e7038f1f931f60636865ada46055f48b1 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Wed, 25 Oct 2017 15:50:46 -0700 Subject: [PATCH 104/276] asoc: codecs: stop mbhc before mbhc init and after ssr In SSR init call stop mbhc before initializing mbhc again, so that usbc analog deregisters the callback with PMI notifier. CRs-fixed: 2128532 Change-Id: I329f25172b5cc2d80b93918787f703d89c95062f Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd934x/wcd934x-mbhc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/asoc/codecs/wcd934x/wcd934x-mbhc.c b/asoc/codecs/wcd934x/wcd934x-mbhc.c index 807d4ea58e89..ae147ca0f411 100644 --- a/asoc/codecs/wcd934x/wcd934x-mbhc.c +++ b/asoc/codecs/wcd934x/wcd934x-mbhc.c @@ -1043,6 +1043,7 @@ int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc, return -EINVAL; } + tavil_mbhc_hs_detect_exit(codec); wcd_mbhc_deinit(wcd_mbhc); ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb, &intr_ids, wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED); From af073943e9bc2a8edd9d265c64ff41b69a64f392 Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Wed, 25 Oct 2017 15:02:28 +0530 Subject: [PATCH 105/276] dsp: add LDAC encoder support Add support for LDAC encoding in dsp to enable LDAC encoder over split a2dp path. CRs-Fixed: 2143086 Change-Id: Ia5ed981f5cd9cc61621d667362585ec7a7e64fa1 Signed-off-by: Preetam Singh Ranawat --- asoc/msm-dai-q6-v2.c | 11 ++++++++++ dsp/q6afe.c | 12 ++++++++-- include/dsp/apr_audio-v2.h | 45 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 2 deletions(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 2ccab3fbb664..a7d82eabd8f7 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -51,6 +51,7 @@ enum { ENC_FMT_APTX = ASM_MEDIA_FMT_APTX, ENC_FMT_APTX_HD = ASM_MEDIA_FMT_APTX_HD, ENC_FMT_CELT = ASM_MEDIA_FMT_CELT, + ENC_FMT_LDAC = ASM_MEDIA_FMT_LDAC, }; enum { @@ -2263,6 +2264,11 @@ static int msm_dai_q6_afe_enc_cfg_get(struct snd_kcontrol *kcontrol, &dai_data->enc_config.data, sizeof(struct asm_celt_enc_cfg_t)); break; + case ENC_FMT_LDAC: + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->enc_config.data, + sizeof(struct asm_ldac_enc_cfg_t)); + break; default: pr_debug("%s: unknown format = %d\n", __func__, dai_data->enc_config.format); @@ -2316,6 +2322,11 @@ static int msm_dai_q6_afe_enc_cfg_put(struct snd_kcontrol *kcontrol, ucontrol->value.bytes.data + format_size, sizeof(struct asm_celt_enc_cfg_t)); break; + case ENC_FMT_LDAC: + memcpy(&dai_data->enc_config.data, + ucontrol->value.bytes.data + format_size, + sizeof(struct asm_ldac_enc_cfg_t)); + break; default: pr_debug("%s: Ignore enc config for unknown format = %d\n", __func__, dai_data->enc_config.format); diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 435cfd7f383f..a95b30d7718b 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -2867,7 +2867,7 @@ static int q6afe_send_enc_config(u16 port_id, 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 && - format != ASM_MEDIA_FMT_CELT) { + format != ASM_MEDIA_FMT_CELT && format != ASM_MEDIA_FMT_LDAC) { pr_err("%s:Unsuppported format Ignore AFE config\n", __func__); return 0; } @@ -2961,7 +2961,15 @@ static int q6afe_send_enc_config(u16 port_id, 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 (format == ASM_MEDIA_FMT_LDAC) { + config.port.media_type.sample_rate = + config.port.enc_blk_param.enc_blk_config.ldac_config. + custom_config.sample_rate; + } else { + 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 diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index ad0aa44a64bd..bc1763216cb2 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -3377,6 +3377,50 @@ struct asm_celt_enc_cfg_t { struct asm_celt_specific_enc_cfg_t celt_specific_config; } __packed; +#define ASM_MEDIA_FMT_LDAC 0x00013224 +struct asm_ldac_specific_enc_cfg_t { + /* + * This is used to calculate the encoder output + * bytes per frame (i.e. bytes per packet). + * Bit rate also configures the EQMID. + * The min bit rate 303000 bps is calculated for + * 44.1 kHz and 88.2 KHz sampling frequencies with + * Mobile use Quality. + * The max bit rate of 990000 bps is calculated for + * 96kHz and 48 KHz with High Quality + * @Range(in bits per second) + * 303000 for Mobile use Quality + * 606000 for standard Quality + * 909000 for High Quality + */ + uint32_t bit_rate; + /* + * The channel setting information for LDAC specification + * of Bluetooth A2DP which is determined by SRC and SNK + * devices in Bluetooth transmission. + * @Range: + * 0 for native mode + * 4 for mono + * 2 for dual channel + * 1 for stereo + */ + uint16_t channel_mode; + /* + * Maximum Transmission Unit (MTU). + * The minimum MTU that a L2CAP implementation for LDAC shall + * support is 679 bytes, because LDAC is optimized with 2-DH5 + * packet as its target. + * @Range : 679 + * @Default: 679 for LDACBT_MTU_2DH5 + */ + uint16_t mtu; +} __packed; + +struct asm_ldac_enc_cfg_t { + struct asm_custom_enc_cfg_t custom_config; + struct asm_ldac_specific_enc_cfg_t ldac_specific_config; +} __packed; + struct afe_enc_fmt_id_param_t { /* * Supported values: @@ -3447,6 +3491,7 @@ union afe_enc_config_data { struct asm_custom_enc_cfg_t custom_config; struct asm_celt_enc_cfg_t celt_config; struct asm_aptx_enc_cfg_t aptx_config; + struct asm_ldac_enc_cfg_t ldac_config; }; struct afe_enc_config { From fc7d71e196225f7cf25c6d9b5ac7f99e2b04d721 Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Fri, 20 Oct 2017 18:06:10 +0530 Subject: [PATCH 106/276] asoc: add multiple sampling rates support for a2dp Add support of 44.1Khz, 88.2Khz and 96Khz over split-a2dp path. CRs-Fixed: 2143086 Change-Id: I05e7e947bec250c509f647718323bd208c46b2b2 Signed-off-by: Preetam Singh Ranawat --- asoc/sdm845.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index ff19b116ac63..9cf7aeec62a1 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -404,7 +404,9 @@ 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 char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; @@ -984,7 +986,16 @@ static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, * value. */ switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: ucontrol->value.integer.value[0] = 2; break; case SAMPLING_RATE_16KHZ: @@ -1010,9 +1021,21 @@ static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; break; case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; case 0: default: slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; From 751329ee52293f507329119ab1e207ca0bc4dff9 Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Fri, 20 Oct 2017 18:13:14 +0530 Subject: [PATCH 107/276] dsp: add scrambler support in DSP over split a2dp Add support to enable scrambler in COP Packetizer module over split A2DP path to fix 44.1Khz playback. CRs-Fixed: 2143142 Change-Id: I52fec39e10a1c0939fd49d1e1303b03312544abf Signed-off-by: Preetam Singh Ranawat --- asoc/msm-dai-q6-v2.c | 37 +++++++++++++++++++++++++++++++++++++ dsp/q6afe.c | 28 +++++++++++++++++++++++----- include/dsp/apr_audio-v2.h | 20 ++++++++++++++++++++ 3 files changed, 80 insertions(+), 5 deletions(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 2ccab3fbb664..150b6d4eb687 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -2419,6 +2419,36 @@ static int msm_dai_q6_afe_input_bit_format_put( return 0; } +static int msm_dai_q6_afe_scrambler_mode_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; + } + ucontrol->value.integer.value[0] = dai_data->enc_config.scrambler_mode; + + return 0; +} + +static int msm_dai_q6_afe_scrambler_mode_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; + } + dai_data->enc_config.scrambler_mode = ucontrol->value.integer.value[0]; + pr_debug("%s: afe scrambler mode : %d\n", + __func__, dai_data->enc_config.scrambler_mode); + return 0; +} static const struct snd_kcontrol_new afe_enc_config_controls[] = { { @@ -2436,6 +2466,10 @@ static const struct snd_kcontrol_new afe_enc_config_controls[] = { 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), + SOC_SINGLE_EXT("AFE Scrambler Mode", + 0, 0, 1, 0, + msm_dai_q6_afe_scrambler_mode_get, + msm_dai_q6_afe_scrambler_mode_put), }; static int msm_dai_q6_slim_rx_drift_info(struct snd_kcontrol *kcontrol, @@ -2598,6 +2632,9 @@ static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) rc = snd_ctl_add(dai->component->card->snd_card, snd_ctl_new1(&afe_enc_config_controls[2], dai_data)); + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_enc_config_controls[3], + dai_data)); rc = snd_ctl_add(dai->component->card->snd_card, snd_ctl_new1(&avd_drift_config_controls[2], dai)); diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 435cfd7f383f..120d0ce71260 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -2856,7 +2856,8 @@ int afe_port_send_usb_dev_param(u16 port_id, union afe_port_config *afe_config) 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) + u16 afe_in_channels, u16 afe_in_bit_width, + u32 scrambler_mode) { struct afe_audioif_config_command config; int index; @@ -2953,6 +2954,20 @@ static int q6afe_send_enc_config(u16 port_id, goto exit; } + config.param.payload_size = + payload_size + sizeof(config.port.enc_set_scrambler_param); + pr_debug("%s:sending AFE_ENCODER_PARAM_ID_ENABLE_SCRAMBLING mode= %d to DSP payload = %d\n", + __func__, scrambler_mode, config.param.payload_size); + config.pdata.param_id = AFE_ENCODER_PARAM_ID_ENABLE_SCRAMBLING; + config.pdata.param_size = sizeof(config.port.enc_set_scrambler_param); + config.port.enc_set_scrambler_param.enable_scrambler = scrambler_mode; + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE_ENCODER_PARAM_ID_ENABLE_SCRAMBLING 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); @@ -2989,7 +3004,8 @@ static int q6afe_send_enc_config(u16 port_id, 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) + union afe_enc_config_data *cfg, u32 enc_format, + u32 scrambler_mode) { struct afe_audioif_config_command config; int ret = 0; @@ -3252,7 +3268,8 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config, __func__, enc_format); ret = q6afe_send_enc_config(port_id, cfg, enc_format, *afe_config, afe_in_channels, - afe_in_bit_width); + afe_in_bit_width, + scrambler_mode); if (ret) { pr_err("%s: AFE encoder config for port 0x%x failed %d\n", __func__, port_id, ret); @@ -3305,7 +3322,7 @@ 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); + 0, 0, NULL, ASM_MEDIA_FMT_NONE, 0); } EXPORT_SYMBOL(afe_port_start); @@ -3328,7 +3345,8 @@ int afe_port_start_v2(u16 port_id, union afe_port_config *afe_config, { return __afe_port_start(port_id, afe_config, rate, afe_in_channels, afe_in_bit_width, - &enc_cfg->data, enc_cfg->format); + &enc_cfg->data, enc_cfg->format, + enc_cfg->scrambler_mode); } EXPORT_SYMBOL(afe_port_start_v2); diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index ad0aa44a64bd..d01a5073ec5f 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -3118,6 +3118,12 @@ struct afe_param_id_aptx_sync_mode { */ #define AFE_ENCODER_PARAM_ID_ENC_FMT_ID 0x0001322B +/* + * Encoder scrambler parameter for the #AVS_MODULE_ID_ENCODER module. + * This parameter cannot be set runtime. + */ +#define AFE_ENCODER_PARAM_ID_ENABLE_SCRAMBLING 0x0001323C + /* * Data format to send compressed data * is transmitted/received over Slimbus lines. @@ -3451,6 +3457,7 @@ union afe_enc_config_data { struct afe_enc_config { u32 format; + u32 scrambler_mode; union afe_enc_config_data data; }; @@ -3474,6 +3481,18 @@ struct avs_enc_packetizer_id_param_t { uint32_t enc_packetizer_id; }; +/* + * Payload of the AVS_ENCODER_PARAM_ID_ENABLE_SCRAMBLING parameter. + */ +struct avs_enc_set_scrambler_param_t { + /* + * Supported values: + * 1 : enable scrambler + * 0 : disable scrambler + */ + uint32_t enable_scrambler; +}; + union afe_port_config { struct afe_param_id_pcm_cfg pcm; struct afe_param_id_i2s_cfg i2s; @@ -3492,6 +3511,7 @@ union afe_port_config { 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; + struct avs_enc_set_scrambler_param_t enc_set_scrambler_param; } __packed; struct afe_audioif_config_command_no_payload { From a8bc17b1e82537ee6f39066e9c0db439f8c0c86f Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Tue, 21 Nov 2017 19:52:26 +0530 Subject: [PATCH 108/276] dsp: add support for new AFE LSM cal types Add support for new AFE LSM cal types in order to avoid topology and cal block overriding during concurrent use cases like SVA with speaker protection. CRs-Fixed: 2085865 Change-Id: I9af5c34ea7ad9403b9e6d63ac5d3e73e708a320e Signed-off-by: Aditya Bavanari --- dsp/audio_cal_utils.c | 4 ++ dsp/q6afe.c | 55 ++++++++++++++++++---- include/uapi/linux/msm_audio_calibration.h | 5 ++ 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/dsp/audio_cal_utils.c b/dsp/audio_cal_utils.c index 9a9a96aade7d..27b81f2a9a76 100644 --- a/dsp/audio_cal_utils.c +++ b/dsp/audio_cal_utils.c @@ -77,6 +77,7 @@ size_t get_cal_info_size(int32_t cal_type) size = sizeof(struct audio_cal_info_audstrm); break; case AFE_TOPOLOGY_CAL_TYPE: + case AFE_LSM_TOPOLOGY_CAL_TYPE: size = sizeof(struct audio_cal_info_afe_top); break; case AFE_CUST_TOPOLOGY_CAL_TYPE: @@ -86,6 +87,7 @@ size_t get_cal_info_size(int32_t cal_type) size = sizeof(struct audio_cal_info_afe); break; case AFE_COMMON_TX_CAL_TYPE: + case AFE_LSM_TX_CAL_TYPE: size = sizeof(struct audio_cal_info_afe); break; case AFE_FB_SPKR_PROT_CAL_TYPE: @@ -223,6 +225,7 @@ size_t get_user_cal_type_size(int32_t cal_type) size = sizeof(struct audio_cal_type_audstrm); break; case AFE_TOPOLOGY_CAL_TYPE: + case AFE_LSM_TOPOLOGY_CAL_TYPE: size = sizeof(struct audio_cal_type_afe_top); break; case AFE_CUST_TOPOLOGY_CAL_TYPE: @@ -232,6 +235,7 @@ size_t get_user_cal_type_size(int32_t cal_type) size = sizeof(struct audio_cal_type_afe); break; case AFE_COMMON_TX_CAL_TYPE: + case AFE_LSM_TX_CAL_TYPE: size = sizeof(struct audio_cal_type_afe); break; case AFE_FB_SPKR_PROT_CAL_TYPE: diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 435cfd7f383f..8cb0527eabfd 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -30,12 +30,14 @@ enum { AFE_COMMON_RX_CAL = 0, AFE_COMMON_TX_CAL, + AFE_LSM_TX_CAL, AFE_AANC_CAL, AFE_FB_SPKR_PROT_CAL, AFE_HW_DELAY_CAL, AFE_SIDETONE_CAL, AFE_SIDETONE_IIR_CAL, AFE_TOPOLOGY_CAL, + AFE_LSM_TOPOLOGY_CAL, AFE_CUST_TOPOLOGY_CAL, AFE_FB_SPKR_PROT_TH_VI_CAL, AFE_FB_SPKR_PROT_EX_VI_CAL, @@ -1372,14 +1374,15 @@ static struct cal_block_data *afe_find_cal_topo_id_by_port( return NULL; } -static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id) +static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id, + int cal_type_index) { 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) { + if (this_afe.cal_data[cal_type_index] == NULL) { pr_err("%s: [AFE_TOPOLOGY_CAL] not initialized\n", __func__); return -EINVAL; } @@ -1389,9 +1392,9 @@ static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id) } *topology_id = 0; - mutex_lock(&this_afe.cal_data[AFE_TOPOLOGY_CAL]->lock); + mutex_lock(&this_afe.cal_data[cal_type_index]->lock); cal_block = afe_find_cal_topo_id_by_port( - this_afe.cal_data[AFE_TOPOLOGY_CAL], port_id); + this_afe.cal_data[cal_type_index], port_id); if (cal_block == NULL) { pr_err("%s: [AFE_TOPOLOGY_CAL] not initialized for this port %d\n", __func__, port_id); @@ -1413,7 +1416,7 @@ static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id) __func__, port_id, afe_top_info->acdb_id, afe_top_info->topology, ret); unlock: - mutex_unlock(&this_afe.cal_data[AFE_TOPOLOGY_CAL]->lock); + mutex_unlock(&this_afe.cal_data[cal_type_index]->lock); return ret; } @@ -1431,7 +1434,12 @@ static int afe_send_port_topology_id(u16 port_id) return -EINVAL; } - ret = afe_get_cal_topology_id(port_id, &topology_id); + ret = afe_get_cal_topology_id(port_id, &topology_id, AFE_TOPOLOGY_CAL); + if (ret < 0) { + pr_debug("%s: Check for LSM topology\n", __func__); + ret = afe_get_cal_topology_id(port_id, &topology_id, + AFE_LSM_TOPOLOGY_CAL); + } if (ret || !topology_id) { pr_debug("%s: AFE port[%d] get_cal_topology[%d] invalid!\n", __func__, port_id, topology_id); @@ -1548,7 +1556,7 @@ static struct cal_block_data *afe_find_cal(int cal_index, int port_id) return cal_block; } -static void send_afe_cal_type(int cal_index, int port_id) +static int send_afe_cal_type(int cal_index, int port_id) { struct cal_block_data *cal_block = NULL; int ret; @@ -1559,19 +1567,22 @@ static void send_afe_cal_type(int cal_index, int port_id) if (this_afe.cal_data[cal_index] == NULL) { pr_warn("%s: cal_index %d not allocated!\n", __func__, cal_index); + ret = -EINVAL; goto done; } if (afe_port_index < 0) { pr_err("%s: Error getting AFE port index %d\n", __func__, afe_port_index); + ret = -EINVAL; goto done; } mutex_lock(&this_afe.cal_data[cal_index]->lock); if (((cal_index == AFE_COMMON_RX_CAL) || - (cal_index == AFE_COMMON_TX_CAL)) && + (cal_index == AFE_COMMON_TX_CAL) || + (cal_index == AFE_LSM_TX_CAL)) && (this_afe.dev_acdb_id[afe_port_index] > 0)) cal_block = afe_find_cal(cal_index, port_id); else @@ -1580,6 +1591,7 @@ static void send_afe_cal_type(int cal_index, int port_id) if (cal_block == NULL) { pr_err("%s cal_block not found!!\n", __func__); + ret = -EINVAL; goto unlock; } @@ -1589,6 +1601,7 @@ static void send_afe_cal_type(int cal_index, int port_id) if (ret) { pr_err("%s: Remap_cal_data failed for cal %d!\n", __func__, cal_index); + ret = -EINVAL; goto unlock; } ret = afe_send_cal_block(port_id, cal_block); @@ -1598,16 +1611,20 @@ static void send_afe_cal_type(int cal_index, int port_id) unlock: mutex_unlock(&this_afe.cal_data[cal_index]->lock); done: - return; + return ret; } void afe_send_cal(u16 port_id) { + int ret; + 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); + ret = send_afe_cal_type(AFE_COMMON_TX_CAL, port_id); + if (ret < 0) + send_afe_cal_type(AFE_LSM_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); @@ -6569,6 +6586,9 @@ static int get_cal_type_index(int32_t cal_type) case AFE_COMMON_TX_CAL_TYPE: ret = AFE_COMMON_TX_CAL; break; + case AFE_LSM_TX_CAL_TYPE: + ret = AFE_LSM_TX_CAL; + break; case AFE_AANC_CAL_TYPE: ret = AFE_AANC_CAL; break; @@ -6587,6 +6607,9 @@ static int get_cal_type_index(int32_t cal_type) case AFE_TOPOLOGY_CAL_TYPE: ret = AFE_TOPOLOGY_CAL; break; + case AFE_LSM_TOPOLOGY_CAL_TYPE: + ret = AFE_LSM_TOPOLOGY_CAL; + break; case AFE_CUST_TOPOLOGY_CAL_TYPE: ret = AFE_CUST_TOPOLOGY_CAL; break; @@ -7089,6 +7112,12 @@ static int afe_init_cal_data(void) {afe_map_cal_data, afe_unmap_cal_data, cal_utils_match_buf_num} }, + {{AFE_LSM_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} }, @@ -7121,6 +7150,12 @@ static int afe_init_cal_data(void) {NULL, NULL, cal_utils_match_buf_num} }, + {{AFE_LSM_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} }, diff --git a/include/uapi/linux/msm_audio_calibration.h b/include/uapi/linux/msm_audio_calibration.h index 29fdf8f966e6..27dacde651f1 100644 --- a/include/uapi/linux/msm_audio_calibration.h +++ b/include/uapi/linux/msm_audio_calibration.h @@ -99,6 +99,8 @@ enum { AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE, AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE, AFE_SIDETONE_IIR_CAL_TYPE, + AFE_LSM_TOPOLOGY_CAL_TYPE, + AFE_LSM_TX_CAL_TYPE, MAX_CAL_TYPES, }; @@ -107,6 +109,9 @@ enum { #define AFE_SIDETONE_IIR_CAL_TYPE AFE_SIDETONE_IIR_CAL_TYPE +#define AFE_LSM_TOPOLOGY_CAL_TYPE AFE_LSM_TOPOLOGY_CAL_TYPE +#define AFE_LSM_TX_CAL_TYPE AFE_LSM_TX_CAL_TYPE + #define TOPOLOGY_SPECIFIC_CHANNEL_INFO #define MSM_SPKR_PROT_SPV3 From 6ba9d434d209b13282023e7cd9937f9cc7de419d Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 20 Nov 2017 14:11:24 -0800 Subject: [PATCH 109/276] asoc: remove deprecated dummy functions in DAP modules File "msm-ds2-dap-config.c" has corresponding dummy functions defined for all of its functions to avoid compilation error when config flags "CONFIG_DOLBY_LICENSE" and "CONFIG_DOLBY_DS2" are not selected for a target. However, this file will not get compiled when these two flags are not selected. Therefore, those dummy functions are useless and should be removed. Change-Id: Ic53a6a9d87d6fd56560af1aec3dc012d6486b42a Signed-off-by: Xiaoyu Ye --- asoc/msm-ds2-dap-config.c | 143 -------------------------------------- 1 file changed, 143 deletions(-) diff --git a/asoc/msm-ds2-dap-config.c b/asoc/msm-ds2-dap-config.c index 7381e5d8c362..645ecf5cfd2d 100644 --- a/asoc/msm-ds2-dap-config.c +++ b/asoc/msm-ds2-dap-config.c @@ -22,8 +22,6 @@ #include "msm-pcm-routing-v2.h" -#if defined(CONFIG_DOLBY_DS2) || defined(CONFIG_DOLBY_LICENSE) - /* ramp up/down for 30ms */ #define DOLBY_SOFT_VOLUME_PERIOD 40 /* Step value 0ms or 0us */ @@ -2174,144 +2172,3 @@ int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx, } 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 || CONFIG_DOLBY_LICENSE */ From 69f2f1b408800c6578cdb25df9da872926eee197 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 15 Nov 2017 11:04:02 +0800 Subject: [PATCH 110/276] ASoC: dsp: avoid free audio client handle twice ac(audio client handle) can be freed twice in q6asm_audio_client_alloc when some error happens. Adjust kfree to avoid kernel panic. Change-Id: If2985f710b8b841f471a4e926e8ca959541a4403 Signed-off-by: Meng Wang --- dsp/q6asm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 5272927d7929..75f668c9e9df 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -1217,6 +1217,7 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) if (n <= 0) { pr_err("%s: ASM Session alloc fail n=%d\n", __func__, n); mutex_unlock(&session_lock); + kfree(ac); goto fail_session; } ac->session = n; @@ -1293,7 +1294,6 @@ struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv) fail_apr1: q6asm_session_free(ac); fail_session: - kfree(ac); return NULL; } From 328f6b49d9df08f6daee2449d1ba8623c705aaa4 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Wed, 22 Nov 2017 11:38:29 -0800 Subject: [PATCH 111/276] dsp: unlock the mutex before return to avoid potential deadlock Function "q6asm_set_shared_circ_buff" has a potential scenario that can cause deadlock due to a missing call to unlock mutex before return. Avoid this potential deadlock by calling the mutex unlock just before the return of function "q6asm_set_shared_circ_buff". Change-Id: Ic454eb5eb57c7616d891277c09e0e3bd37e63cd2 Signed-off-by: Xiaoyu Ye --- dsp/q6asm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 5272927d7929..63aba6e8a02e 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -3411,7 +3411,6 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac, if (ac->port[dir].buf) { pr_err("%s: Buffer already allocated\n", __func__); rc = -EINVAL; - mutex_unlock(&ac->cmd_lock); goto done; } @@ -3434,7 +3433,6 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac, pr_err("%s: Audio ION alloc is failed, rc = %d\n", __func__, rc); kfree(buf_circ); - mutex_unlock(&ac->cmd_lock); goto done; } @@ -3460,8 +3458,8 @@ int q6asm_set_shared_circ_buff(struct audio_client *ac, msm_audio_populate_upper_32_bits(buf_circ->phys); open->map_region_circ_buf.mem_size_bytes = bytes_to_alloc; - mutex_unlock(&ac->cmd_lock); done: + mutex_unlock(&ac->cmd_lock); return rc; } From a5d504a31b7a9e4786d0834ad3f1f9d3735b0a3f Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Wed, 22 Nov 2017 14:15:57 -0800 Subject: [PATCH 112/276] autoconf: sdm845: remove unused configs Remove duplicates and unused configs from the config header file. CRs-fixed: 2147443 Change-Id: I10735b0a344e575591c128bd9ca74850c068023d Signed-off-by: Karthikeyan Mani --- config/sdm845autoconf.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/config/sdm845autoconf.h b/config/sdm845autoconf.h index 5bd70f614fa6..fcef4c13e776 100644 --- a/config/sdm845autoconf.h +++ b/config/sdm845autoconf.h @@ -13,14 +13,11 @@ #define CONFIG_PINCTRL_WCD 1 #define CONFIG_SND_SOC_WCD934X 1 -#define CONFIG_AUDIO_EXT_CLK 1 #define CONFIG_SND_SOC_WCD9XXX_V2 1 #define CONFIG_SND_SOC_WCD_CPE 1 #define CONFIG_SND_SOC_WCD_MBHC 1 #define CONFIG_SND_SOC_WSA881X 1 -#define CONFIG_SND_SOC_WCD_DSP_MGR 1 #define CONFIG_SND_SOC_WCD_SPI 1 -#define CONFIG_SND_SOC_WCD934X 1 #define CONFIG_SND_SOC_WCD934X_MBHC 1 #define CONFIG_SND_SOC_WCD934X_DSD 1 #define CONFIG_MSM_QDSP6V2_CODECS 1 @@ -33,14 +30,12 @@ #define CONFIG_MSM_QDSP6_PDR 1 #define CONFIG_MSM_QDSP6_NOTIFIER 1 #define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 -#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 #define CONFIG_SND_SOC_SDM845 1 #define CONFIG_MSM_GLINK_SPI_XPRT 1 #define CONFIG_SOUNDWIRE 1 #define CONFIG_SOUNDWIRE_WCD_CTRL 1 #define CONFIG_SND_SOC_WCD_MBHC_ADC 1 #define CONFIG_SND_SOC_QDSP6V2 1 -#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 #define CONFIG_MSM_CDC_PINCTRL 1 #define CONFIG_QTI_PP 1 #define CONFIG_SND_HWDEP 1 From 55bb7d4e8a95f01be65872601e02a8785f0fd332 Mon Sep 17 00:00:00 2001 From: Banajit Goswami Date: Thu, 16 Nov 2017 18:40:14 -0800 Subject: [PATCH 113/276] asoc: sdm845: check sound card status before sending AFE config WCD codec must be enumerated before AFE configuration for some of the features can be sent to DSP. This is because as part of setting up this configuration, DSP tries to access some codec register. These register accesses will fail if codec is not enumerated already. Add a check for Sound Card online state to confirm that WCD codec has already enumerated before sending AFE configs. Change-Id: I0b7e5eb49e4f6c6aee9cb142054e57eae592602b Signed-off-by: Banajit Goswami --- asoc/sdm845.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index ff19b116ac63..58f417cc5bc4 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -3630,31 +3630,44 @@ static void msm_afe_clear_config(void) afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); } -static int msm_adsp_power_up_config(struct snd_soc_codec *codec) +static int msm_adsp_power_up_config(struct snd_soc_codec *codec, + struct snd_card *card) { int ret = 0; unsigned long timeout; int adsp_ready = 0; + bool snd_card_online = 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; + if (!snd_card_online) { + snd_card_online = snd_card_is_online_state(card); + pr_debug("%s: Sound card is %s\n", __func__, + snd_card_online ? "Online" : "Offline"); + } + if (!adsp_ready) { + adsp_ready = q6core_is_adsp_ready(); + pr_debug("%s: ADSP Audio is %s\n", __func__, + adsp_ready ? "ready" : "not ready"); } + if (snd_card_online && adsp_ready) + break; + /* - * ADSP will be coming up after subsystem restart and + * Sound card/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__); + if (!snd_card_online || !adsp_ready) { + pr_err("%s: Timeout. Sound card is %s, ADSP Audio is %s\n", + __func__, + snd_card_online ? "Online" : "Offline", + adsp_ready ? "ready" : "not ready"); ret = -ETIMEDOUT; goto err; } @@ -3712,7 +3725,7 @@ static int sdm845_notifier_service_cb(struct notifier_block *this, } codec = rtd->codec; - ret = msm_adsp_power_up_config(codec); + ret = msm_adsp_power_up_config(codec, card->snd_card); if (ret < 0) { dev_err(card->dev, "%s: msm_adsp_power_up_config failed ret = %d!\n", @@ -3810,7 +3823,7 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) msm_codec_fn.get_afe_config_fn = tavil_get_afe_config; - ret = msm_adsp_power_up_config(codec); + ret = msm_adsp_power_up_config(codec, rtd->card->snd_card); if (ret) { pr_err("%s: Failed to set AFE config %d\n", __func__, ret); goto err; @@ -7058,6 +7071,7 @@ static int msm_asoc_machine_remove(struct platform_device *pdev) struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); + audio_notifier_deregister("sdm845"); if (pdata->us_euro_gpio > 0) { gpio_free(pdata->us_euro_gpio); pdata->us_euro_gpio = 0; @@ -7066,7 +7080,6 @@ static int msm_asoc_machine_remove(struct platform_device *pdev) msm_release_pinctrl(pdev); snd_soc_unregister_card(card); - audio_notifier_deregister("sdm845"); return 0; } From 2b0214cec2389d1e7470276646e0deade79d3445 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Fri, 17 Nov 2017 09:35:04 +0800 Subject: [PATCH 114/276] ASoC: dsp: get session_id from audio client There is a possible use after free for the ac variable that causes kernel panic. Add new function q6asm_get_session_id_from_audio_client to get session_id to help acquire spin_lock to avoid this issue. Change-Id: I7d1dc47d77c01b19a931bd154a8771ba789983cf Signed-off-by: Meng Wang --- dsp/q6asm.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 5272927d7929..2f78e20a21fe 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -544,17 +544,22 @@ static int q6asm_session_alloc(struct audio_client *ac) return -ENOMEM; } -static bool q6asm_is_valid_audio_client(struct audio_client *ac) +static int q6asm_get_session_id_from_audio_client(struct audio_client *ac) { int n; for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) { if (session[n].ac == ac) - return 1; + return n; } return 0; } +static bool q6asm_is_valid_audio_client(struct audio_client *ac) +{ + return q6asm_get_session_id_from_audio_client(ac) ? 1 : 0; +} + static void q6asm_session_free(struct audio_client *ac) { int session_id; @@ -1747,10 +1752,10 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) return -EINVAL; } - session_id = ac->session; - if (ac->session <= 0 || ac->session > ASM_ACTIVE_STREAMS_ALLOWED) { + session_id = q6asm_get_session_id_from_audio_client(ac); + if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) { pr_err("%s: Session ID is invalid, session = %d\n", __func__, - ac->session); + session_id); return -EINVAL; } spin_lock(&(session[session_id].session_lock)); From a5774326b0054994fbf705820b7a617648d9a8fa Mon Sep 17 00:00:00 2001 From: Revathi Uddaraju Date: Tue, 21 Nov 2017 15:30:19 +0530 Subject: [PATCH 115/276] sdm670: Add mixer control and update affine qos value Add mixer control for MultiMedia5_RX QOS Vote and update affine qos value. Change-Id: Id027ea4c708d0827a24ca655d0f1810331100cee Signed-off-by: Revathi Uddaraju --- asoc/sdm660-common.c | 61 +++++++++++++++++++++++++++++++++++++ asoc/sdm660-ext-dai-links.c | 28 +++++++++++++++++ asoc/sdm660-internal.c | 29 +++++++++++++++++- 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c index 1874cfa02860..449664287303 100644 --- a/asoc/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -26,6 +26,9 @@ #include "codecs/sdm660_cdc/msm-analog-cdc.h" #include "codecs/wsa881x.h" +#define __CHIPSET__ "SDM660 " +#define MSM_DAILINK_NAME(name) (__CHIPSET__#name) + #define DRV_NAME "sdm660-asoc-snd" #define MSM_INT_DIGITAL_CODEC "msm-dig-codec" @@ -33,6 +36,7 @@ #define DEV_NAME_STR_LEN 32 #define DEFAULT_MCLK_RATE 9600000 +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ struct dev_config { u32 sample_rate; @@ -303,6 +307,7 @@ static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025", static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE"}; static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96", "KHZ_192"}; +static const char *const qos_text[] = {"Disable", "Enable"}; static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text); static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text); @@ -361,6 +366,9 @@ 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(qos_vote, qos_text); + +static int qos_vote_status; static struct afe_clk_set mi2s_clk[MI2S_MAX] = { { @@ -1877,6 +1885,57 @@ static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol, return 0; } +static int msm_qos_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = qos_vote_status; + + return 0; +} + +static int msm_qos_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct snd_soc_card *card = codec->component.card; + const char *fe_name = MSM_DAILINK_NAME(LowLatency); + struct snd_soc_pcm_runtime *rtd; + struct snd_pcm_substream *substream; + s32 usecs; + + rtd = snd_soc_get_pcm_runtime(card, fe_name); + if (!rtd) { + pr_err("%s: fail to get pcm runtime for %s\n", + __func__, fe_name); + return -EINVAL; + } + + substream = rtd->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s: substream is null\n", __func__); + return -EINVAL; + } + + qos_vote_status = ucontrol->value.enumerated.item[0]; + if (qos_vote_status) { + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + if (!substream->runtime) { + pr_err("%s: runtime is null\n", __func__); + return -EINVAL; + } + usecs = MSM_LL_QOS_VALUE; + if (usecs >= 0) + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, usecs); + } else { + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + } + + return 0; +} + 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), @@ -2101,6 +2160,8 @@ const struct snd_kcontrol_new msm_common_snd_controls[] = { SOC_ENUM_EXT("QUIN_TDM_TX_0 Channels", tdm_tx_chs, tdm_tx_ch_get, tdm_tx_ch_put), + SOC_ENUM_EXT("MultiMedia5_RX QOS Vote", qos_vote, msm_qos_ctl_get, + msm_qos_ctl_put), }; /** diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c index a33113550f4c..8b107471a739 100644 --- a/asoc/sdm660-ext-dai-links.c +++ b/asoc/sdm660-ext-dai-links.c @@ -21,6 +21,7 @@ #include "sdm660-external.h" #include "codecs/core.h" #include "codecs/wcd9335.h" +#include #define DEV_NAME_STR_LEN 32 #define __CHIPSET__ "SDM660 " @@ -28,6 +29,7 @@ #define WCN_CDC_SLIM_RX_CH_MAX 2 #define WCN_CDC_SLIM_TX_CH_MAX 3 +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ static struct snd_soc_card snd_soc_card_msm_card_tavil = { .name = "sdm670-tavil-snd-card", @@ -279,6 +281,30 @@ static struct snd_soc_ops msm_tdm_be_ops = { .hw_params = msm_tdm_snd_hw_params }; +static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) +{ + cpumask_t mask; + + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); /* affine to core 1 */ + cpumask_set_cpu(2, &mask); /* affine to core 2 */ + cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask); + + substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + MSM_LL_QOS_VALUE); + return 0; +} + +static struct snd_soc_ops msm_fe_qos_ops = { + .prepare = msm_fe_qos_prepare, +}; + static struct snd_soc_dai_link msm_ext_tasha_fe_dai[] = { /* tasha_vifeedback for speaker protection */ { @@ -963,6 +989,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { /* this dai link has playback support */ .ignore_pmdown_time = 1, .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, }, /* LSM FE */ {/* hw:x,14 */ @@ -1032,6 +1059,7 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .ignore_pmdown_time = 1, /* this dai link has playback support */ .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, }, {/* hw:x,18 */ .name = "HDMI_RX_HOSTLESS", diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c index a1536fefe635..7f6d36073b29 100644 --- a/asoc/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -20,6 +20,7 @@ #include "codecs/sdm660_cdc/msm-digital-cdc.h" #include "codecs/sdm660_cdc/msm-analog-cdc.h" #include "codecs/msm_sdw/msm_sdw.h" +#include #define __CHIPSET__ "SDM660 " #define MSM_DAILINK_NAME(name) (__CHIPSET__#name) @@ -34,7 +35,7 @@ #define WSA8810_NAME_1 "wsa881x.20170211" #define WSA8810_NAME_2 "wsa881x.20170212" - +#define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ enum { INT0_MI2S = 0, INT1_MI2S, @@ -1627,6 +1628,30 @@ static struct snd_soc_ops msm_sdw_mi2s_be_ops = { .shutdown = msm_sdw_mi2s_snd_shutdown, }; +static int msm_fe_qos_prepare(struct snd_pcm_substream *substream) +{ + cpumask_t mask; + + if (pm_qos_request_active(&substream->latency_pm_qos_req)) + pm_qos_remove_request(&substream->latency_pm_qos_req); + + cpumask_clear(&mask); + cpumask_set_cpu(1, &mask); /* affine to core 1 */ + cpumask_set_cpu(2, &mask); /* affine to core 2 */ + cpumask_copy(&substream->latency_pm_qos_req.cpus_affine, &mask); + + substream->latency_pm_qos_req.type = PM_QOS_REQ_AFFINE_CORES; + + pm_qos_add_request(&substream->latency_pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, + MSM_LL_QOS_VALUE); + return 0; +} + +static struct snd_soc_ops msm_fe_qos_ops = { + .prepare = msm_fe_qos_prepare, +}; + struct snd_soc_dai_link_component dlc_rx1[] = { { .of_node = NULL, @@ -1883,6 +1908,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { /* this dai link has playback support */ .ignore_pmdown_time = 1, .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + .ops = &msm_fe_qos_ops, }, /* LSM FE */ {/* hw:x,14 */ @@ -1950,6 +1976,7 @@ static struct snd_soc_dai_link msm_int_dai[] = { .ignore_pmdown_time = 1, /* this dai link has playback support */ .id = MSM_FRONTEND_DAI_MULTIMEDIA8, + .ops = &msm_fe_qos_ops, }, {/* hw:x,18 */ .name = "HDMI_RX_HOSTLESS", From b11ef7166178a54db90854286ee0fd7805fa1638 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Tue, 21 Nov 2017 20:24:53 +0530 Subject: [PATCH 116/276] dsp: add support for new ADM LSM cal types Add support for new ADM LSM cal types in order to avoid topology and cal block overriding during concurrent use cases like SVA with speaker protection. CRs-Fixed: 2085865 Change-Id: I155d29ff0b77b069aa5970408332064c5c2aebd7 Signed-off-by: Aditya Bavanari --- asoc/msm-pcm-routing-v2.c | 101 +++++++++++++++------ asoc/msm-pcm-routing-v2.h | 6 ++ dsp/audio_cal_utils.c | 4 + dsp/q6adm.c | 34 +++++-- include/dsp/q6adm-v2.h | 2 + include/uapi/linux/msm_audio_calibration.h | 5 + 6 files changed, 116 insertions(+), 36 deletions(-) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index c17aaef6aca5..2d39681d1a0e 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -57,7 +57,7 @@ static struct mutex routing_lock; -static struct cal_type_data *cal_data; +static struct cal_type_data *cal_data[MAX_ROUTING_CAL_TYPES]; static int fm_switch_enable; static int hfp_switch_enable; @@ -883,21 +883,21 @@ int msm_pcm_routing_get_stream_app_type_cfg( } EXPORT_SYMBOL(msm_pcm_routing_get_stream_app_type_cfg); -static struct cal_block_data *msm_routing_find_topology_by_path(int path) +static struct cal_block_data *msm_routing_find_topology_by_path(int path, + int cal_index) { - struct list_head *ptr, *next; - struct cal_block_data *cal_block = NULL; - + 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_data[cal_index]->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) { + if (((struct audio_cal_info_adm_top *)cal_block + ->cal_info)->path == path) { return cal_block; } } @@ -907,7 +907,8 @@ static struct cal_block_data *msm_routing_find_topology_by_path(int path) static struct cal_block_data *msm_routing_find_topology(int path, int app_type, - int acdb_id) + int acdb_id, + int cal_index) { struct list_head *ptr, *next; struct cal_block_data *cal_block = NULL; @@ -916,7 +917,7 @@ static struct cal_block_data *msm_routing_find_topology(int path, pr_debug("%s\n", __func__); list_for_each_safe(ptr, next, - &cal_data->cal_blocks) { + &cal_data[cal_index]->cal_blocks) { cal_block = list_entry(ptr, struct cal_block_data, list); @@ -931,7 +932,7 @@ static struct cal_block_data *msm_routing_find_topology(int path, } pr_debug("%s: Can't find topology for path %d, app %d, acdb_id %d defaulting to search by path\n", __func__, path, app_type, acdb_id); - return msm_routing_find_topology_by_path(path); + return msm_routing_find_topology_by_path(cal_index, path); } static int msm_routing_get_adm_topology(int fedai_id, int session_type, @@ -941,28 +942,37 @@ static int msm_routing_get_adm_topology(int fedai_id, int session_type, struct cal_block_data *cal_block = NULL; int app_type = 0, acdb_dev_id = 0; - pr_debug("%s: fedai_id %d, session_type %d, be_id %d\n", __func__, fedai_id, session_type, be_id); if (cal_data == NULL) goto done; - mutex_lock(&cal_data->lock); - app_type = fe_dai_app_type_cfg[fedai_id][session_type][be_id].app_type; acdb_dev_id = fe_dai_app_type_cfg[fedai_id][session_type][be_id].acdb_dev_id; + mutex_lock(&cal_data[ADM_TOPOLOGY_CAL_TYPE_IDX]->lock); cal_block = msm_routing_find_topology(session_type, app_type, - acdb_dev_id); - if (cal_block == NULL) - goto unlock; - - topology = ((struct audio_cal_info_adm_top *) - cal_block->cal_info)->topology; -unlock: - mutex_unlock(&cal_data->lock); + acdb_dev_id, + ADM_TOPOLOGY_CAL_TYPE_IDX); + if (cal_block != NULL) + topology = ((struct audio_cal_info_adm_top *) + cal_block->cal_info)->topology; + mutex_unlock(&cal_data[ADM_TOPOLOGY_CAL_TYPE_IDX]->lock); + + if (cal_block == NULL) { + pr_debug("%s: Check for LSM topology\n", __func__); + mutex_lock(&cal_data[ADM_LSM_TOPOLOGY_CAL_TYPE_IDX]->lock); + cal_block = msm_routing_find_topology(session_type, app_type, + acdb_dev_id, + ADM_LSM_TOPOLOGY_CAL_TYPE_IDX); + if (cal_block != NULL) + topology = ((struct audio_cal_info_adm_top *) + cal_block->cal_info)->topology; + mutex_unlock(&cal_data[ADM_LSM_TOPOLOGY_CAL_TYPE_IDX]->lock); + } + done: pr_debug("%s: Using topology %d\n", __func__, topology); return topology; @@ -2562,6 +2572,7 @@ static int msm_routing_lsm_func_put(struct snd_kcontrol *kcontrol, pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, mad_type); + adm_set_lsm_port_id(port_id); return afe_port_set_mad_type(port_id, mad_type); } @@ -17156,14 +17167,39 @@ int msm_routing_check_backend_enabled(int fedai_id) return 0; } +static int get_cal_type_index(int32_t cal_type) +{ + int ret = -EINVAL; + + switch (cal_type) { + case ADM_TOPOLOGY_CAL_TYPE: + ret = ADM_TOPOLOGY_CAL_TYPE_IDX; + break; + case ADM_LSM_TOPOLOGY_CAL_TYPE: + ret = ADM_LSM_TOPOLOGY_CAL_TYPE_IDX; + break; + default: + pr_err("%s: Invalid cal type %d\n", __func__, cal_type); + } + return ret; +} + static int msm_routing_set_cal(int32_t cal_type, size_t data_size, void *data) { int ret = 0; - + int cal_index; pr_debug("%s\n", __func__); - ret = cal_utils_set_cal(data_size, data, cal_data, 0, NULL); + 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); @@ -17178,22 +17214,27 @@ static void msm_routing_delete_cal_data(void) { pr_debug("%s\n", __func__); - cal_utils_destroy_cal_types(1, &cal_data); + cal_utils_destroy_cal_types(MAX_ROUTING_CAL_TYPES, &cal_data[0]); } static int msm_routing_init_cal_data(void) { int ret = 0; - struct cal_type_info cal_type_info = { - {ADM_TOPOLOGY_CAL_TYPE, + 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} }, + + {{ADM_LSM_TOPOLOGY_CAL_TYPE, {NULL, NULL, NULL, msm_routing_set_cal, NULL, NULL} }, - {NULL, NULL, cal_utils_match_buf_num} + {NULL, NULL, cal_utils_match_buf_num} }, }; pr_debug("%s\n", __func__); - ret = cal_utils_create_cal_types(1, &cal_data, - &cal_type_info); + ret = cal_utils_create_cal_types(MAX_ROUTING_CAL_TYPES, &cal_data[0], + &cal_type_info[0]); if (ret < 0) { pr_err("%s: could not create cal type!\n", __func__); diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index 5360b5b5f770..a1977b4b3737 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -433,6 +433,12 @@ enum { #define BE_DAI_PORT_SESSIONS_IDX_MAX 4 #define BE_DAI_FE_SESSIONS_IDX_MAX 2 +enum { + ADM_TOPOLOGY_CAL_TYPE_IDX = 0, + ADM_LSM_TOPOLOGY_CAL_TYPE_IDX, + MAX_ROUTING_CAL_TYPES +}; + struct msm_pcm_routing_evt { void (*event_func)(enum msm_pcm_routing_event, void *); void *priv_data; diff --git a/dsp/audio_cal_utils.c b/dsp/audio_cal_utils.c index 27b81f2a9a76..f6b66d3ce5a7 100644 --- a/dsp/audio_cal_utils.c +++ b/dsp/audio_cal_utils.c @@ -54,6 +54,7 @@ size_t get_cal_info_size(int32_t cal_type) size = sizeof(struct audio_cal_info_voc_col); break; case ADM_TOPOLOGY_CAL_TYPE: + case ADM_LSM_TOPOLOGY_CAL_TYPE: size = sizeof(struct audio_cal_info_adm_top); break; case ADM_CUST_TOPOLOGY_CAL_TYPE: @@ -61,6 +62,7 @@ size_t get_cal_info_size(int32_t cal_type) size = 0; break; case ADM_AUDPROC_CAL_TYPE: + case ADM_LSM_AUDPROC_CAL_TYPE: size = sizeof(struct audio_cal_info_audproc); break; case ADM_AUDVOL_CAL_TYPE: @@ -202,6 +204,7 @@ size_t get_user_cal_type_size(int32_t cal_type) size = sizeof(struct audio_cal_type_voc_col); break; case ADM_TOPOLOGY_CAL_TYPE: + case ADM_LSM_TOPOLOGY_CAL_TYPE: size = sizeof(struct audio_cal_type_adm_top); break; case ADM_CUST_TOPOLOGY_CAL_TYPE: @@ -209,6 +212,7 @@ size_t get_user_cal_type_size(int32_t cal_type) size = sizeof(struct audio_cal_type_basic); break; case ADM_AUDPROC_CAL_TYPE: + case ADM_LSM_AUDPROC_CAL_TYPE: size = sizeof(struct audio_cal_type_audproc); break; case ADM_AUDVOL_CAL_TYPE: diff --git a/dsp/q6adm.c b/dsp/q6adm.c index 461e155d4176..fd8b89a4ec46 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -104,6 +104,7 @@ struct adm_ctl { int num_ec_ref_rx_chans; int ec_ref_rx_bit_width; int ec_ref_rx_sampling_rate; + int lsm_port_id; }; static struct adm_ctl this_adm; @@ -2008,7 +2009,8 @@ static struct cal_block_data *adm_find_cal_by_path(int cal_index, int path) cal_block = list_entry(ptr, struct cal_block_data, list); - if (cal_index == ADM_AUDPROC_CAL) { + if (cal_index == ADM_AUDPROC_CAL || + cal_index == ADM_LSM_AUDPROC_CAL) { audproc_cal_info = cal_block->cal_info; if ((audproc_cal_info->path == path) && (cal_block->cal_data.size > 0)) @@ -2041,7 +2043,8 @@ static struct cal_block_data *adm_find_cal_by_app_type(int cal_index, int path, cal_block = list_entry(ptr, struct cal_block_data, list); - if (cal_index == ADM_AUDPROC_CAL) { + if (cal_index == ADM_AUDPROC_CAL || + cal_index == ADM_LSM_AUDPROC_CAL) { audproc_cal_info = cal_block->cal_info; if ((audproc_cal_info->path == path) && (audproc_cal_info->app_type == app_type) && @@ -2078,7 +2081,8 @@ static struct cal_block_data *adm_find_cal(int cal_index, int path, cal_block = list_entry(ptr, struct cal_block_data, list); - if (cal_index == ADM_AUDPROC_CAL) { + if (cal_index == ADM_AUDPROC_CAL || + cal_index == ADM_LSM_AUDPROC_CAL) { audproc_cal_info = cal_block->cal_info; if ((audproc_cal_info->path == path) && (audproc_cal_info->app_type == app_type) && @@ -2159,13 +2163,22 @@ static int get_cal_path(int path) return TX_DEVICE; } +void adm_set_lsm_port_id(int port_id) +{ + this_adm.lsm_port_id = port_id; +} + 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); + if (port_id != this_adm.lsm_port_id) + send_adm_cal_type(ADM_AUDPROC_CAL, path, port_id, copp_idx, + perf_mode, app_type, acdb_id, sample_rate); + else + send_adm_cal_type(ADM_LSM_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); } @@ -3226,6 +3239,9 @@ static int get_cal_type_index(int32_t cal_type) case ADM_AUDPROC_CAL_TYPE: ret = ADM_AUDPROC_CAL; break; + case ADM_LSM_AUDPROC_CAL_TYPE: + ret = ADM_LSM_AUDPROC_CAL; + break; case ADM_AUDVOL_CAL_TYPE: ret = ADM_AUDVOL_CAL; break; @@ -3433,6 +3449,12 @@ static int adm_init_cal_data(void) {adm_map_cal_data, adm_unmap_cal_data, cal_utils_match_buf_num} }, + {{ADM_LSM_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} }, @@ -4132,7 +4154,7 @@ int adm_store_cal_data(int port_id, int copp_idx, int path, int perf_mode, goto unlock; } - if (cal_index == ADM_AUDPROC_CAL) { + if (cal_index == ADM_AUDPROC_CAL || cal_index == ADM_LSM_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); diff --git a/include/dsp/q6adm-v2.h b/include/dsp/q6adm-v2.h index 013d5229cf08..6d1f4f69c5a4 100644 --- a/include/dsp/q6adm-v2.h +++ b/include/dsp/q6adm-v2.h @@ -32,6 +32,7 @@ enum { ADM_CUSTOM_TOP_CAL = 0, ADM_AUDPROC_CAL, + ADM_LSM_AUDPROC_CAL, ADM_AUDVOL_CAL, ADM_RTAC_INFO_CAL, ADM_RTAC_APR_CAL, @@ -184,4 +185,5 @@ int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id, int session_type, struct msm_pcm_channel_mixer *ch_mixer, int channel_index); +void adm_set_lsm_port_id(int port_id); #endif /* __Q6_ADM_V2_H__ */ diff --git a/include/uapi/linux/msm_audio_calibration.h b/include/uapi/linux/msm_audio_calibration.h index 27dacde651f1..3b97ab2bf1e0 100644 --- a/include/uapi/linux/msm_audio_calibration.h +++ b/include/uapi/linux/msm_audio_calibration.h @@ -101,6 +101,8 @@ enum { AFE_SIDETONE_IIR_CAL_TYPE, AFE_LSM_TOPOLOGY_CAL_TYPE, AFE_LSM_TX_CAL_TYPE, + ADM_LSM_TOPOLOGY_CAL_TYPE, + ADM_LSM_AUDPROC_CAL_TYPE, MAX_CAL_TYPES, }; @@ -111,6 +113,9 @@ enum { #define AFE_LSM_TOPOLOGY_CAL_TYPE AFE_LSM_TOPOLOGY_CAL_TYPE #define AFE_LSM_TX_CAL_TYPE AFE_LSM_TX_CAL_TYPE +#define ADM_LSM_TOPOLOGY_CAL_TYPE ADM_LSM_TOPOLOGY_CAL_TYPE +#define ADM_LSM_AUDPROC_CAL_TYPE ADM_LSM_AUDPROC_CAL_TYPE +#define LSM_CAL_TYPES #define TOPOLOGY_SPECIFIC_CHANNEL_INFO #define MSM_SPKR_PROT_SPV3 From d5231fa166521a32621c32fb749b80fc37c13c6a Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Wed, 22 Nov 2017 15:29:52 +0530 Subject: [PATCH 117/276] ipc: add validity check to APR port Add boundary checks for APR port received from ADSP. CRs-Fixed: 2143207 Change-Id: I9a7fa39ee223e1859323caa6eb74c1c8a26a041d Signed-off-by: Aditya Bavanari --- ipc/apr.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ipc/apr.c b/ipc/apr.c index 1ddd8b9a7a62..02fddef4ad1d 100644 --- a/ipc/apr.c +++ b/ipc/apr.c @@ -679,9 +679,10 @@ void apr_cb_func(void *buf, int len, void *priv) } 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]); + if (((temp_port >= 0) && (temp_port < APR_MAX_PORTS)) + && (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 From 5c81772d824e6c4857ee1aa625939adcbec1afec Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 2 Oct 2017 18:47:29 -0700 Subject: [PATCH 118/276] ASoC: sdxpoorwills: add machine driver for audio support Add ALSA based machine driver for SDXPOORWILLS targets to provide initial audio support. Change-Id: Ie13dfa086711863ee7e0f297dfb2d75dc8cf781f Signed-off-by: Xiaoyu Ye --- asoc/Makefile | 4 + asoc/sdxpoorwills.c | 2499 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 2503 insertions(+) create mode 100644 asoc/sdxpoorwills.c diff --git a/asoc/Makefile b/asoc/Makefile index 4d3839de9556..78f8158f6c86 100644 --- a/asoc/Makefile +++ b/asoc/Makefile @@ -23,6 +23,10 @@ obj-$(CONFIG_SND_SOC_SDM670) += snd-soc-sdm670.o snd-soc-sdm845-objs := sdm845.o obj-$(CONFIG_SND_SOC_MACHINE_SDM845) += snd-soc-sdm845.o +# for SDXPOORWILLS sound card driver +snd-soc-sdxpoorwills-objs := sdxpoorwills.o +obj-$(CONFIG_SND_SOC_MACHINE_SDXPOORWILLS) += snd-soc-sdxpoorwills.o + 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-pcm-afe-v2.o msm-pcm-voip-v2.o \ diff --git a/asoc/sdxpoorwills.c b/asoc/sdxpoorwills.c new file mode 100644 index 000000000000..b7b07d7d6624 --- /dev/null +++ b/asoc/sdxpoorwills.c @@ -0,0 +1,2499 @@ +/* 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" +#include "codecs/msm-cdc-pinctrl.h" +#include "codecs/wcd934x/wcd934x.h" +#include "codecs/wcd934x/wcd934x-mbhc.h" +#include "codecs/wsa881x.h" + +/* Machine driver Name */ +#define DRV_NAME "sdx-asoc-tavil" + +#define __CHIPSET__ "SDX " +#define SDX_DAILINK_NAME(name) (__CHIPSET__#name) + +#define SAMPLE_RATE_8KHZ 8000 +#define SAMPLE_RATE_16KHZ 16000 +#define SAMPLE_RATE_48KHZ 48000 +#define NUM_OF_BITS_PER_SAMPLE 16 +#define DEV_NAME_STR_LEN 32 + +#define LPAIF_OFFSET 0x07700000 +#define LPAIF_PRI_MODE_MUXSEL (LPAIF_OFFSET + 0x2008) +#define LPAIF_SEC_MODE_MUXSEL (LPAIF_OFFSET + 0x200c) +#define LPASS_CSR_GP_IO_MUX_SPKR_CTL (LPAIF_OFFSET + 0x2004) +#define LPASS_CSR_GP_IO_MUX_MIC_CTL (LPAIF_OFFSET + 0x2000) + +#define I2S_SEL 0 +#define PCM_SEL 1 +#define I2S_PCM_SEL_OFFSET 0 +#define I2S_PCM_MASTER_MODE 1 +#define I2S_PCM_SLAVE_MODE 0 + +#define PRI_TLMM_CLKS_EN_MASTER 0x4 +#define SEC_TLMM_CLKS_EN_MASTER 0x2 +#define PRI_TLMM_CLKS_EN_SLAVE 0x100000 +#define SEC_TLMM_CLKS_EN_SLAVE 0x800000 +#define CLOCK_ON 1 +#define CLOCK_OFF 0 + +#define WCD9XXX_MBHC_DEF_BUTTONS 8 +#define WCD9XXX_MBHC_DEF_RLOADS 5 + +/* Spk control */ +#define SDX_SPK_ON 1 +#define SDX_HIFI_ON 1 + +enum mi2s_types { + PRI_MI2S, + SEC_MI2S, +}; + +struct sdx_machine_data { + u32 mclk_freq; + u16 prim_mi2s_mode; + u16 sec_mi2s_mode; + u16 prim_auxpcm_mode; + struct device_node *prim_master_p; + struct device_node *prim_slave_p; + u16 sec_auxpcm_mode; + struct device_node *sec_master_p; + struct device_node *sec_slave_p; + u32 prim_clk_usrs; + int hph_en1_gpio; + int hph_en0_gpio; + struct snd_info_entry *codec_root; + void __iomem *lpaif_pri_muxsel_virt_addr; + void __iomem *lpaif_sec_muxsel_virt_addr; + void __iomem *lpass_mux_spkr_ctl_virt_addr; + void __iomem *lpass_mux_mic_ctl_virt_addr; +}; + +struct sdx_wsa881x_dev_info { + struct device_node *of_node; + u32 index; +}; + +static void *def_tavil_mbhc_cal(void); +static void *adsp_state_notifier; +static bool dummy_device_registered; + +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, +}; + +static const struct afe_clk_set lpass_default_v2 = { + 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, +}; + +static struct snd_soc_aux_dev *sdx_aux_dev; +static struct snd_soc_codec_conf *sdx_codec_conf; + +static int sdx_auxpcm_rate = 8000; + +static struct mutex cdc_mclk_mutex; +static int sdx_mi2s_rx_ch = 1; +static int sdx_mi2s_tx_ch = 1; +static int sdx_sec_mi2s_rx_ch = 1; +static int sdx_sec_mi2s_tx_ch = 1; +static int sdx_mi2s_rate = SAMPLE_RATE_48KHZ; +static int sdx_sec_mi2s_rate = SAMPLE_RATE_48KHZ; + +static int sdx_mi2s_mode = I2S_PCM_MASTER_MODE; +static int sdx_sec_mi2s_mode = I2S_PCM_MASTER_MODE; +static int sdx_auxpcm_mode = I2S_PCM_MASTER_MODE; +static int sdx_sec_auxpcm_mode = I2S_PCM_MASTER_MODE; + +static int sdx_spk_control = 1; +static int sdx_hifi_control; +static atomic_t mi2s_ref_count; +static atomic_t sec_mi2s_ref_count; + +static int sdx_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 sdx_machine_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; +} + +static int sdx_mi2s_clk_ctl(struct snd_soc_pcm_runtime *rtd, bool enable, + enum mi2s_types mi2s_type, int rate, u16 mode) +{ + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + struct afe_clk_set m_clk = lpass_default_v2; + struct afe_clk_set ibit_clk = lpass_default_v2; + u16 mi2s_port; + u16 ibit_clk_id; + int bit_clk_freq = (rate * 2 * NUM_OF_BITS_PER_SAMPLE); + int ret = 0; + + dev_dbg(card->dev, "%s: setting lpass clock using v2\n", __func__); + + if (pdata == NULL) { + dev_err(card->dev, "%s: platform data is null\n", __func__); + ret = -ENOMEM; + goto done; + } + + if (mi2s_type == PRI_MI2S) { + mi2s_port = AFE_PORT_ID_PRIMARY_MI2S_RX; + ibit_clk_id = Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT; + } else { + mi2s_port = AFE_PORT_ID_SECONDARY_MI2S_RX; + ibit_clk_id = Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT; + } + + /* Set both mclk and ibit clocks when using LPASS_CLK_VER_2 */ + m_clk.clk_id = Q6AFE_LPASS_CLK_ID_MCLK_3; + m_clk.clk_freq_in_hz = pdata->mclk_freq; + m_clk.enable = enable; + ret = afe_set_lpass_clock_v2(mi2s_port, &m_clk); + if (ret < 0) { + dev_err(card->dev, + "%s: afe_set_lpass_clock_v2 failed for mclk_3 with ret %d\n", + __func__, ret); + goto done; + } + + if (mode) { + ibit_clk.clk_id = ibit_clk_id; + ibit_clk.clk_freq_in_hz = bit_clk_freq; + ibit_clk.enable = enable; + ret = afe_set_lpass_clock_v2(mi2s_port, &ibit_clk); + if (ret < 0) { + dev_err(card->dev, + "%s: afe_set_lpass_clock_v2 failed for ibit with ret %d\n", + __func__, ret); + goto err_ibit_clk_set; + } + } + ret = 0; + +done: + return ret; + +err_ibit_clk_set: + m_clk.enable = false; + if (afe_set_lpass_clock_v2(mi2s_port, &m_clk)) + dev_err(card->dev, "%s: afe_set_lpass_clock_v2 failed for mclk_3\n", + __func__); + + return ret; +} + +static void sdx_mi2s_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int ret; + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + + if (atomic_dec_return(&mi2s_ref_count) == 0) { + ret = sdx_mi2s_clk_ctl(rtd, false, PRI_MI2S, 0, + pdata->prim_mi2s_mode); + if (ret < 0) + pr_err("%s Clock disable failed\n", __func__); + + if (pdata->prim_mi2s_mode == 1) + ret = msm_cdc_pinctrl_select_sleep_state + (pdata->prim_master_p); + else + ret = msm_cdc_pinctrl_select_sleep_state + (pdata->prim_slave_p); + if (ret) + pr_err("%s: failed to set pri gpios to sleep: %d\n", + __func__, ret); + } +} + +static int sdx_mi2s_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_dai *codec_dai = rtd->codec_dai; + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + int ret = 0; + + pdata->prim_mi2s_mode = sdx_mi2s_mode; + if (atomic_inc_return(&mi2s_ref_count) == 1) { + if (pdata->lpaif_pri_muxsel_virt_addr != NULL) { + ret = afe_enable_lpass_core_shared_clock(MI2S_RX, + CLOCK_ON); + if (ret < 0) { + ret = -EINVAL; + goto done; + } + iowrite32(I2S_SEL << I2S_PCM_SEL_OFFSET, + pdata->lpaif_pri_muxsel_virt_addr); + if (pdata->lpass_mux_spkr_ctl_virt_addr != NULL) { + if (pdata->prim_mi2s_mode == 1) + iowrite32(PRI_TLMM_CLKS_EN_MASTER, + pdata->lpass_mux_spkr_ctl_virt_addr); + else + iowrite32(PRI_TLMM_CLKS_EN_SLAVE, + pdata->lpass_mux_spkr_ctl_virt_addr); + } else { + dev_err(card->dev, "%s: mux spkr ctl virt addr is NULL\n", + __func__); + + ret = -EINVAL; + goto err; + } + } else { + dev_err(card->dev, "%s lpaif_pri_muxsel_virt_addr is NULL\n", + __func__); + + ret = -EINVAL; + goto done; + } + /* + * This sets the CONFIG PARAMETER WS_SRC. + * 1 means internal clock master mode. + * 0 means external clock slave mode. + */ + if (pdata->prim_mi2s_mode == 1) { + ret = msm_cdc_pinctrl_select_active_state + (pdata->prim_master_p); + if (ret < 0) { + pr_err("%s pinctrl set failed\n", __func__); + goto err; + } + ret = sdx_mi2s_clk_ctl(rtd, true, PRI_MI2S, + sdx_mi2s_rate, + pdata->prim_mi2s_mode); + if (ret < 0) { + dev_err(card->dev, "%s clock enable failed\n", + __func__); + goto err; + } + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) { + sdx_mi2s_clk_ctl(rtd, false, PRI_MI2S, + 0, pdata->prim_mi2s_mode); + dev_err(card->dev, + "%s Set fmt for cpu dai failed\n", + __func__); + goto err; + } + ret = snd_soc_dai_set_fmt(codec_dai, + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) { + sdx_mi2s_clk_ctl(rtd, false, PRI_MI2S, + 0, pdata->prim_mi2s_mode); + dev_err(card->dev, + "%s Set fmt for codec dai failed\n", + __func__); + } + } else { + /* + * Disable bit clk in slave mode for QC codec. + * Enable only mclk. + */ + ret = msm_cdc_pinctrl_select_active_state + (pdata->prim_slave_p); + if (ret < 0) { + pr_err("%s pinctrl set failed\n", __func__); + goto err; + } + ret = sdx_mi2s_clk_ctl(rtd, false, PRI_MI2S, 0, + pdata->prim_mi2s_mode); + if (ret < 0) { + dev_err(card->dev, + "%s clock enable failed\n", __func__); + goto err; + } + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) { + dev_err(card->dev, + "%s Set fmt for cpu dai failed\n", + __func__); + goto err; + } + } +err: + afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_OFF); + } +done: + if (ret) + atomic_dec_return(&mi2s_ref_count); + return ret; +} + +static void sdx_sec_mi2s_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int ret; + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + + if (atomic_dec_return(&sec_mi2s_ref_count) == 0) { + ret = sdx_mi2s_clk_ctl(rtd, false, SEC_MI2S, + 0, pdata->sec_mi2s_mode); + if (ret < 0) + pr_err("%s Clock disable failed\n", __func__); + + if (pdata->sec_mi2s_mode == 1) + ret = msm_cdc_pinctrl_select_sleep_state + (pdata->sec_master_p); + else + ret = msm_cdc_pinctrl_select_sleep_state + (pdata->sec_slave_p); + if (ret) + pr_err("%s: failed to set sec gpios to sleep: %d\n", + __func__, ret); + } +} + +static int sdx_sec_mi2s_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_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + int ret = 0; + + pdata->sec_mi2s_mode = sdx_sec_mi2s_mode; + if (atomic_inc_return(&sec_mi2s_ref_count) == 1) { + if (pdata->lpaif_sec_muxsel_virt_addr != NULL) { + ret = afe_enable_lpass_core_shared_clock( + SECONDARY_I2S_RX, CLOCK_ON); + if (ret < 0) { + ret = -EINVAL; + goto done; + } + iowrite32(I2S_SEL << I2S_PCM_SEL_OFFSET, + pdata->lpaif_sec_muxsel_virt_addr); + + if (pdata->lpass_mux_mic_ctl_virt_addr != NULL) { + if (pdata->sec_mi2s_mode == 1) + iowrite32(SEC_TLMM_CLKS_EN_MASTER, + pdata->lpass_mux_mic_ctl_virt_addr); + else + iowrite32(SEC_TLMM_CLKS_EN_SLAVE, + pdata->lpass_mux_mic_ctl_virt_addr); + } else { + dev_err(card->dev, + "%s: mux spkr ctl virt addr is NULL\n", + __func__); + ret = -EINVAL; + goto err; + } + } else { + dev_err(card->dev, "%s lpaif_sec_muxsel_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + goto done; + } + /* + * This sets the CONFIG PARAMETER WS_SRC. + * 1 means internal clock master mode. + * 0 means external clock slave mode. + */ + if (pdata->sec_mi2s_mode == 1) { + ret = msm_cdc_pinctrl_select_active_state + (pdata->sec_master_p); + if (ret < 0) { + pr_err("%s pinctrl set failed\n", __func__); + goto err; + } + ret = sdx_mi2s_clk_ctl(rtd, true, SEC_MI2S, + sdx_sec_mi2s_rate, + pdata->sec_mi2s_mode); + if (ret < 0) { + dev_err(card->dev, "%s clock enable failed\n", + __func__); + goto err; + } + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_CBS_CFS); + if (ret < 0) { + ret = sdx_mi2s_clk_ctl(rtd, false, SEC_MI2S, + 0, + pdata->sec_mi2s_mode); + dev_err(card->dev, "%s Set fmt for cpu dai failed\n", + __func__); + } + } else { + /* + * Enable mclk here, if needed for external codecs. + * Optional. Refer primary mi2s slave interface. + */ + ret = msm_cdc_pinctrl_select_active_state + (pdata->sec_slave_p); + if (ret < 0) { + pr_err("%s pinctrl set failed\n", __func__); + goto err; + } + ret = sdx_mi2s_clk_ctl(rtd, false, SEC_MI2S, 0, + pdata->sec_mi2s_mode); + if (ret < 0) { + dev_err(card->dev, + "%s clock enable failed\n", __func__); + goto err; + } + ret = snd_soc_dai_set_fmt(cpu_dai, + SND_SOC_DAIFMT_CBM_CFM); + if (ret < 0) + dev_err(card->dev, "%s Set fmt for cpu dai failed\n", + __func__); + } +err: + afe_enable_lpass_core_shared_clock(SECONDARY_I2S_RX, + CLOCK_OFF); + } +done: + if (ret) + atomic_dec_return(&sec_mi2s_ref_count); + return ret; +} + +static struct snd_soc_ops sdx_mi2s_be_ops = { + .startup = sdx_mi2s_startup, + .shutdown = sdx_mi2s_shutdown, +}; + +static struct snd_soc_ops sdx_sec_mi2s_be_ops = { + .startup = sdx_sec_mi2s_startup, + .shutdown = sdx_sec_mi2s_shutdown, +}; + +static int sdx_mi2s_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: sdx_i2s_rate = %d", __func__, sdx_mi2s_rate); + ucontrol->value.integer.value[0] = sdx_mi2s_rate; + return 0; +} + +static int sdx_sec_mi2s_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: sdx_sec_i2s_rate = %d", __func__, sdx_sec_mi2s_rate); + ucontrol->value.integer.value[0] = sdx_sec_mi2s_rate; + return 0; +} + +static int sdx_mi2s_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + sdx_mi2s_rate = SAMPLE_RATE_8KHZ; + break; + case 1: + sdx_mi2s_rate = SAMPLE_RATE_16KHZ; + break; + case 2: + default: + sdx_mi2s_rate = SAMPLE_RATE_48KHZ; + break; + } + pr_debug("%s: sdx_i2s_rate = %d ucontrol->value = %d\n", + __func__, sdx_mi2s_rate, + (int)ucontrol->value.integer.value[0]); + return 0; +} + +static int sdx_sec_mi2s_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + sdx_sec_mi2s_rate = SAMPLE_RATE_8KHZ; + break; + case 1: + sdx_sec_mi2s_rate = SAMPLE_RATE_16KHZ; + break; + case 2: + default: + sdx_sec_mi2s_rate = SAMPLE_RATE_48KHZ; + break; + } + pr_debug("%s: sdx_sec_mi2s_rate = %d ucontrol->value = %d\n", + __func__, sdx_sec_mi2s_rate, + (int)ucontrol->value.integer.value[0]); + return 0; +} + +static void param_set_mask(struct snd_pcm_hw_params *p, int n, + unsigned int bit) +{ + struct snd_mask *m = NULL; + + if (bit >= SNDRV_MASK_MAX) + return; + if ((n >= SNDRV_PCM_HW_PARAM_FIRST_MASK) && + (n <= SNDRV_PCM_HW_PARAM_LAST_MASK)) { + m = &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]); + + m->bits[0] = 0; + m->bits[1] = 0; + m->bits[bit >> 5] |= (1 << (bit & 31)); + } +} + +static int sdx_mi2s_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rt, + 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_S16_LE); + rate->min = rate->max = sdx_mi2s_rate; + channels->min = channels->max = sdx_mi2s_rx_ch; + return 0; +} + +static int sdx_sec_mi2s_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rt, + 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_S16_LE); + rate->min = rate->max = sdx_sec_mi2s_rate; + channels->min = channels->max = sdx_sec_mi2s_rx_ch; + return 0; +} + +static int sdx_mi2s_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rt, + 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_S16_LE); + rate->min = rate->max = sdx_mi2s_rate; + channels->min = channels->max = sdx_mi2s_tx_ch; + return 0; +} + +static int sdx_sec_mi2s_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rt, + 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_S16_LE); + rate->min = rate->max = sdx_sec_mi2s_rate; + channels->min = channels->max = sdx_sec_mi2s_tx_ch; + return 0; +} + +static int sdx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rt, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FORMAT_S16_LE); + rate->min = rate->max = sdx_mi2s_rate; + return 0; +} + +static int sdx_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s sdx_mi2s_rx_ch %d\n", __func__, sdx_mi2s_rx_ch); + ucontrol->value.integer.value[0] = sdx_mi2s_rx_ch - 1; + return 0; +} + +static int sdx_sec_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s sdx_sec_mi2s_rx_ch %d\n", __func__, sdx_mi2s_rx_ch); + ucontrol->value.integer.value[0] = sdx_sec_mi2s_rx_ch - 1; + return 0; +} + +static int sdx_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + sdx_mi2s_rx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s sdx_mi2s_rx_ch %d\n", __func__, sdx_mi2s_rx_ch); + return 1; +} + +static int sdx_sec_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + sdx_mi2s_rx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s sdx_sec_mi2s_rx_ch %d\n", __func__, sdx_sec_mi2s_rx_ch); + return 1; +} + +static int sdx_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s sdx_mi2s_tx_ch %d\n", __func__, sdx_mi2s_tx_ch); + ucontrol->value.integer.value[0] = sdx_mi2s_tx_ch - 1; + return 0; +} + +static int sdx_sec_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s sdx_sec_mi2s_tx_ch %d\n", __func__, sdx_mi2s_tx_ch); + ucontrol->value.integer.value[0] = sdx_sec_mi2s_tx_ch - 1; + return 0; +} + +static int sdx_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + sdx_mi2s_tx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s sdx_mi2s_tx_ch %d\n", __func__, sdx_mi2s_tx_ch); + return 1; +} + +static int sdx_sec_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + sdx_mi2s_tx_ch = ucontrol->value.integer.value[0] + 1; + pr_debug("%s sdx_sec_mi2s_tx_ch %d\n", __func__, sdx_sec_mi2s_tx_ch); + return 1; +} + +static int sdx_mi2s_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s sdx_mi2s_mode %d\n", __func__, sdx_mi2s_mode); + ucontrol->value.integer.value[0] = sdx_mi2s_mode; + return 0; +} + +static int sdx_mi2s_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + sdx_mi2s_mode = I2S_PCM_MASTER_MODE; + break; + case 1: + sdx_mi2s_mode = I2S_PCM_SLAVE_MODE; + break; + default: + sdx_mi2s_mode = I2S_PCM_MASTER_MODE; + break; + } + pr_debug("%s: sdx_mi2s_mode = %d ucontrol->value = %d\n", + __func__, sdx_mi2s_mode, + (int)ucontrol->value.integer.value[0]); + return 0; +} + +static int sdx_sec_mi2s_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s sdx_sec_mi2s_mode %d\n", __func__, sdx_sec_mi2s_mode); + ucontrol->value.integer.value[0] = sdx_sec_mi2s_mode; + return 0; +} + +static int sdx_sec_mi2s_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + sdx_sec_mi2s_mode = I2S_PCM_MASTER_MODE; + break; + case 1: + sdx_sec_mi2s_mode = I2S_PCM_SLAVE_MODE; + break; + default: + sdx_sec_mi2s_mode = I2S_PCM_MASTER_MODE; + break; + } + pr_debug("%s: sdx_sec_mi2s_mode = %d ucontrol->value = %d\n", + __func__, sdx_sec_mi2s_mode, + (int)ucontrol->value.integer.value[0]); + return 0; +} + +static int sdx_auxpcm_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s sdx_auxpcm_mode %d\n", __func__, sdx_auxpcm_mode); + ucontrol->value.integer.value[0] = sdx_auxpcm_mode; + return 0; +} + +static int sdx_auxpcm_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + sdx_auxpcm_mode = I2S_PCM_MASTER_MODE; + break; + case 1: + sdx_auxpcm_mode = I2S_PCM_SLAVE_MODE; + break; + default: + sdx_auxpcm_mode = I2S_PCM_MASTER_MODE; + break; + } + pr_debug("%s: sdx_auxpcm_mode = %d ucontrol->value = %d\n", + __func__, sdx_auxpcm_mode, + (int)ucontrol->value.integer.value[0]); + return 0; +} + +static int sdx_sec_auxpcm_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s sdx_sec_auxpcm_mode %d\n", __func__, sdx_sec_auxpcm_mode); + ucontrol->value.integer.value[0] = sdx_sec_auxpcm_mode; + return 0; +} + +static int sdx_sec_auxpcm_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 0: + sdx_sec_auxpcm_mode = I2S_PCM_MASTER_MODE; + break; + case 1: + sdx_sec_auxpcm_mode = I2S_PCM_SLAVE_MODE; + break; + default: + sdx_sec_auxpcm_mode = I2S_PCM_MASTER_MODE; + break; + } + pr_debug("%s: sdx_sec_auxpcm_mode = %d ucontrol->value = %d\n", + __func__, sdx_sec_auxpcm_mode, + (int)ucontrol->value.integer.value[0]); + return 0; +} + +static int sdx_mi2s_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s sdx_spk_control %d", __func__, sdx_spk_control); + ucontrol->value.integer.value[0] = sdx_spk_control; + return 0; +} + +static void sdx_ext_control(struct snd_soc_codec *codec) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + + pr_debug("%s sdx_spk_control %d", __func__, sdx_spk_control); + + if (sdx_spk_control == SDX_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 sdx_mi2s_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + + pr_debug("%s()\n", __func__); + + if (sdx_spk_control == ucontrol->value.integer.value[0]) + return 0; + sdx_spk_control = ucontrol->value.integer.value[0]; + sdx_ext_control(codec); + return 1; +} + +static int sdx_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 sdx_machine_data *pdata = + snd_soc_card_get_drvdata(card); + + pr_debug("%s: sdx_hifi_control = %d", __func__, sdx_hifi_control); + if (pdata->hph_en1_gpio < 0) { + pr_err("%s: hph_en1_gpio is invalid\n", __func__); + return -EINVAL; + } + + if (sdx_hifi_control == SDX_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 sdx_hifi_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: sdx_hifi_control = %d\n", + __func__, sdx_hifi_control); + ucontrol->value.integer.value[0] = sdx_hifi_control; + return 0; +} + +static int sdx_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]); + sdx_hifi_control = ucontrol->value.integer.value[0]; + sdx_hifi_ctrl(codec); + return 1; +} + +static int sdx_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: + tavil_cdc_mclk_enable(codec, 1); + break; + case SND_SOC_DAPM_POST_PMD: + tavil_cdc_mclk_enable(codec, 0); + break; + } + return 0; +} + +static void sdx_auxpcm_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int ret; + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + + if (pdata->prim_auxpcm_mode == 1) + ret = msm_cdc_pinctrl_select_sleep_state(pdata->prim_master_p); + else + ret = msm_cdc_pinctrl_select_sleep_state(pdata->prim_slave_p); + if (ret) + pr_err("%s: failed to set prim gpios to sleep: %d\n", + __func__, ret); +} + +static int sdx_auxpcm_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + + pdata->prim_auxpcm_mode = sdx_auxpcm_mode; + if (pdata->lpaif_pri_muxsel_virt_addr != NULL) { + ret = afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_ON); + if (ret < 0) { + ret = -EINVAL; + goto done; + } + iowrite32(PCM_SEL << I2S_PCM_SEL_OFFSET, + pdata->lpaif_pri_muxsel_virt_addr); + if (pdata->lpass_mux_spkr_ctl_virt_addr != NULL) { + if (pdata->prim_auxpcm_mode == 1) + iowrite32(PRI_TLMM_CLKS_EN_MASTER, + pdata->lpass_mux_spkr_ctl_virt_addr); + else + iowrite32(PRI_TLMM_CLKS_EN_SLAVE, + pdata->lpass_mux_spkr_ctl_virt_addr); + } else { + dev_err(card->dev, "%s lpass_mux_spkr_ctl_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + } + } else { + dev_err(card->dev, "%s lpaif_pri_muxsel_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + goto done; + } + + if (pdata->prim_auxpcm_mode == 1) { + ret = msm_cdc_pinctrl_select_active_state + (pdata->prim_master_p); + if (ret < 0) + pr_err("%s pinctrl set failed\n", __func__); + } else { + ret = msm_cdc_pinctrl_select_active_state(pdata->prim_slave_p); + if (ret < 0) + pr_err("%s pinctrl set failed\n", __func__); + } + afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_OFF); +done: + return ret; +} + +static struct snd_soc_ops sdx_auxpcm_be_ops = { + .startup = sdx_auxpcm_startup, + .shutdown = sdx_auxpcm_shutdown, +}; + +static void sdx_sec_auxpcm_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int ret; + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + + if (pdata->sec_auxpcm_mode == 1) + ret = msm_cdc_pinctrl_select_sleep_state(pdata->sec_master_p); + else + ret = msm_cdc_pinctrl_select_sleep_state(pdata->sec_slave_p); + if (ret) + pr_err("%s: failed to set sec gpios to sleep: %d\n", + __func__, ret); +} + +static int sdx_sec_auxpcm_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + + pdata->sec_auxpcm_mode = sdx_sec_auxpcm_mode; + if (pdata->lpaif_sec_muxsel_virt_addr != NULL) { + ret = afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_ON); + if (ret < 0) { + ret = -EINVAL; + goto done; + } + iowrite32(PCM_SEL << I2S_PCM_SEL_OFFSET, + pdata->lpaif_sec_muxsel_virt_addr); + if (pdata->lpass_mux_mic_ctl_virt_addr != NULL) { + if (pdata->sec_auxpcm_mode == 1) + iowrite32(SEC_TLMM_CLKS_EN_MASTER, + pdata->lpass_mux_mic_ctl_virt_addr); + else + iowrite32(SEC_TLMM_CLKS_EN_SLAVE, + pdata->lpass_mux_mic_ctl_virt_addr); + } else { + dev_err(card->dev, + "%s lpass_mux_mic_ctl_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + } + } else { + dev_err(card->dev, + "%s lpaif_sec_muxsel_virt_addr is NULL\n", __func__); + ret = -EINVAL; + goto done; + } + + if (pdata->sec_auxpcm_mode == 1) { + ret = msm_cdc_pinctrl_select_active_state(pdata->sec_master_p); + if (ret < 0) + pr_err("%s pinctrl set failed\n", __func__); + } else { + ret = msm_cdc_pinctrl_select_active_state(pdata->sec_slave_p); + if (ret < 0) + pr_err("%s pinctrl set failed\n", __func__); + } + afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_OFF); +done: + return ret; +} + +static struct snd_soc_ops sdx_sec_auxpcm_be_ops = { + .startup = sdx_sec_auxpcm_startup, + .shutdown = sdx_sec_auxpcm_shutdown, +}; + +static int sdx_auxpcm_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = sdx_auxpcm_rate; + return 0; +} + +static int sdx_auxpcm_rate_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + sdx_auxpcm_rate = 16000; + break; + case 0: + default: + sdx_auxpcm_rate = 8000; + break; + } + return 0; +} + +static int sdx_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 = sdx_auxpcm_rate; + channels->min = channels->max = 1; + + return 0; +} + +static const struct snd_soc_dapm_widget sdx_dapm_widgets[] = { + + SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, + sdx_mclk_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("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 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[] = { + {"MIC BIAS1", NULL, "MCLK"}, + {"MIC BIAS2", NULL, "MCLK"}, + {"MIC BIAS3", NULL, "MCLK"}, + {"MIC BIAS4", NULL, "MCLK"}, +}; + +static const char *const spk_function[] = {"Off", "On"}; +static const char *const hifi_function[] = {"Off", "On"}; +static const char *const mi2s_rx_ch_text[] = {"One", "Two"}; +static const char *const mi2s_tx_ch_text[] = {"One", "Two"}; +static const char *const auxpcm_rate_text[] = {"rate_8000", "rate_16000"}; + +static const char *const mi2s_rate_text[] = {"rate_8000", + "rate_16000", "rate_48000"}; +static const char *const mode_text[] = {"master", "slave"}; + +static const struct soc_enum sdx_enum[] = { + SOC_ENUM_SINGLE_EXT(2, spk_function), + SOC_ENUM_SINGLE_EXT(2, mi2s_rx_ch_text), + SOC_ENUM_SINGLE_EXT(2, mi2s_tx_ch_text), + SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text), + SOC_ENUM_SINGLE_EXT(3, mi2s_rate_text), + SOC_ENUM_SINGLE_EXT(2, hifi_function), + SOC_ENUM_SINGLE_EXT(2, mode_text), +}; + +static const struct snd_kcontrol_new sdx_snd_controls[] = { + SOC_ENUM_EXT("Speaker Function", sdx_enum[0], + sdx_mi2s_get_spk, + sdx_mi2s_set_spk), + SOC_ENUM_EXT("MI2S_RX Channels", sdx_enum[1], + sdx_mi2s_rx_ch_get, + sdx_mi2s_rx_ch_put), + SOC_ENUM_EXT("MI2S_TX Channels", sdx_enum[2], + sdx_mi2s_tx_ch_get, + sdx_mi2s_tx_ch_put), + SOC_ENUM_EXT("AUX PCM SampleRate", sdx_enum[3], + sdx_auxpcm_rate_get, + sdx_auxpcm_rate_put), + SOC_ENUM_EXT("MI2S SampleRate", sdx_enum[4], + sdx_mi2s_rate_get, + sdx_mi2s_rate_put), + SOC_ENUM_EXT("SEC_MI2S_RX Channels", sdx_enum[1], + sdx_sec_mi2s_rx_ch_get, + sdx_sec_mi2s_rx_ch_put), + SOC_ENUM_EXT("SEC_MI2S_TX Channels", sdx_enum[2], + sdx_sec_mi2s_tx_ch_get, + sdx_sec_mi2s_tx_ch_put), + SOC_ENUM_EXT("SEC MI2S SampleRate", sdx_enum[4], + sdx_sec_mi2s_rate_get, + sdx_sec_mi2s_rate_put), + SOC_ENUM_EXT("HiFi Function", sdx_enum[5], + sdx_hifi_get, + sdx_hifi_put), + SOC_ENUM_EXT("MI2S Mode", sdx_enum[6], + sdx_mi2s_mode_get, + sdx_mi2s_mode_put), + SOC_ENUM_EXT("SEC_MI2S Mode", sdx_enum[6], + sdx_sec_mi2s_mode_get, + sdx_sec_mi2s_mode_put), + SOC_ENUM_EXT("AUXPCM Mode", sdx_enum[6], + sdx_auxpcm_mode_get, + sdx_auxpcm_mode_put), + SOC_ENUM_EXT("SEC_AUXPCM Mode", sdx_enum[6], + sdx_sec_auxpcm_mode_get, + sdx_sec_auxpcm_mode_put), +}; + +static int sdx_mi2s_audrx_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); + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_card *card; + struct snd_info_entry *entry; + struct sdx_machine_data *pdata = + snd_soc_card_get_drvdata(rtd->card); + + pr_debug("%s dev_name %s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + ret = snd_soc_add_codec_controls(codec, sdx_snd_controls, + ARRAY_SIZE(sdx_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, %d\n", + __func__, ret); + goto done; + } + + snd_soc_dapm_new_controls(dapm, sdx_dapm_widgets, + ARRAY_SIZE(sdx_dapm_widgets)); + + snd_soc_dapm_add_routes(dapm, wcd_audio_paths, + ARRAY_SIZE(wcd_audio_paths)); + + /* + * After DAPM Enable pins always + * DAPM SYNC needs to be called. + */ + 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, "Digital Mic6"); + + 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, "HEADPHONE"); + 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, "SPK_OUT"); + snd_soc_dapm_ignore_suspend(dapm, "ANC HEADPHONE"); + 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, "AIF4 VI"); + snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); + 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_sync(dapm); + + wcd_mbhc_cfg.calibration = def_tavil_mbhc_cal(); + if (wcd_mbhc_cfg.calibration) + ret = tavil_mbhc_hs_detect(codec, &wcd_mbhc_cfg); + else + ret = -ENOMEM; + + 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 done; + } + pdata->codec_root = entry; + tavil_codec_info_create_codec_entry(pdata->codec_root, codec); +done: + return ret; +} + +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; +} + +/* Digital audio interface connects codec <---> CPU */ +static struct snd_soc_dai_link sdx_common_dai_links[] = { + /* FrontEnd DAI Links */ + { + .name = SDX_DAILINK_NAME(Media1), + .stream_name = "MultiMedia1", + .cpu_dai_name = "MultiMedia1", + .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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA1 + }, + { + .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 VOIP support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOIP, + }, + { + .name = "Primary MI2S RX Hostless", + .stream_name = "Primary MI2S_RX Hostless Playback", + .cpu_dai_name = "PRI_MI2S_RX_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, + .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 = "DTMF RX Hostless", + .stream_name = "DTMF RX Hostless", + .cpu_dai_name = "DTMF_RX_HOSTLESS", + .platform_name = "msm-pcm-dtmf", + .dynamic = 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, + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_DTMF_RX, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + }, + { + .name = "DTMF TX", + .stream_name = "DTMF TX", + .cpu_dai_name = "msm-dai-stub-dev.4", + .platform_name = "msm-pcm-dtmf", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .ignore_suspend = 1, + }, + { + .name = SDX_DAILINK_NAME(Compress1), + .stream_name = "COMPR", + .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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA4, + }, + { + .name = SDX_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, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA2, + }, + { + .name = "Primary MI2S TX Hostless", + .stream_name = "Primary MI2S_TX Hostless Playback", + .cpu_dai_name = "PRI_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 = SDX_DAILINK_NAME(LowLatency), + .stream_name = "MultiMedia5", + .cpu_dai_name = "MultiMedia5", + .platform_name = "msm-pcm-dsp.1", + .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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA5, + }, + { + .name = "SDX VoiceMMode1", + .stream_name = "VoiceMMode1", + .cpu_dai_name = "VoiceMMode1", + .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 Voice support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOICEMMODE1, + }, + { + .name = "SDX VoiceMMode2", + .stream_name = "VoiceMMode2", + .cpu_dai_name = "VoiceMMode2", + .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 Voice support */ + .ignore_pmdown_time = 1, + .id = MSM_FRONTEND_DAI_VOICEMMODE2, + }, + { + .name = "VoiceMMode1 HOST RX CAPTURE", + .stream_name = "VoiceMMode1 HOST RX CAPTURE", + .cpu_dai_name = "msm-dai-stub-dev.5", + .platform_name = "msm-voice-host-pcm", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .ignore_suspend = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + }, + { + .name = "VoiceMMode1 HOST RX PLAYBACK", + .stream_name = "VoiceMMode1 HOST RX PLAYBACK", + .cpu_dai_name = "msm-dai-stub-dev.6", + .platform_name = "msm-voice-host-pcm", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = "VoiceMMode1 HOST TX CAPTURE", + .stream_name = "VoiceMMode1 HOST TX CAPTURE", + .cpu_dai_name = "msm-dai-stub-dev.7", + .platform_name = "msm-voice-host-pcm", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .ignore_suspend = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + }, + { + .name = "VoiceMMode1 HOST TX PLAYBACK", + .stream_name = "VoiceMMode1 HOST TX PLAYBACK", + .cpu_dai_name = "msm-dai-stub-dev.8", + .platform_name = "msm-voice-host-pcm", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = "VoiceMMode2 HOST RX CAPTURE", + .stream_name = "VoiceMMode2 HOST RX CAPTURE", + .cpu_dai_name = "msm-dai-stub-dev.5", + .platform_name = "msm-voice-host-pcm", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .ignore_suspend = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + }, + { + .name = "VoiceMMode2 HOST RX PLAYBACK", + .stream_name = "VOiceMMode2 HOST RX PLAYBACK", + .cpu_dai_name = "msm-dai-stub-dev.6", + .platform_name = "msm-voice-host-pcm", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = "VoiceMMode2 HOST TX CAPTURE", + .stream_name = "VoiceMMode2 HOST TX CAPTURE", + .cpu_dai_name = "msm-dai-stub-dev.7", + .platform_name = "msm-voice-host-pcm", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-tx", + .ignore_suspend = 1, + .trigger = {SND_SOC_DPCM_TRIGGER_POST, + SND_SOC_DPCM_TRIGGER_POST}, + }, + { + .name = "VoiceMMode2 HOST TX PLAYBACK", + .stream_name = "VOiceMMode2 HOST TX PLAYBACK", + .cpu_dai_name = "msm-dai-stub-dev.8", + .platform_name = "msm-voice-host-pcm", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + { + .name = "Secondary MI2S RX Hostless", + .stream_name = "Secondary MI2S_RX Hostless Playback", + .cpu_dai_name = "SEC_MI2S_RX_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, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Secondary MI2S TX Hostless", + .stream_name = "Secondary MI2S_TX Hostless Playback", + .cpu_dai_name = "SEC_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 = "Primary AUXPCM RX Hostless", + .stream_name = "AUXPCM_HOSTLESS Playback", + .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, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Primary AUXPCM TX Hostless", + .stream_name = "AUXPCM_HOSTLESS Capture", + .cpu_dai_name = "AUXPCM_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 = "Secondary AUXPCM RX Hostless", + .stream_name = "SEC_AUXPCM_HOSTLESS Playback", + .cpu_dai_name = "SEC_AUXPCM_RX_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, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Secondary AUXPCM TX Hostless", + .stream_name = "SEC_AUXPCM_HOSTLESS Capture", + .cpu_dai_name = "SEC_AUXPCM_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", + }, +}; + +static struct snd_soc_dai_link sdx_common_misc_fe_dai_links[] = { + { + .name = SDX_DAILINK_NAME(ASM Loopback), + .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, + .ignore_pmdown_time = 1, + .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, + /* this dainlink has playback support */ + .id = MSM_FRONTEND_DAI_MULTIMEDIA6, + }, +}; + +static struct snd_soc_dai_link sdx_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, + .id = MSM_BACKEND_DAI_AFE_PCM_RX, + .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, + .id = MSM_BACKEND_DAI_AFE_PCM_TX, + .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, + .id = MSM_BACKEND_DAI_INCALL_RECORD_TX, + .be_hw_params_fixup = sdx_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, + .id = MSM_BACKEND_DAI_INCALL_RECORD_RX, + .be_hw_params_fixup = sdx_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, + .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, + .be_hw_params_fixup = sdx_be_hw_params_fixup, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link sdx_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 = "tavil_codec", + .codec_dai_name = "tavil_i2s_rx1", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .init = &sdx_mi2s_audrx_init, + .be_hw_params_fixup = &sdx_mi2s_rx_be_hw_params_fixup, + .ops = &sdx_mi2s_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 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 = "tavil_codec", + .codec_dai_name = "tavil_i2s_tx1", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = &sdx_mi2s_tx_be_hw_params_fixup, + .ops = &sdx_mi2s_be_ops, + .ignore_pmdown_time = 1, + .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, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + .be_hw_params_fixup = &sdx_sec_mi2s_rx_be_hw_params_fixup, + .ops = &sdx_sec_mi2s_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 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, + .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, + .be_hw_params_fixup = &sdx_sec_mi2s_tx_be_hw_params_fixup, + .ops = &sdx_sec_mi2s_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link sdx_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, + .id = MSM_BACKEND_DAI_AUXPCM_RX, + .be_hw_params_fixup = sdx_auxpcm_be_params_fixup, + .ops = &sdx_auxpcm_be_ops, + .ignore_pmdown_time = 1, + /* this dainlink has playback support */ + .ignore_suspend = 1, + }, + { + .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, + .id = MSM_BACKEND_DAI_AUXPCM_TX, + .be_hw_params_fixup = sdx_auxpcm_be_params_fixup, + .ops = &sdx_auxpcm_be_ops, + .ignore_suspend = 1, + }, + /* 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, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_RX, + .be_hw_params_fixup = sdx_auxpcm_be_params_fixup, + .ops = &sdx_sec_auxpcm_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, + { + .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, + .id = MSM_BACKEND_DAI_SEC_AUXPCM_TX, + .be_hw_params_fixup = sdx_auxpcm_be_params_fixup, + .ops = &sdx_sec_auxpcm_be_ops, + .ignore_suspend = 1, + }, +}; + +static struct snd_soc_dai_link sdx_tavil_snd_card_dai_links[ + ARRAY_SIZE(sdx_common_dai_links) + + ARRAY_SIZE(sdx_common_misc_fe_dai_links) + + ARRAY_SIZE(sdx_common_be_dai_links) + + ARRAY_SIZE(sdx_mi2s_be_dai_links) + + ARRAY_SIZE(sdx_auxpcm_be_dai_links)]; + +static struct snd_soc_card snd_soc_card_tavil_sdx = { + .name = "sdx-tavil-i2s-snd-card", +}; + +static int sdx_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_debug("%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 struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) +{ + struct snd_soc_card *card = NULL; + int len_1, len_2, len_3, len_4; + int total_links; + + card = &snd_soc_card_tavil_sdx; + len_1 = ARRAY_SIZE(sdx_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(sdx_common_misc_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(sdx_common_be_dai_links); + len_4 = len_3 + ARRAY_SIZE(sdx_mi2s_be_dai_links); + total_links = len_4 + ARRAY_SIZE(sdx_auxpcm_be_dai_links); + memcpy(sdx_tavil_snd_card_dai_links, + sdx_common_dai_links, + sizeof(sdx_common_dai_links)); + memcpy(sdx_tavil_snd_card_dai_links + len_1, + sdx_common_misc_fe_dai_links, + sizeof(sdx_common_misc_fe_dai_links)); + memcpy(sdx_tavil_snd_card_dai_links + len_2, + sdx_common_be_dai_links, + sizeof(sdx_common_be_dai_links)); + memcpy(sdx_tavil_snd_card_dai_links + len_3, + sdx_mi2s_be_dai_links, + sizeof(sdx_mi2s_be_dai_links)); + memcpy(sdx_tavil_snd_card_dai_links + len_4, + sdx_auxpcm_be_dai_links, + sizeof(sdx_auxpcm_be_dai_links)); + + if (card) { + card->dai_link = sdx_tavil_snd_card_dai_links; + card->num_links = total_links; + } + + return card; +} + +static int sdx_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 sdx_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_dbg(&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 sdx_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 */ + sdx_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_aux_dev), + GFP_KERNEL); + if (!sdx_aux_dev) + return -ENOMEM; + + /* Alloc array of codec conf struct */ + sdx_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs, + sizeof(struct snd_soc_codec_conf), + GFP_KERNEL); + if (!sdx_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); + sdx_aux_dev[i].name = dev_name_str; + sdx_aux_dev[i].codec_name = NULL; + sdx_aux_dev[i].codec_of_node = wsa881x_dev_info[i].of_node; + sdx_aux_dev[i].init = sdx_wsa881x_init; + sdx_codec_conf[i].dev_name = NULL; + sdx_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0]; + sdx_codec_conf[i].of_node = wsa881x_dev_info[i].of_node; + } + card->codec_conf = sdx_codec_conf; + card->aux_dev = sdx_aux_dev; + + return 0; +} + +static int sdx_asoc_machine_probe(struct platform_device *pdev) +{ + int ret; + struct sdx_machine_data *pdata; + struct snd_soc_card *card; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, + "%s No platform supplied from device tree\n", __func__); + + return -EINVAL; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(struct sdx_machine_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; + } + + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,tavil-mclk-clk-freq", + &pdata->mclk_freq); + if (ret) { + dev_err(&pdev->dev, + "%s Looking up %s property in node %s failed", + __func__, "qcom,tavil-mclk-clk-freq", + pdev->dev.of_node->full_name); + + goto err; + } + /* At present only 12.288MHz is supported on SDX. */ + if (q6afe_check_osr_clk_freq(pdata->mclk_freq)) { + dev_err(&pdev->dev, "%s Unsupported tavil mclk freq %u\n", + __func__, pdata->mclk_freq); + + ret = -EINVAL; + goto err; + } + + pdata->prim_master_p = of_parse_phandle(pdev->dev.of_node, + "qcom,prim_mi2s_aux_master", + 0); + pdata->prim_slave_p = of_parse_phandle(pdev->dev.of_node, + "qcom,prim_mi2s_aux_slave", 0); + pdata->sec_master_p = of_parse_phandle(pdev->dev.of_node, + "qcom,sec_mi2s_aux_master", 0); + pdata->sec_slave_p = of_parse_phandle(pdev->dev.of_node, + "qcom,sec_mi2s_aux_slave", 0); + mutex_init(&cdc_mclk_mutex); + atomic_set(&mi2s_ref_count, 0); + atomic_set(&sec_mi2s_ref_count, 0); + pdata->prim_clk_usrs = 0; + + 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; + ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); + if (ret) + goto err; + ret = sdx_populate_dai_link_component_of_node(card); + if (ret) { + ret = -EPROBE_DEFER; + goto err; + } + + ret = sdx_init_wsa_dev(pdev, card); + if (ret) + goto err; + + ret = snd_soc_register_card(card); + if (ret == -EPROBE_DEFER) { + goto err; + } else if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + goto err; + } + + pdata->lpaif_pri_muxsel_virt_addr = ioremap(LPAIF_PRI_MODE_MUXSEL, 4); + if (pdata->lpaif_pri_muxsel_virt_addr == NULL) { + pr_err("%s Pri muxsel virt addr is null\n", __func__); + + ret = -EINVAL; + goto err; + } + pdata->lpass_mux_spkr_ctl_virt_addr = + ioremap(LPASS_CSR_GP_IO_MUX_SPKR_CTL, 4); + if (pdata->lpass_mux_spkr_ctl_virt_addr == NULL) { + pr_err("%s lpass spkr ctl virt addr is null\n", __func__); + + ret = -EINVAL; + goto err1; + } + + pdata->lpaif_sec_muxsel_virt_addr = ioremap(LPAIF_SEC_MODE_MUXSEL, 4); + if (pdata->lpaif_sec_muxsel_virt_addr == NULL) { + pr_err("%s Sec muxsel virt addr is null\n", __func__); + ret = -EINVAL; + goto err2; + } + + pdata->lpass_mux_mic_ctl_virt_addr = + ioremap(LPASS_CSR_GP_IO_MUX_MIC_CTL, 4); + if (pdata->lpass_mux_mic_ctl_virt_addr == NULL) { + pr_err("%s lpass_mux_mic_ctl_virt_addr is null\n", + __func__); + ret = -EINVAL; + goto err3; + } + + return 0; +err3: + iounmap(pdata->lpaif_sec_muxsel_virt_addr); +err2: + iounmap(pdata->lpass_mux_spkr_ctl_virt_addr); +err1: + iounmap(pdata->lpaif_pri_muxsel_virt_addr); +err: + devm_kfree(&pdev->dev, pdata); + return ret; +} + +static int sdx_asoc_machine_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + + pdata->mclk_freq = 0; + gpio_free(pdata->hph_en1_gpio); + gpio_free(pdata->hph_en0_gpio); + iounmap(pdata->lpaif_pri_muxsel_virt_addr); + iounmap(pdata->lpass_mux_spkr_ctl_virt_addr); + iounmap(pdata->lpaif_sec_muxsel_virt_addr); + iounmap(pdata->lpass_mux_mic_ctl_virt_addr); + snd_soc_unregister_card(card); + + return 0; +} + +static const struct of_device_id sdx_asoc_machine_of_match[] = { + { .compatible = "qcom,sdx-asoc-snd-tavil", }, + {}, +}; + +static struct platform_driver sdx_asoc_machine_driver = { + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .pm = &snd_soc_pm_ops, + .of_match_table = sdx_asoc_machine_of_match, + }, + .probe = sdx_asoc_machine_probe, + .remove = sdx_asoc_machine_remove, +}; + +static int dummy_machine_probe(struct platform_device *pdev) +{ + return 0; +} + +static int dummy_machine_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_device dummy_machine_device = { + .name = "dummymachinedriver", +}; + +static struct platform_driver sdx_asoc_machine_dummy_driver = { + .driver = { + .name = "dummymachinedriver", + .owner = THIS_MODULE, + }, + .probe = dummy_machine_probe, + .remove = dummy_machine_remove, +}; + +static int sdx_adsp_state_callback(struct notifier_block *nb, + unsigned long value, void *priv) +{ + if (!dummy_device_registered && SUBSYS_AFTER_POWERUP == value) { + platform_driver_register(&sdx_asoc_machine_dummy_driver); + platform_device_register(&dummy_machine_device); + dummy_device_registered = true; + } + + return NOTIFY_OK; +} + +static struct notifier_block adsp_state_notifier_block = { + .notifier_call = sdx_adsp_state_callback, + .priority = -INT_MAX, +}; + +static int __init sdx_soc_platform_init(void) +{ + adsp_state_notifier = subsys_notif_register_notifier("modem", + &adsp_state_notifier_block); + return 0; +} + +module_init(sdx_soc_platform_init); + +module_platform_driver(sdx_asoc_machine_driver); + +MODULE_DESCRIPTION("ALSA SoC sdx"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DEVICE_TABLE(of, sdx_asoc_machine_of_match); From dde178b542893bea76aa8cdcc1d4876a9184c754 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 27 Oct 2017 09:27:29 +0530 Subject: [PATCH 119/276] asoc: codecs: disable analog codec static supplies during suspend Disable regulator supplies of analog codec during suspend to reduce power consumption. CRs-Fixed: 2132296 Change-Id: Ia4b690f01e83e929f8bf4d279454e2ff2e8655a2 Signed-off-by: Rohit kumar --- asoc/codecs/sdm660_cdc/msm-analog-cdc.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c index d83d15a3b1cc..35e00a3930bf 100644 --- a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -4208,7 +4208,7 @@ static int msm_anlg_cdc_enable_static_supplies_to_optimum( struct sdm660_cdc_pdata *pdata) { int i; - int ret = 0; + int ret = 0, rc = 0; for (i = 0; i < sdm660_cdc->num_of_supplies; i++) { if (pdata->regulator[i].ondemand) @@ -4217,6 +4217,12 @@ static int msm_anlg_cdc_enable_static_supplies_to_optimum( sdm660_cdc->supplies[i].consumer) <= 0) continue; + rc = regulator_enable(sdm660_cdc->supplies[i].consumer); + if (rc) { + dev_err(sdm660_cdc->dev, "Failed to enable %s: %d\n", + sdm660_cdc->supplies[i].supply, rc); + break; + } ret = regulator_set_voltage( sdm660_cdc->supplies[i].consumer, pdata->regulator[i].min_uv, @@ -4233,7 +4239,10 @@ static int msm_anlg_cdc_enable_static_supplies_to_optimum( sdm660_cdc->supplies[i].supply); } - return ret; + while (rc && i--) + if (!pdata->regulator[i].ondemand) + regulator_disable(sdm660_cdc->supplies[i].consumer); + return rc; } static int msm_anlg_cdc_disable_static_supplies_to_optimum( @@ -4252,7 +4261,12 @@ static int msm_anlg_cdc_disable_static_supplies_to_optimum( regulator_set_voltage(sdm660_cdc->supplies[i].consumer, 0, pdata->regulator[i].max_uv); regulator_set_load(sdm660_cdc->supplies[i].consumer, 0); - dev_dbg(sdm660_cdc->dev, "Regulator %s set optimum mode\n", + ret = regulator_disable(sdm660_cdc->supplies[i].consumer); + if (ret) + dev_err(sdm660_cdc->dev, "Failed to disable %s: %d\n", + sdm660_cdc->supplies[i].supply, ret); + + dev_dbg(sdm660_cdc->dev, "Regulator %s disable\n", sdm660_cdc->supplies[i].supply); } @@ -4421,7 +4435,7 @@ static int msm_anlg_cdc_enable_static_supplies( sdm660_cdc->supplies[i].supply); } - while (ret && --i) + while (ret && i--) if (!pdata->regulator[i].ondemand) regulator_disable(sdm660_cdc->supplies[i].consumer); return ret; From c1a5e7a0aaab510ae7cc13867060948dca7783d5 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Thu, 30 Nov 2017 15:42:11 -0800 Subject: [PATCH 120/276] asoc: sdm845: set flag ignore_pmdown_time for ultrasound RX DAI During PCM closing, the AIF power down will be delayed due to flag ignore_pmdown_time is not set. Set this flag for ultrasound RX DAI to avoid delay in PCM close which causes slimbus overflow. Change-Id: I5eeaceaacc5be0c200b50d1a7d0f211fa0d41f2b Signed-off-by: Xiaoyu Ye --- asoc/sdm845.c | 1 + 1 file changed, 1 insertion(+) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index 9cf7aeec62a1..1964d4a9aae2 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -5399,6 +5399,7 @@ static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = { .codec_name = "tavil_codec", .codec_dai_name = "tavil_rx2", .ignore_suspend = 1, + .ignore_pmdown_time = 1, .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, .ops = &msm_slimbus_2_be_ops, }, From 4b053ca2f87ab646f54793c02981877f4dae9f9a Mon Sep 17 00:00:00 2001 From: Aalique Grahame Date: Tue, 28 Nov 2017 13:24:13 -0800 Subject: [PATCH 121/276] ASoC: Enable display-port for voice calls Add mixer controls to enable voice calls over display-port. CRs-Fixed: 2151849 Change-Id: Id245b9c5b97d55c1db60e897954cc94eecd7f42d Signed-off-by: Aalique Grahame --- asoc/msm-pcm-routing-v2.c | 46 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index c17aaef6aca5..883867a6f177 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -3620,6 +3620,10 @@ static int msm_routing_ec_ref_rx_put(struct snd_kcontrol *kcontrol, msm_route_ec_ref_rx = 22; ec_ref_port_id = AFE_PORT_ID_INT3_MI2S_TX; break; + case 23: + msm_route_ec_ref_rx = 23; + ec_ref_port_id = AFE_PORT_ID_HDMI_OVER_DP_RX; + break; default: msm_route_ec_ref_rx = 0; /* NONE */ pr_err("%s EC ref rx %ld not valid\n", @@ -3642,7 +3646,7 @@ static const char *const ec_ref_rx[] = { "None", "SLIM_RX", "I2S_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", - "INT0_MI2S_RX", "INT4_MI2S_RX", "INT3_MI2S_TX"}; + "INT0_MI2S_RX", "INT4_MI2S_RX", "INT3_MI2S_TX", "DISPLAY_PORT"}; static const struct soc_enum msm_route_ec_ref_rx_enum[] = { SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ec_ref_rx), ec_ref_rx), @@ -8091,6 +8095,33 @@ static const struct snd_kcontrol_new usb_audio_rx_voice_mixer_controls[] = { msm_routing_put_voice_mixer), }; +static const struct snd_kcontrol_new display_port_rx_voice_mixer_controls[] = { + SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_DISPLAY_PORT_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_DISPLAY_PORT_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_DISPLAY_PORT_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_DISPLAY_PORT_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_DISPLAY_PORT_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_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_DISPLAY_PORT_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_DISPLAY_PORT_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("Voip", MSM_BACKEND_DAI_INT_BT_SCO_RX, MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, @@ -13552,6 +13583,9 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_MIXER("USB_AUDIO_RX Port Mixer", SND_SOC_NOPM, 0, 0, usb_rx_port_mixer_controls, ARRAY_SIZE(usb_rx_port_mixer_controls)), + SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX_Voice Mixer", + SND_SOC_NOPM, 0, 0, display_port_rx_voice_mixer_controls, + ARRAY_SIZE(display_port_rx_voice_mixer_controls)), /* lsm mixer definitions */ SND_SOC_DAPM_MIXER("LSM1 Mixer", SND_SOC_NOPM, 0, 0, lsm1_mixer_controls, ARRAY_SIZE(lsm1_mixer_controls)), @@ -15029,6 +15063,16 @@ static const struct snd_soc_dapm_route intercon[] = { {"USB_AUDIO_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, {"USB_AUDIO_RX", NULL, "USB_AUDIO_RX_Voice Mixer"}, + {"DISPLAY_PORT_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"DISPLAY_PORT_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, + {"DISPLAY_PORT_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"DISPLAY_PORT_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, + {"DISPLAY_PORT_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, + {"DISPLAY_PORT_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, + {"DISPLAY_PORT_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"DISPLAY_PORT_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, + {"DISPLAY_PORT", NULL, "DISPLAY_PORT_RX_Voice Mixer"}, + {"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"}, From 80754fbb3c216f799046b61fc0b5dd47fbe9d4d7 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Mon, 20 Nov 2017 13:31:31 +0530 Subject: [PATCH 122/276] asoc: set freed pointers to NULL Set freed pointers to NULL to avoid double free in msm_compr_playback_open and msm_compr_playback_free functions of the compress driver. CRs-Fixed: 2142216 Change-Id: Ifd011dd85dd9f610c7b69dd460f73d26e006cd66 Signed-off-by: Aditya Bavanari --- asoc/msm-compress-q6-v2.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index f0aa4eed7493..775663926916 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -102,6 +102,7 @@ struct msm_compr_pdata { 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]; + bool is_in_use[MSM_FRONTEND_DAI_MAX]; }; struct msm_compr_audio { @@ -1531,11 +1532,16 @@ 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; - struct msm_compr_audio *prtd; + struct msm_compr_audio *prtd = NULL; struct msm_compr_pdata *pdata = snd_soc_platform_get_drvdata(rtd->platform); pr_debug("%s\n", __func__); + if (pdata->is_in_use[rtd->dai_link->id] == true) { + pr_err("%s: %s is already in use, err: %d\n", + __func__, rtd->dai_link->cpu_dai_name, -EBUSY); + return -EBUSY; + } prtd = kzalloc(sizeof(struct msm_compr_audio), GFP_KERNEL); if (prtd == NULL) { pr_err("Failed to allocate memory for msm_compr_audio\n"); @@ -1547,7 +1553,7 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) pdata->cstream[rtd->dai_link->id] = cstream; pdata->audio_effects[rtd->dai_link->id] = kzalloc(sizeof(struct msm_compr_audio_effects), GFP_KERNEL); - if (!pdata->audio_effects[rtd->dai_link->id]) { + if (pdata->audio_effects[rtd->dai_link->id] == NULL) { pr_err("%s: Could not allocate memory for effects\n", __func__); pdata->cstream[rtd->dai_link->id] = NULL; kfree(prtd); @@ -1555,10 +1561,11 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) } pdata->dec_params[rtd->dai_link->id] = kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL); - if (!pdata->dec_params[rtd->dai_link->id]) { + if (pdata->dec_params[rtd->dai_link->id] == NULL) { pr_err("%s: Could not allocate memory for dec params\n", __func__); kfree(pdata->audio_effects[rtd->dai_link->id]); + pdata->audio_effects[rtd->dai_link->id] = NULL; pdata->cstream[rtd->dai_link->id] = NULL; kfree(prtd); return -ENOMEM; @@ -1603,20 +1610,22 @@ static int msm_compr_playback_open(struct snd_compr_stream *cstream) populate_codec_list(prtd); prtd->audio_client = q6asm_audio_client_alloc( (app_cb)compr_event_handler, prtd); - if (!prtd->audio_client) { + if (prtd->audio_client == NULL) { pr_err("%s: Could not allocate memory for client\n", __func__); kfree(pdata->audio_effects[rtd->dai_link->id]); + pdata->audio_effects[rtd->dai_link->id] = NULL; kfree(pdata->dec_params[rtd->dai_link->id]); + pdata->dec_params[rtd->dai_link->id] = NULL; pdata->cstream[rtd->dai_link->id] = NULL; - runtime->private_data = NULL; kfree(prtd); + runtime->private_data = NULL; 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; msm_adsp_init_mixer_ctl_pp_event_queue(rtd); - + pdata->is_in_use[rtd->dai_link->id] = true; return 0; } @@ -1772,10 +1781,15 @@ static int msm_compr_playback_free(struct snd_compr_stream *cstream) q6asm_audio_client_free(ac); msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd); - kfree(pdata->audio_effects[soc_prtd->dai_link->id]); - pdata->audio_effects[soc_prtd->dai_link->id] = NULL; - kfree(pdata->dec_params[soc_prtd->dai_link->id]); - pdata->dec_params[soc_prtd->dai_link->id] = NULL; + if (pdata->audio_effects[soc_prtd->dai_link->id] != NULL) { + kfree(pdata->audio_effects[soc_prtd->dai_link->id]); + pdata->audio_effects[soc_prtd->dai_link->id] = NULL; + } + if (pdata->dec_params[soc_prtd->dai_link->id] != NULL) { + kfree(pdata->dec_params[soc_prtd->dai_link->id]); + pdata->dec_params[soc_prtd->dai_link->id] = NULL; + } + pdata->is_in_use[soc_prtd->dai_link->id] = false; kfree(prtd); runtime->private_data = NULL; @@ -3842,6 +3856,7 @@ static int msm_compr_probe(struct snd_soc_platform *platform) pdata->dec_params[i] = NULL; pdata->cstream[i] = NULL; pdata->ch_map[i] = NULL; + pdata->is_in_use[i] = false; } snd_soc_add_platform_controls(platform, msm_compr_gapless_controls, From c3c1cc68c090fe6849f7a31830855ba517ff16da Mon Sep 17 00:00:00 2001 From: kunleiz Date: Mon, 27 Nov 2017 15:27:36 +0800 Subject: [PATCH 123/276] ASoC: msm: qdsp6v2: Remove unused dapm routings Clean up unused routings from dapm routing map. CRs-Fixed: 2149819 Change-Id: I056e8ca14d75262844616c7c831bec450d809d48 Signed-off-by: kunleiz --- asoc/msm-pcm-routing-v2.c | 316 ++++---------------------------------- 1 file changed, 26 insertions(+), 290 deletions(-) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 65a9bb6242a6..223674fff187 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -8029,15 +8029,6 @@ static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = { 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 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("DTMF", MSM_BACKEND_DAI_SLIMBUS_0_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8056,15 +8047,6 @@ static const struct snd_kcontrol_new slimbus_6_rx_voice_mixer_controls[] = { 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 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("DTMF", MSM_BACKEND_DAI_SLIMBUS_6_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8083,15 +8065,6 @@ static const struct snd_kcontrol_new usb_audio_rx_voice_mixer_controls[] = { 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 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("DTMF", MSM_BACKEND_DAI_USB_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8110,15 +8083,6 @@ static const struct snd_kcontrol_new display_port_rx_voice_mixer_controls[] = { SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_DISPLAY_PORT_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_DISPLAY_PORT_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_DISPLAY_PORT_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_DISPLAY_PORT_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_DISPLAY_PORT_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8137,12 +8101,6 @@ static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = { 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("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), @@ -8161,9 +8119,6 @@ static const struct snd_kcontrol_new mi2s_rx_voice_mixer_controls[] = { 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("DTMF", MSM_BACKEND_DAI_MI2S_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8182,15 +8137,6 @@ static const struct snd_kcontrol_new pri_mi2s_rx_voice_mixer_controls[] = { 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 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("DTMF", MSM_BACKEND_DAI_PRI_MI2S_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8245,15 +8191,6 @@ static const struct snd_kcontrol_new tert_mi2s_rx_voice_mixer_controls[] = { 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 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("DTMF", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8272,15 +8209,6 @@ static const struct snd_kcontrol_new quat_mi2s_rx_voice_mixer_controls[] = { 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 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("DTMF", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8299,15 +8227,6 @@ static const struct snd_kcontrol_new quin_mi2s_rx_voice_mixer_controls[] = { 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 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("DTMF", MSM_BACKEND_DAI_QUINARY_MI2S_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8326,15 +8245,6 @@ static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = { 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 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("DTMF", MSM_BACKEND_DAI_AFE_PCM_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8353,15 +8263,6 @@ static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = { 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 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("DTMF", MSM_BACKEND_DAI_AUXPCM_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8380,9 +8281,6 @@ static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = { 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("DTMF", MSM_BACKEND_DAI_SEC_AUXPCM_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8401,9 +8299,6 @@ static const struct snd_kcontrol_new tert_aux_pcm_rx_voice_mixer_controls[] = { 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("DTMF", MSM_BACKEND_DAI_TERT_AUXPCM_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8422,9 +8317,6 @@ static const struct snd_kcontrol_new quat_aux_pcm_rx_voice_mixer_controls[] = { 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("DTMF", MSM_BACKEND_DAI_QUAT_AUXPCM_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8443,9 +8335,6 @@ static const struct snd_kcontrol_new quin_aux_pcm_rx_voice_mixer_controls[] = { SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_QUIN_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_QUIN_AUXPCM_RX, - MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer, - msm_routing_put_voice_stub_mixer), SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_QUIN_AUXPCM_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8464,15 +8353,6 @@ static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = { 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("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), @@ -8491,15 +8371,6 @@ static const struct snd_kcontrol_new slimbus_7_rx_voice_mixer_controls[] = { 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 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("DTMF", MSM_BACKEND_DAI_SLIMBUS_7_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8518,15 +8389,6 @@ static const struct snd_kcontrol_new slimbus_8_rx_voice_mixer_controls[] = { 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 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("DTMF", MSM_BACKEND_DAI_SLIMBUS_8_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8548,39 +8410,30 @@ static const struct snd_kcontrol_new quat_tdm_rx_2_voice_mixer_controls[] = { }; 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), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_EXTPROC_RX, + MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_EXTPROC_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_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), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SLIMBUS_1_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_1_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_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), + SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SLIMBUS_3_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_3_RX, + MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), }; static const struct snd_kcontrol_new tx_voicemmode1_mixer_controls[] = { @@ -15046,9 +14899,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15056,9 +14906,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15066,9 +14913,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15076,9 +14920,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"DISPLAY_PORT_RX_Voice Mixer", "Voip", "VOIP_DL"}, {"DISPLAY_PORT_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, - {"DISPLAY_PORT_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, - {"DISPLAY_PORT_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, - {"DISPLAY_PORT_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"}, {"DISPLAY_PORT_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, {"DISPLAY_PORT_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, {"DISPLAY_PORT_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, @@ -15089,8 +14930,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "Voip", "VOIP_DL"}, @@ -15102,9 +14941,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15112,7 +14948,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15120,7 +14955,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15128,7 +14962,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, @@ -15136,7 +14969,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"}, {"QUIN_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"}, - {"QUIN_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, {"QUIN_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, {"QUIN_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, {"QUIN_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, @@ -15151,7 +14983,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"HDMI", NULL, "HDMI_DL_HL"}, {"MI2S_RX_Voice Mixer", "Voip", "VOIP_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"}, @@ -15159,9 +14990,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"}, {"PRI_MI2S_RX_Voice Mixer", "Voip", "VOIP_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"}, @@ -15183,9 +15011,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_Voice Mixer"}, {"TERT_MI2S_RX_Voice Mixer", "Voip", "VOIP_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"}, @@ -15193,18 +15018,12 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX_Voice Mixer"}, {"QUAT_MI2S_RX_Voice Mixer", "Voip", "VOIP_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", "Voip", "VOIP_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"}, @@ -15219,11 +15038,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"VOC_EXT_EC MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"VOC_EXT_EC MUX", "QUIN_MI2S_TX", "QUIN_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"}, - {"VoWLAN_UL", NULL, "VOC_EXT_EC MUX"}, {"VOICEMMODE1_UL", NULL, "VOC_EXT_EC MUX"}, {"VOICEMMODE2_UL", NULL, "VOC_EXT_EC MUX"}, @@ -15328,75 +15143,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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", "QUIN_AUX_PCM_TX_Voice", "QUIN_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_Tx Mixer", "QUIN_AUX_PCM_TX_Voice2", "QUIN_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", "QUIN_AUX_PCM_TX_VoLTE", "QUIN_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", "QUIN_AUX_PCM_TX_VoWLAN", "QUIN_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"}, @@ -16216,27 +15962,20 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"STUB_RX Mixer", "VoiceMMode2", "VOICEMMODE2_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 Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIMBUS_1_RX Mixer", "VoiceMMode2", "VOICEMMODE2_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_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, + {"SLIMBUS_3_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, {"SLIMBUS_3_RX", NULL, "SLIMBUS_3_RX_Voice Mixer"}, {"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"}, @@ -16244,9 +15983,6 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, From fbf66aa0c6ae84db64bdf0b8f3c3a32370c70c67 Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Mon, 4 Dec 2017 15:56:05 -0800 Subject: [PATCH 124/276] msm: Array bounds check for buffer index Check the ASM buffer index boundary before using it to access the buffer to avoid out of array bounds error. CRs-Fixed: 2145996 Change-Id: I5492941dfd9e7c3fa90af6caec14f9ff7e75d248 Signed-off-by: Vignesh Kulothungan --- asoc/msm-pcm-q6-v2.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 17cd5fcb9481..f1b2ba521d68 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -184,6 +184,11 @@ static void event_handler(uint32_t opcode, 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); + if (buf_index >= CAPTURE_MAX_NUM_PERIODS) { + pr_err("%s: buffer index %u is out of range.\n", + __func__, buf_index); + return; + } pr_debug("%s: token=0x%08x buf_index=0x%08x\n", __func__, token, buf_index); prtd->in_frame_info[buf_index].size = payload[4]; From 4e742c1a07aa24c3d787d2778d0034a0cfe0cc32 Mon Sep 17 00:00:00 2001 From: Ramprasad Katkam Date: Fri, 8 Dec 2017 16:39:17 +0530 Subject: [PATCH 125/276] audio-lnx: Correct mutex usage during error handling Release the acquired mutex when there is failure in enabling the soundwire clock. Change-Id: I2e10ed7f0bd86524998a67d62e85754f7989eca2 Signed-off-by: Ramprasad Katkam --- asoc/codecs/msm_sdw/msm_sdw_cdc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/codecs/msm_sdw/msm_sdw_cdc.c b/asoc/codecs/msm_sdw/msm_sdw_cdc.c index 5649167ee6bb..7a5ba8f0c7c4 100644 --- a/asoc/codecs/msm_sdw/msm_sdw_cdc.c +++ b/asoc/codecs/msm_sdw/msm_sdw_cdc.c @@ -256,8 +256,8 @@ static int msm_int_enable_sdw_cdc_clk(struct msm_sdw_priv *msm_sdw, msm_sdw->int_mclk1_enabled = false; } } - mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); rtn: + mutex_unlock(&msm_sdw->cdc_int_mclk1_mutex); return ret; } EXPORT_SYMBOL(msm_int_enable_sdw_cdc_clk); From 4b4553aa09ace0083c8d42176fe78360ac7125e7 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Wed, 13 Dec 2017 09:30:15 +0530 Subject: [PATCH 126/276] dsp: assign channel mapping to three channel voice Tx devices Assign default channel mapping to three channel voice Tx devices when the topology specific info cal data is NULL. Change-Id: Ic9d572edc1d7de07a2bd5e11d82f875368c35854 Signed-off-by: Aditya Bavanari --- dsp/q6voice.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dsp/q6voice.c b/dsp/q6voice.c index 983e0a2388ca..241e3622085c 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -36,6 +36,7 @@ #define CMD_STATUS_FAIL 1 #define NUM_CHANNELS_MONO 1 #define NUM_CHANNELS_STEREO 2 +#define NUM_CHANNELS_THREE 3 #define NUM_CHANNELS_QUAD 4 #define CVP_VERSION_2 2 #define GAIN_Q14_FORMAT(a) (a << 14) @@ -8245,6 +8246,11 @@ int voice_set_topology_specific_info(struct voice_data *v, NUM_CHANNELS_STEREO) { v->dev_tx.channel_mapping[0] = PCM_CHANNEL_FL; v->dev_tx.channel_mapping[1] = PCM_CHANNEL_FR; + } else if (v->dev_tx.no_of_channels == + NUM_CHANNELS_THREE) { + v->dev_tx.channel_mapping[0] = PCM_CHANNEL_FL; + v->dev_tx.channel_mapping[1] = PCM_CHANNEL_FR; + v->dev_tx.channel_mapping[2] = PCM_CHANNEL_FC; } else if (v->dev_tx.no_of_channels == NUM_CHANNELS_QUAD) { v->dev_tx.channel_mapping[0] = PCM_CHANNEL_FL; From 1bca500b67459f8b009eab5c2b9098439cae0bfe Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Tue, 12 Dec 2017 14:22:31 +0530 Subject: [PATCH 127/276] ASoC: wcd: Avoid multiple insertion/removals for aux cable High impedance aux cable when inserted causes jack to change to lineout. This causes removal event to be called which results in pause during music playback because jack value is not updated. Do not change current plug while plug is marked as lineout. CRs-Fixed: 2151012 Change-Id: I99797b58c48be00185a352094e32e8623faee491 Signed-off-by: Vatsal Bucha --- asoc/codecs/wcd-mbhc-legacy.c | 6 +++++- asoc/codecs/wcd-mbhc-v2.c | 3 +++ asoc/codecs/wcd-mbhc-v2.h | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wcd-mbhc-legacy.c b/asoc/codecs/wcd-mbhc-legacy.c index a72f64b40861..34f9eb71cf27 100644 --- a/asoc/codecs/wcd-mbhc-legacy.c +++ b/asoc/codecs/wcd-mbhc-legacy.c @@ -688,7 +688,11 @@ static void wcd_correct_swch_plug(struct work_struct *work) if (!wrk_complete && mbhc->btn_press_intr) { pr_debug("%s: Can be slow insertion of headphone\n", __func__); wcd_cancel_btn_work(mbhc); - plug_type = MBHC_PLUG_TYPE_HEADPHONE; + /* Report as headphone only if previously + * not reported as lineout + */ + if (!mbhc->force_linein) + plug_type = MBHC_PLUG_TYPE_HEADPHONE; } /* * If plug_tye is headset, we might have already reported either in diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index d7c59211658a..d2270d5c97bc 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -605,6 +605,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, hphrocp_off_report(mbhc, SND_JACK_OC_HPHR); hphlocp_off_report(mbhc, SND_JACK_OC_HPHL); mbhc->current_plug = MBHC_PLUG_TYPE_NONE; + mbhc->force_linein = false; } else { /* * Report removal of current jack type. @@ -657,6 +658,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, SND_JACK_LINEOUT | SND_JACK_ANC_HEADPHONE | SND_JACK_UNSUPPORTED); + mbhc->force_linein = false; } if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET && @@ -699,6 +701,7 @@ void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion, mbhc->zr < MAX_IMPED) && (jack_type == SND_JACK_HEADPHONE)) { jack_type = SND_JACK_LINEOUT; + mbhc->force_linein = true; mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH; if (mbhc->hph_status) { mbhc->hph_status &= ~(SND_JACK_HEADSET | diff --git a/asoc/codecs/wcd-mbhc-v2.h b/asoc/codecs/wcd-mbhc-v2.h index 0c57eee8a454..0b6d718b98dd 100644 --- a/asoc/codecs/wcd-mbhc-v2.h +++ b/asoc/codecs/wcd-mbhc-v2.h @@ -587,6 +587,7 @@ struct wcd_mbhc { struct work_struct usbc_analog_work; struct wcd_mbhc_fn *mbhc_fn; + bool force_linein; }; void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc, From ee9534b83038a72047181af8aa367f3a4fb8d33f Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Fri, 15 Dec 2017 16:17:42 -0800 Subject: [PATCH 128/276] asoc: codecs: Fix device switch pop issue With AANC enabled, and when the device switches from speaker path to handset path there is a audible pop. Add more delay to the aanc enable work function to resolve the pop issue. CRs-Fixed: 2137607 Change-Id: Icaf381179b0a81440b21ab4280cb6fc95e6fe6ba Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd934x/wcd934x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 8e3981c9fdf5..e7cec2b4d348 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -526,7 +526,7 @@ struct tx_mute_work { struct delayed_work dwork; }; -#define WCD934X_SPK_ANC_EN_DELAY_MS 350 +#define WCD934X_SPK_ANC_EN_DELAY_MS 550 static int spk_anc_en_delay = WCD934X_SPK_ANC_EN_DELAY_MS; module_param(spk_anc_en_delay, int, 0664); MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path"); From b40dba1328efa7bfff96fede3f202a258db77859 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Mon, 11 Dec 2017 15:37:06 -0800 Subject: [PATCH 129/276] asoc: codecs: Add i2c interface support for tavil Add i2c driver node for tavil codec for i2c communication with the codec. Change-Id: Ie28dfeca102251265a5f50153c3ba98dae84dde2 Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd9xxx-core.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wcd9xxx-core.c b/asoc/codecs/wcd9xxx-core.c index 49be43a3e12f..ffadd606fe4c 100644 --- a/asoc/codecs/wcd9xxx-core.c +++ b/asoc/codecs/wcd9xxx-core.c @@ -51,7 +51,7 @@ * Number of return values needs to be checked for each * registration of Slimbus of I2C bus for each codec */ -#define NUM_WCD9XXX_REG_RET 4 +#define NUM_WCD9XXX_REG_RET 5 #define SLIM_USR_MC_REPEAT_CHANGE_VALUE 0x0 #define SLIM_REPEAT_WRITE_MAX_SLICE 16 @@ -91,6 +91,8 @@ static const int wcd9xxx_cdc_types[] = { }; static const struct of_device_id wcd9xxx_of_match[] = { + { .compatible = "qcom,tavil-i2c", + .data = (void *)&wcd9xxx_cdc_types[WCD934X]}, { .compatible = "qcom,tasha-i2c-pgd", .data = (void *)&wcd9xxx_cdc_types[WCD9335]}, { .compatible = "qcom,wcd9xxx-i2c", @@ -1623,6 +1625,11 @@ static struct i2c_device_id tasha_id_table[] = { {} }; +static struct i2c_device_id tavil_id_table[] = { + {"tavil-i2c", WCD9XXX_I2C_TOP_LEVEL}, + {} +}; + static struct i2c_device_id tabla_id_table[] = { {"tabla top level", WCD9XXX_I2C_TOP_LEVEL}, {"tabla analog", WCD9XXX_I2C_ANALOG}, @@ -1670,6 +1677,17 @@ static struct i2c_driver wcd9335_i2c_driver = { .remove = wcd9xxx_i2c_remove, }; +static struct i2c_driver wcd934x_i2c_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "tavil-i2c-core", + .pm = &wcd9xxx_i2c_pm_ops, + }, + .id_table = tavil_id_table, + .probe = wcd9xxx_i2c_probe, + .remove = wcd9xxx_i2c_remove, +}; + int wcd9xxx_init(void) { int ret[NUM_WCD9XXX_REG_RET] = {0}; @@ -1697,6 +1715,11 @@ int wcd9xxx_init(void) pr_err("%s: Failed to register wcd SB driver: %d\n", __func__, ret[3]); + ret[4] = i2c_add_driver(&wcd934x_i2c_driver); + if (ret[4]) + pr_err("%s: Failed to add the wcd934x I2C driver: %d\n", + __func__, ret[4]); + for (i = 0; i < NUM_WCD9XXX_REG_RET; i++) { if (ret[i]) return ret[i]; @@ -1712,6 +1735,7 @@ void wcd9xxx_exit(void) i2c_del_driver(&tabla_i2c_driver); i2c_del_driver(&wcd9xxx_i2c_driver); i2c_del_driver(&wcd9335_i2c_driver); + i2c_del_driver(&wcd934x_i2c_driver); slim_driver_unregister(&wcd_slim_driver); } From 2b61228c2630d63243dcad7a13db97aa61e73016 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Wed, 3 Jan 2018 13:24:06 +0530 Subject: [PATCH 130/276] asoc: sdm670: check sound card status before sending AFE config WCD codec must be enumerated before AFE configuration for some of the features can be sent to DSP. This is because as part of setting up this configuration, DSP tries to access some codec register. These register accesses will fail if codec is not enumerated already. Add a check for sound card online state to confirm that WCD codec has already enumerated before sending AFE configs. CRs-Fixed: 2166356 Change-Id: Ie5883f3543872d7a049a00a4c70445232360f2f9 Signed-off-by: Aditya Bavanari --- asoc/sdm660-external.c | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/asoc/sdm660-external.c b/asoc/sdm660-external.c index 1c12bfd732cd..d4928664a975 100644 --- a/asoc/sdm660-external.c +++ b/asoc/sdm660-external.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -1192,34 +1192,44 @@ static void msm_afe_clear_config(void) afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG); } -static int msm_adsp_power_up_config(struct snd_soc_codec *codec) +static int msm_adsp_power_up_config(struct snd_soc_codec *codec, + struct snd_card *card) { int ret = 0; unsigned long timeout; int adsp_ready = 0; - struct snd_soc_card *card = codec->component.card; - struct msm_asoc_mach_data *pdata; + bool snd_card_online = 0; - pdata = snd_soc_card_get_drvdata(card); 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; + if (!snd_card_online) { + snd_card_online = snd_card_is_online_state(card); + pr_debug("%s: Sound card is %s\n", __func__, + snd_card_online ? "Online" : "Offline"); } + if (!adsp_ready) { + adsp_ready = q6core_is_adsp_ready(); + pr_debug("%s: ADSP Audio is %s\n", __func__, + adsp_ready ? "ready" : "not ready"); + } + if (snd_card_online && adsp_ready) + break; + /* - * ADSP will be coming up after subsystem restart and + * Sound card/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__); + if (!snd_card_online || !adsp_ready) { + pr_err("%s: Timeout. Sound card is %s, ADSP Audio is %s\n", + __func__, + snd_card_online ? "Online" : "Offline", + adsp_ready ? "ready" : "not ready"); ret = -ETIMEDOUT; goto err_fail; } @@ -1277,7 +1287,7 @@ static int sdm660_notifier_service_cb(struct notifier_block *this, } codec = rtd->codec; - ret = msm_adsp_power_up_config(codec); + ret = msm_adsp_power_up_config(codec, card->snd_card); if (ret < 0) { dev_err(card->dev, "%s: msm_adsp_power_up_config failed ret = %d!\n", @@ -1665,7 +1675,7 @@ int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) msm_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit; } - ret = msm_adsp_power_up_config(codec); + ret = msm_adsp_power_up_config(codec, rtd->card->snd_card); if (ret) { pr_err("%s: Failed to set AFE config %d\n", __func__, ret); goto err_afe_cfg; From 4cda10e14bd6a3f70fb9bdb27650b225fa911f9f Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Wed, 3 Jan 2018 16:51:17 +0530 Subject: [PATCH 131/276] asoc: sdm670: add/remove flag ignore_pmdown_time for RX/TX DAIs - During PCM close, the AIF power down will be delayed if ignore_pmdown_time flag is not set. Set this flag for ultrasound RX DAI to avoid delay in PCM close which causes slimbus overflow. - Flag ignore_pmdown_time is only required to be set for RX DAIs. Add this flag for RX DAIs that don't have it and remove this flag for TX DAIs that have it in sdm670 machine driver. CRs-Fixed: 2166356 Change-Id: I794ee96fb96fe04091ce73f0b48f84ebf119256c Signed-off-by: Aditya Bavanari --- asoc/sdm660-ext-dai-links.c | 27 ++++++++++----------------- asoc/sdm660-internal.c | 25 ++++++++----------------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c index 33e8e0aa2a89..b5333cd933cf 100644 --- a/asoc/sdm660-ext-dai-links.c +++ b/asoc/sdm660-ext-dai-links.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -305,6 +305,7 @@ static struct snd_soc_dai_link msm_ext_tasha_fe_dai[] = { .ignore_suspend = 1, .dpcm_playback = 1, .dpcm_capture = 1, + .ignore_pmdown_time = 1, .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, .ops = &msm_ext_slimbus_2_be_ops, }, @@ -332,7 +333,6 @@ static struct snd_soc_dai_link msm_ext_tasha_fe_dai[] = { .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, @@ -392,6 +392,7 @@ static struct snd_soc_dai_link msm_ext_tavil_fe_dai[] = { .codec_name = "tavil_codec", .codec_dai_name = "tavil_rx2", .ignore_suspend = 1, + .ignore_pmdown_time = 1, .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, .ops = &msm_ext_slimbus_2_be_ops, }, @@ -976,7 +977,6 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM1, @@ -1006,7 +1006,6 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { .dynamic = 1, .dpcm_capture = 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", @@ -1076,7 +1075,6 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM2, @@ -1092,7 +1090,6 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM3, @@ -1108,7 +1105,6 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM4, @@ -1124,7 +1120,6 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM5, @@ -1140,7 +1135,6 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM6 @@ -1156,7 +1150,6 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM7, @@ -1172,7 +1165,6 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM8, @@ -1307,7 +1299,6 @@ static struct snd_soc_dai_link msm_ext_common_fe_dai[] = { 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", }, @@ -1416,6 +1407,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, .be_hw_params_fixup = msm_ext_be_hw_params_fixup, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, /* Incall Music 2 BACK END DAI Link */ { @@ -1430,6 +1422,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, .be_hw_params_fixup = msm_ext_be_hw_params_fixup, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_USB_AUDIO_RX, @@ -1471,6 +1464,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ops = &msm_tdm_be_ops, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_PRI_TDM_TX_0, @@ -1499,6 +1493,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ops = &msm_tdm_be_ops, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_SEC_TDM_TX_0, @@ -1527,6 +1522,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ops = &msm_tdm_be_ops, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_TERT_TDM_TX_0, @@ -1555,6 +1551,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ops = &msm_tdm_be_ops, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_QUAT_TDM_TX_0, @@ -1583,6 +1580,7 @@ static struct snd_soc_dai_link msm_ext_common_be_dai[] = { .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ops = &msm_tdm_be_ops, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_QUIN_TDM_TX_0, @@ -1776,7 +1774,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .dpcm_capture = 1, .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, }, @@ -1808,7 +1805,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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 */ @@ -1839,7 +1835,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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 */ @@ -1870,7 +1865,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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, }, /* Quinary AUX PCM Backend DAI Links */ @@ -1901,7 +1895,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .id = MSM_BACKEND_DAI_QUIN_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, }, }; diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c index 68e6685de47b..4ee8861b0394 100644 --- a/asoc/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -1833,7 +1833,6 @@ static struct snd_soc_dai_link msm_int_dai[] = { 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", }, @@ -1882,7 +1881,6 @@ static struct snd_soc_dai_link msm_int_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM1, @@ -1980,7 +1978,6 @@ static struct snd_soc_dai_link msm_int_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM2, @@ -1996,7 +1993,6 @@ static struct snd_soc_dai_link msm_int_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM3, @@ -2012,7 +2008,6 @@ static struct snd_soc_dai_link msm_int_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM4, @@ -2028,7 +2023,6 @@ static struct snd_soc_dai_link msm_int_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM5, @@ -2044,7 +2038,6 @@ static struct snd_soc_dai_link msm_int_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM6 @@ -2060,7 +2053,6 @@ static struct snd_soc_dai_link msm_int_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM7, @@ -2076,7 +2068,6 @@ static struct snd_soc_dai_link msm_int_dai[] = { 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", .id = MSM_FRONTEND_DAI_LSM8, @@ -2211,7 +2202,6 @@ static struct snd_soc_dai_link msm_int_dai[] = { 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", }, @@ -2317,7 +2307,6 @@ static struct snd_soc_dai_link msm_int_wsa_dai[] = { .no_host_mode = SND_SOC_DAI_LINK_NO_HOST, .ignore_suspend = 1, .dpcm_capture = 1, - .ignore_pmdown_time = 1, }, }; @@ -2441,6 +2430,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, .be_hw_params_fixup = msm_be_hw_params_fixup, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, /* Incall Music 2 BACK END DAI Link */ { @@ -2455,6 +2445,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX, .be_hw_params_fixup = msm_be_hw_params_fixup, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_USB_AUDIO_RX, @@ -2496,6 +2487,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ops = &msm_tdm_be_ops, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_PRI_TDM_TX_0, @@ -2524,6 +2516,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ops = &msm_tdm_be_ops, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_SEC_TDM_TX_0, @@ -2552,6 +2545,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ops = &msm_tdm_be_ops, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_TERT_TDM_TX_0, @@ -2580,6 +2574,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ops = &msm_tdm_be_ops, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_QUAT_TDM_TX_0, @@ -2608,6 +2603,7 @@ static struct snd_soc_dai_link msm_int_be_dai[] = { .be_hw_params_fixup = msm_common_be_hw_params_fixup, .ops = &msm_tdm_be_ops, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_QUIN_TDM_TX_0, @@ -2801,7 +2797,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .dpcm_capture = 1, .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, }, @@ -2833,7 +2828,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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 */ @@ -2864,7 +2858,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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 */ @@ -2895,7 +2888,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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, }, /* Quinary AUX PCM Backend DAI Links */ @@ -2926,7 +2918,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .id = MSM_BACKEND_DAI_QUIN_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, }, }; From 5dbc31f6055ddb7b15faf690551dd8e3c8c659c1 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Mon, 8 Jan 2018 19:14:41 +0530 Subject: [PATCH 132/276] ASoC: sdm670: fix clock refcount for MI2S shutdown For MI2S shutdown, if clock disable is failed, do not bother to re-increment clock reference. Otherwise, next time when startup is called, clock enable will not be called again, so use case will not work. CRs-Fixed: 2166356 Change-Id: Ic7a93a5f20eaabed93d0cd94d76d09f0f1c869fd Signed-off-by: Aditya Bavanari --- asoc/sdm660-common.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c index 1874cfa02860..db65d2ff1ae9 100644 --- a/asoc/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -2722,11 +2722,10 @@ void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) pdata->mi2s_gpio_p[index]); ret = msm_mi2s_set_sclk(substream, false); - if (ret < 0) { + if (ret < 0) pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", __func__, index, ret); - mi2s_intf_conf[index].ref_cnt++; - } + if (mi2s_intf_conf[index].msm_is_ext_mclk) { mi2s_mclk[index].enable = 0; pr_debug("%s: Disabling mclk, clk_freq_in_hz = %u\n", From 089104ffdc6faf384bbd00a053152be84a78fdf3 Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Tue, 12 Dec 2017 17:33:24 -0800 Subject: [PATCH 133/276] dsp: Update ADSP status to reflect failure Update the adsp status to log failures during AVCS_CMDRSP_GET_FWK_VERSION callback. Set q6core_avcs_ver_info status to VER_QUERY_SUPPORTED only when there is a valid ADSP response for framework version. Change-Id: Ie2c6db2842c0b89c9d1a3807bcb49d413a639988 Signed-off-by: Vignesh Kulothungan --- dsp/q6core.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dsp/q6core.c b/dsp/q6core.c index 9f1d9e411ea1..c6536e1d1f73 100644 --- a/dsp/q6core.c +++ b/dsp/q6core.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -236,12 +236,16 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) pr_debug("%s: Received AVCS_CMDRSP_GET_FWK_VERSION\n", __func__); payload1 = data->payload; - q6core_lcl.q6core_avcs_ver_info.status = VER_QUERY_SUPPORTED; q6core_lcl.avcs_fwk_ver_resp_received = 1; ret = parse_fwk_version_info(payload1); - if (ret < 0) + if (ret < 0) { + q6core_lcl.adsp_status = ret; pr_err("%s: Failed to parse payload:%d\n", __func__, ret); + } else { + q6core_lcl.q6core_avcs_ver_info.status = + VER_QUERY_SUPPORTED; + } wake_up(&q6core_lcl.avcs_fwk_ver_req_wait); break; default: From e8ca7eed20f2a4e9885b5fc7bf2248707a1eb90e Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Tue, 9 Jan 2018 11:35:29 +0530 Subject: [PATCH 134/276] dsp: fix NULL pointer exception in core driver NULL pointer dereference occurs while getting version size when version info memory allocation fails. Add NULL check to avoid this NULL pointer exception. CRs-Fixed: 2142971 Change-Id: I47a905a9b4e767d54b406a279626369f18a861d9 Signed-off-by: Aditya Bavanari --- dsp/q6core.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dsp/q6core.c b/dsp/q6core.c index c6536e1d1f73..7bb690ec9f51 100644 --- a/dsp/q6core.c +++ b/dsp/q6core.c @@ -108,8 +108,7 @@ static int parse_fwk_version_info(uint32_t *payload) */ ver_size = sizeof(struct avcs_get_fwk_version) + num_services * sizeof(struct avs_svc_api_info); - if (q6core_lcl.q6core_avcs_ver_info.ver_info != NULL) - pr_warn("%s: Version info is not NULL\n", __func__); + q6core_lcl.q6core_avcs_ver_info.ver_info = kzalloc(ver_size, GFP_ATOMIC); if (q6core_lcl.q6core_avcs_ver_info.ver_info == NULL) @@ -236,7 +235,6 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) pr_debug("%s: Received AVCS_CMDRSP_GET_FWK_VERSION\n", __func__); payload1 = data->payload; - q6core_lcl.avcs_fwk_ver_resp_received = 1; ret = parse_fwk_version_info(payload1); if (ret < 0) { q6core_lcl.adsp_status = ret; @@ -246,6 +244,7 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) q6core_lcl.q6core_avcs_ver_info.status = VER_QUERY_SUPPORTED; } + q6core_lcl.avcs_fwk_ver_resp_received = 1; wake_up(&q6core_lcl.avcs_fwk_ver_req_wait); break; default: @@ -445,8 +444,14 @@ size_t q6core_get_fwk_version_size(uint32_t service_id) if (ret) goto done; - num_services = q6core_lcl.q6core_avcs_ver_info.ver_info - ->avcs_fwk_version.num_services; + if (q6core_lcl.q6core_avcs_ver_info.ver_info != NULL) { + num_services = q6core_lcl.q6core_avcs_ver_info.ver_info + ->avcs_fwk_version.num_services; + } else { + pr_err("%s: ver_info is NULL\n", __func__); + ret = -EINVAL; + goto done; + } ret = sizeof(struct avcs_get_fwk_version); if (service_id == AVCS_SERVICE_ID_ALL) From 61de2bd910ac316108313922bcfee2fe2d6b6eed Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Thu, 4 Jan 2018 11:25:46 +0530 Subject: [PATCH 135/276] ASoC: Update config of SND_HWDEP As CONFIG_SND_HWDEP is defined by default in kernel tree, msm-pcm-routing-devdep.c file in audio drivers gets compiled always. Kernel 4.9 is now enabled with 8953 compilation, and result in failure on audio drivers component 1.0 due to above file. Audio drivers component 2.0 is used for 8953 support, but kernel 4.9 is common for audio drivers 1.0 and 2.0. To fix above compilation issue, use CONFIG_SND_HWDEP_ROUTING config instead of CONFIG_SND_HWDEP to compile msm-pcm-routing-devdep.c file. Change-Id: I19c4b0a56dbe6c4890ece645fcf100687f762f5d Signed-off-by: Laxminath Kasam --- asoc/Makefile | 2 +- config/sdm670auto.conf | 2 +- config/sdm670autoconf.h | 4 ++-- config/sdm845auto.conf | 2 +- config/sdm845autoconf.h | 4 ++-- config/sdxpoorwillsauto.conf | 2 +- config/sdxpoorwillsautoconf.h | 4 ++-- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/asoc/Makefile b/asoc/Makefile index 78f8158f6c86..061c31f207d9 100644 --- a/asoc/Makefile +++ b/asoc/Makefile @@ -36,7 +36,7 @@ snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o \ msm-dai-slim.o msm-transcode-loopback-q6-v2.o msm-pcm-q6-noirq.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_SND_HWDEP_ROUTING) += msm-pcm-routing-devdep.o obj-$(CONFIG_DOLBY_DAP) += msm-dolby-dap-config.o obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o obj-$(CONFIG_DOLBY_LICENSE) += msm-ds2-dap-config.o diff --git a/config/sdm670auto.conf b/config/sdm670auto.conf index ca30dd039e7d..9e410fc6ab6d 100644 --- a/config/sdm670auto.conf +++ b/config/sdm670auto.conf @@ -32,7 +32,7 @@ CONFIG_MSM_CDC_PINCTRL=y CONFIG_SND_SOC_WCD_MBHC_ADC=y CONFIG_SND_SOC_WCD_MBHC_LEGACY=y CONFIG_QTI_PP=y -CONFIG_SND_HWDEP=y +CONFIG_SND_HWDEP_ROUTING=y CONFIG_DTS_EAGLE=y CONFIG_DOLBY_DS2=y CONFIG_DOLBY_LICENSE=y diff --git a/config/sdm670autoconf.h b/config/sdm670autoconf.h index 6aba91d63f8d..76f2d9f886ea 100644 --- a/config/sdm670autoconf.h +++ b/config/sdm670autoconf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, 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 @@ -43,7 +43,7 @@ #define CONFIG_SND_SOC_QDSP6V2 1 #define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 #define CONFIG_QTI_PP 1 -#define CONFIG_SND_HWDEP 1 +#define CONFIG_SND_HWDEP_ROUTING 1 #define CONFIG_DTS_EAGLE 1 #define CONFIG_DOLBY_DS2 1 #define CONFIG_DOLBY_LICENSE 1 diff --git a/config/sdm845auto.conf b/config/sdm845auto.conf index 5234e2b3a618..4c195b43ddc6 100644 --- a/config/sdm845auto.conf +++ b/config/sdm845auto.conf @@ -27,7 +27,7 @@ CONFIG_SOUNDWIRE=y CONFIG_SND_SOC_QDSP6V2=y CONFIG_SND_SOC_WCD_MBHC_ADC=y CONFIG_QTI_PP=y -CONFIG_SND_HWDEP=y +CONFIG_SND_HWDEP_ROUTING=y CONFIG_DTS_EAGLE=y CONFIG_DOLBY_DS2=y CONFIG_DOLBY_LICENSE=y diff --git a/config/sdm845autoconf.h b/config/sdm845autoconf.h index fcef4c13e776..6ef465a61871 100644 --- a/config/sdm845autoconf.h +++ b/config/sdm845autoconf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, 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 @@ -38,7 +38,7 @@ #define CONFIG_SND_SOC_QDSP6V2 1 #define CONFIG_MSM_CDC_PINCTRL 1 #define CONFIG_QTI_PP 1 -#define CONFIG_SND_HWDEP 1 +#define CONFIG_SND_HWDEP_ROUTING 1 #define CONFIG_DTS_EAGLE 1 #define CONFIG_DOLBY_DS2 1 #define CONFIG_DOLBY_LICENSE 1 diff --git a/config/sdxpoorwillsauto.conf b/config/sdxpoorwillsauto.conf index d9dd081e876a..dee34a08be5d 100644 --- a/config/sdxpoorwillsauto.conf +++ b/config/sdxpoorwillsauto.conf @@ -24,6 +24,6 @@ CONFIG_MSM_CDC_PINCTRL=y CONFIG_WCD9XXX_CODEC_CORE=y CONFIG_SND_SOC_WCD_MBHC_ADC=y CONFIG_QTI_PP=y -CONFIG_SND_HWDEP=y +CONFIG_SND_HWDEP_ROUTING=y CONFIG_SND_SOC_MACHINE_SDXPOORWILLS=y CONFIG_SND_SOC_MSM_STUB=y diff --git a/config/sdxpoorwillsautoconf.h b/config/sdxpoorwillsautoconf.h index 9fc6258fe1b3..7b7dac403b63 100644 --- a/config/sdxpoorwillsautoconf.h +++ b/config/sdxpoorwillsautoconf.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2018, 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 @@ -37,6 +37,6 @@ #define CONFIG_WCD9XXX_CODEC_CORE 1 #define CONFIG_SND_SOC_WCD_MBHC_ADC 1 #define CONFIG_QTI_PP 1 -#define CONFIG_SND_HWDEP 1 +#define CONFIG_SND_HWDEP_ROUTING 1 #define CONFIG_SND_SOC_MACHINE_SDXPOORWILLS 1 #define CONFIG_SND_SOC_MSM_STUB 1 From 41b0800a22433576113018eb093bb3bd214545a2 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 10 Jan 2018 15:25:57 +0800 Subject: [PATCH 136/276] asoc: wcd934x: update register default values before post SSR Update register default values before post SSR to avoid codec nack issue. Change-Id: Ibf1e3275d27c4b65ab179b9ddc5a51621c89eab7 Signed-off-by: Meng Wang --- asoc/codecs/wcd934x/wcd934x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index e7cec2b4d348..bc7a2fb00264 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -9272,8 +9272,8 @@ static int tavil_post_reset_cb(struct wcd9xxx *wcd9xxx) else if (control->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ) snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x01); - wcd_resmgr_post_ssr_v2(tavil->resmgr); tavil_update_reg_defaults(tavil); + wcd_resmgr_post_ssr_v2(tavil->resmgr); tavil_codec_init_reg(tavil); __tavil_enable_efuse_sensing(tavil); tavil_mclk2_reg_defaults(tavil); From cd5cdce07b3238642006905b21584d27e7a2599e Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Tue, 12 Sep 2017 12:23:32 -0700 Subject: [PATCH 137/276] asoc: codecs: enable i2s mode for tavil Add support for tavil i2c access. Add audio routings dapm widgets for data and control. Change-Id: I96ebf7a5700b10f294f4fadfeeb21dab490a9313 Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd934x/wcd934x-routing.h | 143 +++- asoc/codecs/wcd934x/wcd934x.c | 930 ++++++++++++++++++++++++-- 2 files changed, 966 insertions(+), 107 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x-routing.h b/asoc/codecs/wcd934x/wcd934x-routing.h index 93a1ad3bab90..8adeef79a7f0 100644 --- a/asoc/codecs/wcd934x/wcd934x-routing.h +++ b/asoc/codecs/wcd934x/wcd934x-routing.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, 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 @@ -17,13 +17,9 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { - /* Virtual input widgets */ - {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, - {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, - {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, {"AIF4 MAD", NULL, "AIF4_MAD Mixer"}, - /* Virtual input widget Mixer */ + /* Virtual input widget Mixer SLIMBUS*/ {"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0"}, {"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1"}, {"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2"}, @@ -68,6 +64,21 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { {"AIF4_MAD Mixer", "SLIM TX13", "SLIM TX13"}, + /* CDC Tx interface with SLIMBUS */ + {"SLIM TX0", NULL, "CDC_IF TX0 MUX"}, + {"SLIM TX1", NULL, "CDC_IF TX1 MUX"}, + {"SLIM TX2", NULL, "CDC_IF TX2 MUX"}, + {"SLIM TX3", NULL, "CDC_IF TX3 MUX"}, + {"SLIM TX4", NULL, "CDC_IF TX4 MUX"}, + {"SLIM TX5", NULL, "CDC_IF TX5 MUX"}, + {"SLIM TX6", NULL, "CDC_IF TX6 MUX"}, + {"SLIM TX7", NULL, "CDC_IF TX7 MUX"}, + {"SLIM TX8", NULL, "CDC_IF TX8 MUX"}, + {"SLIM TX9", NULL, "CDC_IF TX9 MUX"}, + {"SLIM TX10", NULL, "CDC_IF TX10 MUX"}, + {"SLIM TX11", NULL, "CDC_IF TX11 MUX"}, + {"SLIM TX13", NULL, "CDC_IF TX13 MUX"}, + {"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"}, {"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"}, {"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"}, @@ -113,10 +124,100 @@ const struct snd_soc_dapm_route tavil_slim_audio_map[] = { {"SLIM RX6", NULL, "SLIM RX6 MUX"}, {"SLIM RX7", NULL, "SLIM RX7 MUX"}, + /* CDC Rx interface with SLIMBUS */ + {"CDC_IF RX0 MUX", "SLIM RX0", "SLIM RX0"}, + {"CDC_IF RX1 MUX", "SLIM RX1", "SLIM RX1"}, + {"CDC_IF RX2 MUX", "SLIM RX2", "SLIM RX2"}, + {"CDC_IF RX3 MUX", "SLIM RX3", "SLIM RX3"}, + {"CDC_IF RX4 MUX", "SLIM RX4", "SLIM RX4"}, + {"CDC_IF RX5 MUX", "SLIM RX5", "SLIM RX5"}, + {"CDC_IF RX6 MUX", "SLIM RX6", "SLIM RX6"}, + {"CDC_IF RX7 MUX", "SLIM RX7", "SLIM RX7"}, + + /* VI Feedback */ + {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"}, + {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"}, + {"AIF4 VI", NULL, "AIF4_VI Mixer"}, + +}; + +const struct snd_soc_dapm_route tavil_i2s_audio_map[] = { + + /* Virtual input widget Mixer I2S*/ + {"AIF1_CAP Mixer", "I2S TX1", "I2S TX1"}, + {"AIF1_CAP Mixer", "I2S TX2", "I2S TX2"}, + {"AIF1_CAP Mixer", "I2S TX3", "I2S TX3"}, + {"AIF1_CAP Mixer", "I2S TX4", "I2S TX4"}, + {"AIF1_CAP Mixer", "I2S TX5", "I2S TX5"}, + {"AIF1_CAP Mixer", "I2S TX6", "I2S TX6"}, + {"AIF1_CAP Mixer", "I2S TX7", "I2S TX7"}, + + {"AIF2_CAP Mixer", "I2S TX8", "I2S TX8"}, + {"AIF2_CAP Mixer", "I2S TX11", "I2S TX11"}, + + {"AIF3_CAP Mixer", "I2S TX0", "I2S TX0"}, + {"AIF3_CAP Mixer", "I2S TX1", "I2S TX1"}, + + /* CDC Tx interface with I2S */ + {"I2S TX0", NULL, "CDC_IF TX0 MUX"}, + {"I2S TX1", NULL, "CDC_IF TX1 MUX"}, + {"I2S TX2", NULL, "CDC_IF TX2 MUX"}, + {"I2S TX3", NULL, "CDC_IF TX3 MUX"}, + {"I2S TX4", NULL, "CDC_IF TX4 MUX"}, + {"I2S TX5", NULL, "CDC_IF TX5 MUX"}, + {"I2S TX6", NULL, "CDC_IF TX6 MUX"}, + {"I2S TX7", NULL, "CDC_IF TX7 MUX"}, + {"I2S TX8", NULL, "CDC_IF TX8 MUX"}, + {"I2S TX11", NULL, "CDC_IF TX11 MUX"}, + + {"I2S RX0 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX1 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX2 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX3 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX4 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX5 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX6 MUX", "AIF1_PB", "AIF1 PB"}, + {"I2S RX7 MUX", "AIF1_PB", "AIF1 PB"}, + + {"I2S RX2 MUX", "AIF2_PB", "AIF2 PB"}, + {"I2S RX3 MUX", "AIF2_PB", "AIF2 PB"}, + + {"I2S RX4 MUX", "AIF3_PB", "AIF3 PB"}, + {"I2S RX5 MUX", "AIF3_PB", "AIF3 PB"}, + + {"I2S RX0", NULL, "I2S RX0 MUX"}, + {"I2S RX1", NULL, "I2S RX1 MUX"}, + {"I2S RX2", NULL, "I2S RX2 MUX"}, + {"I2S RX3", NULL, "I2S RX3 MUX"}, + {"I2S RX4", NULL, "I2S RX4 MUX"}, + {"I2S RX5", NULL, "I2S RX5 MUX"}, + {"I2S RX6", NULL, "I2S RX6 MUX"}, + {"I2S RX7", NULL, "I2S RX7 MUX"}, + + /* CDC Rx interface with I2S */ + {"CDC_IF RX0 MUX", "I2S RX0", "I2S RX0"}, + {"CDC_IF RX1 MUX", "I2S RX1", "I2S RX1"}, + {"CDC_IF RX2 MUX", "I2S RX2", "I2S RX2"}, + {"CDC_IF RX3 MUX", "I2S RX3", "I2S RX3"}, + {"CDC_IF RX4 MUX", "I2S RX4", "I2S RX4"}, + {"CDC_IF RX5 MUX", "I2S RX5", "I2S RX5"}, + {"CDC_IF RX6 MUX", "I2S RX6", "I2S RX6"}, + {"CDC_IF RX7 MUX", "I2S RX7", "I2S RX7"}, + }; const struct snd_soc_dapm_route tavil_audio_map[] = { + /* + * AIF CAP to Mixer routes are common + * for both SLIM as well as I2S + */ + + /* Virtual input widgets */ + {"AIF1 CAP", NULL, "AIF1_CAP Mixer"}, + {"AIF2 CAP", NULL, "AIF2_CAP Mixer"}, + {"AIF3 CAP", NULL, "AIF3_CAP Mixer"}, + /* WDMA3 */ {"WDMA3 PORT0 MUX", "DEC0", "ADC MUX0"}, {"WDMA3 PORT0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, @@ -195,26 +296,6 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"MAD_CPE_OUT1", NULL, "MAD_CPE1"}, {"MAD_CPE_OUT2", NULL, "MAD_CPE2"}, - /* VI Feedback */ - {"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"}, - {"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"}, - {"AIF4 VI", NULL, "AIF4_VI Mixer"}, - - /* CDC Tx interface with SLIMBUS */ - {"SLIM TX0", NULL, "CDC_IF TX0 MUX"}, - {"SLIM TX1", NULL, "CDC_IF TX1 MUX"}, - {"SLIM TX2", NULL, "CDC_IF TX2 MUX"}, - {"SLIM TX3", NULL, "CDC_IF TX3 MUX"}, - {"SLIM TX4", NULL, "CDC_IF TX4 MUX"}, - {"SLIM TX5", NULL, "CDC_IF TX5 MUX"}, - {"SLIM TX6", NULL, "CDC_IF TX6 MUX"}, - {"SLIM TX7", NULL, "CDC_IF TX7 MUX"}, - {"SLIM TX8", NULL, "CDC_IF TX8 MUX"}, - {"SLIM TX9", NULL, "CDC_IF TX9 MUX"}, - {"SLIM TX10", NULL, "CDC_IF TX10 MUX"}, - {"SLIM TX11", NULL, "CDC_IF TX11 MUX"}, - {"SLIM TX13", NULL, "CDC_IF TX13 MUX"}, - {"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"}, {"CDC_IF TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"}, {"CDC_IF TX0 MUX", "DEC0_192", "ADC US MUX0"}, @@ -568,16 +649,6 @@ const struct snd_soc_dapm_route tavil_audio_map[] = { {"ADC3", NULL, "AMIC3"}, {"ADC4", NULL, "AMIC4_5 SEL"}, - /* CDC Rx interface with SLIMBUS */ - {"CDC_IF RX0 MUX", "SLIM RX0", "SLIM RX0"}, - {"CDC_IF RX1 MUX", "SLIM RX1", "SLIM RX1"}, - {"CDC_IF RX2 MUX", "SLIM RX2", "SLIM RX2"}, - {"CDC_IF RX3 MUX", "SLIM RX3", "SLIM RX3"}, - {"CDC_IF RX4 MUX", "SLIM RX4", "SLIM RX4"}, - {"CDC_IF RX5 MUX", "SLIM RX5", "SLIM RX5"}, - {"CDC_IF RX6 MUX", "SLIM RX6", "SLIM RX6"}, - {"CDC_IF RX7 MUX", "SLIM RX7", "SLIM RX7"}, - {"RX INT0_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"}, {"RX INT0_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"}, {"RX INT0_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"}, diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index e7cec2b4d348..1be2f66ec7af 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -1273,6 +1273,93 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol, return 0; } +static int i2s_tx_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tavil_p->tx_port_value; + return 0; +} + +static int i2s_tx_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct snd_soc_dapm_update *update = NULL; + struct soc_multi_mixer_control *mixer = + (struct soc_multi_mixer_control *)kcontrol->private_value; + u32 dai_id = widget->shift; + u32 port_id = mixer->shift; + u32 enable = ucontrol->value.integer.value[0]; + u32 vtable; + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, + widget->name, ucontrol->id.name, tavil_p->tx_port_value, + widget->shift, ucontrol->value.integer.value[0]); + + mutex_lock(&tavil_p->codec_mutex); + if (dai_id >= ARRAY_SIZE(vport_slim_check_table)) { + dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n", + __func__, dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + vtable = vport_slim_check_table[dai_id]; + + switch (dai_id) { + case AIF1_CAP: + case AIF2_CAP: + case AIF3_CAP: + /* only add to the list if value not set */ + if (enable && !(tavil_p->tx_port_value & 1 << port_id)) { + if (wcd9xxx_tx_vport_validation(vtable, port_id, + tavil_p->dai, NUM_CODEC_DAIS)) { + dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n", + __func__, port_id); + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + tavil_p->tx_port_value |= 1 << port_id; + } else if (!enable && (tavil_p->tx_port_value & + 1 << port_id)) { + tavil_p->tx_port_value &= ~(1 << port_id); + } else { + if (enable) + dev_dbg(codec->dev, "%s: TX%u port is used by\n" + "this virtual port\n", + __func__, port_id); + else + dev_dbg(codec->dev, "%s: TX%u port is not used by\n" + "this virtual port\n", + __func__, port_id); + /* avoid update power function */ + mutex_unlock(&tavil_p->codec_mutex); + return 0; + } + break; + default: + dev_err(codec->dev, "Unknown AIF %d\n", dai_id); + mutex_unlock(&tavil_p->codec_mutex); + return -EINVAL; + } + dev_dbg(codec->dev, "%s: name %s sname %s updated value %u shift %d\n", + __func__, widget->name, widget->sname, tavil_p->tx_port_value, + widget->shift); + + mutex_unlock(&tavil_p->codec_mutex); + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update); + + return 0; +} + static int slim_rx_mux_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1477,9 +1564,192 @@ static void tavil_codec_mute_dsd(struct snd_soc_codec *codec, } } -static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w, +static int tavil_codec_set_i2s_rx_ch(struct snd_soc_dapm_widget *w, + u32 i2s_reg, bool up) +{ + int rx_fs_rate = -EINVAL; + int i2s_bit_mode; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + + dai = &tavil_p->dai[w->shift]; + dev_dbg(tavil_p->dev, "%s: %d up/down, %d width, %d rate\n", + __func__, up, dai->bit_width, dai->rate); + if (up) { + if (dai->bit_width == 16) + i2s_bit_mode = 0x01; + else + i2s_bit_mode = 0x00; + + switch (dai->rate) { + case 8000: + rx_fs_rate = 0; + break; + case 16000: + rx_fs_rate = 1; + break; + case 32000: + rx_fs_rate = 2; + break; + case 48000: + rx_fs_rate = 3; + break; + case 96000: + rx_fs_rate = 4; + break; + case 192000: + rx_fs_rate = 5; + break; + case 384000: + rx_fs_rate = 6; + break; + default: + dev_err(tavil_p->dev, "%s: Invalid RX sample rate: %d\n", + __func__, dai->rate); + return -EINVAL; + }; + snd_soc_update_bits(codec, i2s_reg, + 0x40, i2s_bit_mode << 6); + snd_soc_update_bits(codec, i2s_reg, + 0x3c, (rx_fs_rate << 2)); + } else { + snd_soc_update_bits(codec, i2s_reg, + 0x40, 0x00); + snd_soc_update_bits(codec, i2s_reg, + 0x3c, 0x00); + } + return 0; +} + +static int tavil_codec_set_i2s_tx_ch(struct snd_soc_dapm_widget *w, + u32 i2s_reg, bool up) +{ + int tx_fs_rate = -EINVAL; + int i2s_bit_mode; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct wcd9xxx_codec_dai_data *dai; + + dai = &tavil_p->dai[w->shift]; + if (up) { + if (dai->bit_width == 16) + i2s_bit_mode = 0x01; + else + i2s_bit_mode = 0x00; + + snd_soc_update_bits(codec, i2s_reg, 0x40, i2s_bit_mode << 6); + + switch (dai->rate) { + case 8000: + tx_fs_rate = 0; + break; + case 16000: + tx_fs_rate = 1; + break; + case 32000: + tx_fs_rate = 2; + break; + case 48000: + tx_fs_rate = 3; + break; + case 96000: + tx_fs_rate = 4; + break; + case 192000: + tx_fs_rate = 5; + break; + case 384000: + tx_fs_rate = 6; + break; + default: + dev_err(tavil_p->dev, "%s: Invalid sample rate: %d\n", + __func__, dai->rate); + return -EINVAL; + }; + + snd_soc_update_bits(codec, i2s_reg, 0x3c, tx_fs_rate << 2); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x03, 0x01); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x0C, 0x01); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_0_CFG, + 0x03, 0x01); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_1_CFG, + 0x05, 0x05); + } else { + snd_soc_update_bits(codec, i2s_reg, 0x40, 0x00); + snd_soc_update_bits(codec, i2s_reg, 0x3c, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x03, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX0_CFG, + 0x0C, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_0_CFG, + 0x03, 0x00); + + snd_soc_update_bits(codec, + WCD934X_DATA_HUB_I2S_TX1_1_CFG, + 0x05, 0x00); + } + return 0; +} + +static int tavil_codec_enable_rx_i2c(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + int ret = -EINVAL; + u32 i2s_reg; + + switch (tavil_p->rx_port_value[w->shift]) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(codec->dev, "%s Invalid i2s Id received", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret = tavil_codec_set_i2s_rx_ch(w, i2s_reg, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = tavil_codec_set_i2s_rx_ch(w, i2s_reg, false); + break; + } + + return ret; +} + +static int tavil_codec_enable_rx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) { struct wcd9xxx *core; struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); @@ -1499,6 +1769,11 @@ static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w, dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n", __func__, w->name, w->shift, event); + if (tavil_p->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tavil_codec_enable_rx_i2c(w, kcontrol, event); + return ret; + } + switch (event) { case SND_SOC_DAPM_POST_PMU: dai->bus_down_in_recovery = false; @@ -1530,9 +1805,48 @@ static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w, return ret; } -static int tavil_codec_enable_slimtx(struct snd_soc_dapm_widget *w, +static int tavil_codec_enable_tx_i2c(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + int ret = -EINVAL; + u32 i2s_reg; + + switch (tavil_p->rx_port_value[w->shift]) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(codec->dev, "%s Invalid i2s Id received", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret = tavil_codec_set_i2s_tx_ch(w, i2s_reg, true); + break; + case SND_SOC_DAPM_POST_PMD: + ret = tavil_codec_set_i2s_tx_ch(w, i2s_reg, false); + break; + } + + return ret; +} + +static int tavil_codec_enable_tx(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) { struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); @@ -1548,6 +1862,11 @@ static int tavil_codec_enable_slimtx(struct snd_soc_dapm_widget *w, dai = &tavil_p->dai[w->shift]; core = dev_get_drvdata(codec->dev->parent); + if (tavil_p->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + ret = tavil_codec_enable_tx_i2c(w, kcontrol, event); + return ret; + } + switch (event) { case SND_SOC_DAPM_POST_PMU: dai->bus_down_in_recovery = false; @@ -2234,6 +2553,83 @@ static int tavil_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w, return 0; } +static int i2s_rx_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + ucontrol->value.enumerated.item[0] = + tavil_p->rx_port_value[widget->shift]; + return 0; +} + +static int i2s_rx_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + struct snd_soc_dapm_update *update = NULL; + unsigned int rx_port_value; + u32 port_id = widget->shift; + + tavil_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0]; + rx_port_value = tavil_p->rx_port_value[port_id]; + + dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n", + __func__, widget->name, ucontrol->id.name, + rx_port_value, widget->shift, + ucontrol->value.integer.value[0]); + + snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, + rx_port_value, e, update); + return 0; +} + +static int tavil_codec_enable_i2s_path(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + int ret = 0; + u32 i2s_reg; + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec); + + switch (tavil_p->rx_port_value[w->shift]) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(codec->dev, "%s Invalid i2s Id received", __func__); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + ret = snd_soc_update_bits(codec, i2s_reg, 0x01, 0x01); + break; + case SND_SOC_DAPM_POST_PMD: + ret = snd_soc_update_bits(codec, i2s_reg, 0x01, 0x00); + break; + } + + return ret; +} + static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -6285,29 +6681,45 @@ static const char *const slim_rx_mux_text[] = { "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB" }; +static const char *const i2s_rx01_mux_text[] = { + "ZERO", "AIF1_PB" +}; + +static const char *const i2s_rx23_mux_text[] = { + "ZERO", "AIF1_PB", "AIF2_PB" +}; + +static const char *const i2s_rx45_mux_text[] = { + "ZERO", "AIF1_PB", "AIF3_PB" +}; + +static const char *const i2s_rx67_mux_text[] = { + "ZERO", "AIF1_PB" +}; + static const char *const cdc_if_rx0_mux_text[] = { - "SLIM RX0", "I2S_0 RX0" + "SLIM RX0", "I2S RX0" }; static const char *const cdc_if_rx1_mux_text[] = { - "SLIM RX1", "I2S_0 RX1" + "SLIM RX1", "I2S RX1" }; static const char *const cdc_if_rx2_mux_text[] = { - "SLIM RX2", "I2S_0 RX2" + "SLIM RX2", "I2S RX2" }; static const char *const cdc_if_rx3_mux_text[] = { - "SLIM RX3", "I2S_0 RX3" + "SLIM RX3", "I2S RX3" }; static const char *const cdc_if_rx4_mux_text[] = { - "SLIM RX4", "I2S_0 RX4" + "SLIM RX4", "I2S RX4" }; static const char *const cdc_if_rx5_mux_text[] = { - "SLIM RX5", "I2S_0 RX5" + "SLIM RX5", "I2S RX5" }; static const char *const cdc_if_rx6_mux_text[] = { - "SLIM RX6", "I2S_0 RX6" + "SLIM RX6", "I2S RX6" }; static const char *const cdc_if_rx7_mux_text[] = { - "SLIM RX7", "I2S_0 RX7" + "SLIM RX7", "I2S RX7" }; static const char * const asrc0_mux_text[] = { @@ -6370,7 +6782,7 @@ static const struct snd_kcontrol_new aif4_vi_mixer[] = { tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put), }; -static const struct snd_kcontrol_new aif1_cap_mixer[] = { +static const struct snd_kcontrol_new aif1_slim_cap_mixer[] = { SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, slim_tx_mixer_get, slim_tx_mixer_put), SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, @@ -6399,7 +6811,24 @@ static const struct snd_kcontrol_new aif1_cap_mixer[] = { slim_tx_mixer_get, slim_tx_mixer_put), }; -static const struct snd_kcontrol_new aif2_cap_mixer[] = { +static const struct snd_kcontrol_new aif1_i2s_cap_mixer[] = { + SOC_SINGLE_EXT("I2S TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif2_slim_cap_mixer[] = { SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, slim_tx_mixer_get, slim_tx_mixer_put), SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, @@ -6428,7 +6857,14 @@ static const struct snd_kcontrol_new aif2_cap_mixer[] = { slim_tx_mixer_get, slim_tx_mixer_put), }; -static const struct snd_kcontrol_new aif3_cap_mixer[] = { +static const struct snd_kcontrol_new aif2_i2s_cap_mixer[] = { + SOC_SINGLE_EXT("I2S TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif3_slim_cap_mixer[] = { SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, slim_tx_mixer_get, slim_tx_mixer_put), SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, @@ -6457,9 +6893,16 @@ static const struct snd_kcontrol_new aif3_cap_mixer[] = { slim_tx_mixer_get, slim_tx_mixer_put), }; -static const struct snd_kcontrol_new aif4_mad_mixer[] = { +static const struct snd_kcontrol_new aif3_i2s_cap_mixer[] = { + SOC_SINGLE_EXT("I2S TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), + SOC_SINGLE_EXT("I2S TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0, + i2s_tx_mixer_get, i2s_tx_mixer_put), +}; + +static const struct snd_kcontrol_new aif4_slim_mad_mixer[] = { SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0, - slim_tx_mixer_get, slim_tx_mixer_put), + slim_tx_mixer_get, slim_tx_mixer_put), }; WCD_DAPM_ENUM_EXT(slim_rx0, SND_SOC_NOPM, 0, slim_rx_mux_text, @@ -6768,6 +7211,22 @@ WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text); WCD_DAPM_ENUM(anc0_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text); WCD_DAPM_ENUM(anc1_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 3, anc1_fb_mux_text); +WCD_DAPM_ENUM_EXT(i2s_rx0, SND_SOC_NOPM, 0, i2s_rx01_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx1, SND_SOC_NOPM, 0, i2s_rx01_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx2, SND_SOC_NOPM, 0, i2s_rx23_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx3, SND_SOC_NOPM, 0, i2s_rx23_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx4, SND_SOC_NOPM, 0, i2s_rx45_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx5, SND_SOC_NOPM, 0, i2s_rx45_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx6, SND_SOC_NOPM, 0, i2s_rx67_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); +WCD_DAPM_ENUM_EXT(i2s_rx7, SND_SOC_NOPM, 0, i2s_rx67_mux_text, + i2s_rx_mux_get, i2s_rx_mux_put); WCD_DAPM_ENUM(wdma3_port0, WCD934X_DMA_WDMA3_PRT_CFG, 0, wdma3_port0_text); WCD_DAPM_ENUM(wdma3_port1, WCD934X_DMA_WDMA3_PRT_CFG, 1, wdma3_port1_text); @@ -6852,6 +7311,89 @@ static const struct snd_kcontrol_new rx_int4_asrc_switch[] = { static const struct snd_kcontrol_new wdma3_onoff_switch = SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0); +static const struct snd_soc_dapm_widget tavil_dapm_i2s_widgets[] = { + + SND_SOC_DAPM_MUX_E("I2S RX0 MUX", SND_SOC_NOPM, WCD934X_RX0, 0, + &i2s_rx0_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX1 MUX", SND_SOC_NOPM, WCD934X_RX1, 0, + &i2s_rx1_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX2 MUX", SND_SOC_NOPM, WCD934X_RX2, 0, + &i2s_rx2_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX3 MUX", SND_SOC_NOPM, WCD934X_RX3, 0, + &i2s_rx3_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX4 MUX", SND_SOC_NOPM, WCD934X_RX4, 0, + &i2s_rx4_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX5 MUX", SND_SOC_NOPM, WCD934X_RX5, 0, + &i2s_rx5_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX6 MUX", SND_SOC_NOPM, WCD934X_RX6, 0, + &i2s_rx6_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MUX_E("I2S RX7 MUX", SND_SOC_NOPM, WCD934X_RX7, 0, + &i2s_rx7_mux, tavil_codec_enable_i2s_path, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("I2S RX0", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX1", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX2", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX3", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX4", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX5", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX6", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("I2S RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MIXER_E("I2S TX0", SND_SOC_NOPM, WCD934X_TX0, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX1", SND_SOC_NOPM, WCD934X_TX1, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX2", SND_SOC_NOPM, WCD934X_TX2, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX3", SND_SOC_NOPM, WCD934X_TX3, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX4", SND_SOC_NOPM, WCD934X_TX4, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX5", SND_SOC_NOPM, WCD934X_TX5, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX6", SND_SOC_NOPM, WCD934X_TX6, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX7", SND_SOC_NOPM, WCD934X_TX7, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX8", SND_SOC_NOPM, WCD934X_TX8, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_MIXER_E("I2S TX11", SND_SOC_NOPM, WCD934X_TX11, 0, NULL, 0, + tavil_codec_enable_i2s_path, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_i2s_cap_mixer, ARRAY_SIZE(aif1_i2s_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_i2s_cap_mixer, ARRAY_SIZE(aif2_i2s_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_i2s_cap_mixer, ARRAY_SIZE(aif3_i2s_cap_mixer)), +}; + static int tavil_dsd_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -6917,19 +7459,22 @@ static const struct snd_kcontrol_new lo2_mixer[] = { tavil_dsd_mixer_get, tavil_dsd_mixer_put), }; -static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { - SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, - AIF1_PB, 0, tavil_codec_enable_slimrx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, - AIF2_PB, 0, tavil_codec_enable_slimrx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, - AIF3_PB, 0, tavil_codec_enable_slimrx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +static const struct snd_soc_dapm_widget tavil_dapm_slim_widgets[] = { SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM, - AIF4_PB, 0, tavil_codec_enable_slimrx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + AIF4_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, + AIF4_VIFEED, 0, + tavil_codec_enable_slimvi_feedback, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0, + aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)), + SND_SOC_DAPM_INPUT("VIINPUT"), WCD_DAPM_MUX("SLIM RX0 MUX", WCD934X_RX0, slim_rx0), WCD_DAPM_MUX("SLIM RX1 MUX", WCD934X_RX1, slim_rx1), @@ -6949,6 +7494,31 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, + aif1_slim_cap_mixer, + ARRAY_SIZE(aif1_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, + aif2_slim_cap_mixer, + ARRAY_SIZE(aif2_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, + aif3_slim_cap_mixer, + ARRAY_SIZE(aif3_slim_cap_mixer)), + SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, + aif4_slim_mad_mixer, + ARRAY_SIZE(aif4_slim_mad_mixer)), +}; + +static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM, + AIF1_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM, + AIF2_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM, + AIF3_PB, 0, tavil_codec_enable_rx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + WCD_DAPM_MUX("CDC_IF RX0 MUX", WCD934X_RX0, cdc_if_rx0), WCD_DAPM_MUX("CDC_IF RX1 MUX", WCD934X_RX1, cdc_if_rx1), WCD_DAPM_MUX("CDC_IF RX2 MUX", WCD934X_RX2, cdc_if_rx2), @@ -7237,33 +7807,14 @@ static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = { SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM, - AIF1_CAP, 0, tavil_codec_enable_slimtx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + AIF1_CAP, 0, tavil_codec_enable_tx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM, - AIF2_CAP, 0, tavil_codec_enable_slimtx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + AIF2_CAP, 0, tavil_codec_enable_tx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM, - AIF3_CAP, 0, tavil_codec_enable_slimtx, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0, - aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)), - SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0, - aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)), - SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0, - aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)), - SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0, - aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)), - - SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM, - AIF4_VIFEED, 0, tavil_codec_enable_slimvi_feedback, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), - - SND_SOC_DAPM_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0, - SND_SOC_NOPM, 0, 0), - - SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0, - aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)), - SND_SOC_DAPM_INPUT("VIINPUT"), + AIF3_CAP, 0, tavil_codec_enable_tx, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER("SLIM TX0", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_MIXER("SLIM TX1", SND_SOC_NOPM, 0, 0, NULL, 0), @@ -8047,6 +8598,46 @@ static int tavil_hw_params(struct snd_pcm_substream *substream, return 0; } +static int tavil_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); + u32 i2s_reg; + + switch (dai->id) { + case AIF1_PB: + case AIF1_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_0_CTL; + break; + case AIF2_PB: + case AIF2_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_1_CTL; + break; + case AIF3_PB: + case AIF3_CAP: + i2s_reg = WCD934X_DATA_HUB_I2S_2_CTL; + break; + default: + dev_err(dai->codec->dev, "%s Invalid i2s Id", __func__); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* CPU is master */ + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + snd_soc_update_bits(dai->codec, i2s_reg, 0x2, 0x0); + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* CPU is slave */ + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + snd_soc_update_bits(dai->codec, i2s_reg, 0x2, 0x2); + break; + default: + return -EINVAL; + } + return 0; +} + static struct snd_soc_dai_ops tavil_dai_ops = { .startup = tavil_startup, .shutdown = tavil_shutdown, @@ -8056,13 +8647,21 @@ static struct snd_soc_dai_ops tavil_dai_ops = { .get_channel_map = tavil_get_channel_map, }; +static struct snd_soc_dai_ops tavil_i2s_dai_ops = { + .startup = tavil_startup, + .shutdown = tavil_shutdown, + .hw_params = tavil_hw_params, + .prepare = tavil_prepare, + .set_fmt = tavil_set_dai_fmt, +}; + static struct snd_soc_dai_ops tavil_vi_dai_ops = { .hw_params = tavil_vi_hw_params, .set_channel_map = tavil_set_channel_map, .get_channel_map = tavil_get_channel_map, }; -static struct snd_soc_dai_driver tavil_dai[] = { +static struct snd_soc_dai_driver tavil_slim_dai[] = { { .name = "tavil_rx1", .id = AIF1_PB, @@ -8191,6 +8790,93 @@ static struct snd_soc_dai_driver tavil_dai[] = { }, }; +static struct snd_soc_dai_driver tavil_i2s_dai[] = { + { + .name = "tavil_i2s_rx1", + .id = AIF1_PB, + .playback = { + .stream_name = "AIF1 Playback", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_tx1", + .id = AIF1_CAP, + .capture = { + .stream_name = "AIF1 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_rx2", + .id = AIF2_PB, + .playback = { + .stream_name = "AIF2 Playback", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_tx2", + .id = AIF2_CAP, + .capture = { + .stream_name = "AIF2 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_rx3", + .id = AIF3_PB, + .playback = { + .stream_name = "AIF3 Playback", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_S32_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, + { + .name = "tavil_i2s_tx3", + .id = AIF3_CAP, + .capture = { + .stream_name = "AIF3 Capture", + .rates = WCD934X_RATES_MASK, + .formats = WCD934X_FORMATS_S16_S24_LE, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &tavil_i2s_dai_ops, + }, +}; + static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil) { mutex_lock(&tavil->power_lock); @@ -8747,6 +9433,22 @@ static void tavil_codec_init_reg(struct tavil_priv *priv) } } +static const struct tavil_reg_mask_val tavil_codec_reg_i2c_defaults[] = { + {WCD934X_CLK_SYS_MCLK_PRG, 0x40, 0x00}, + {WCD934X_CODEC_RPM_CLK_GATE, 0x03, 0x01}, + {WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x00}, + {WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x05, 0x05}, + {WCD934X_DATA_HUB_RX0_CFG, 0x71, 0x31}, + {WCD934X_DATA_HUB_RX1_CFG, 0x71, 0x31}, + {WCD934X_DATA_HUB_RX2_CFG, 0x03, 0x01}, + {WCD934X_DATA_HUB_RX3_CFG, 0x03, 0x01}, + {WCD934X_DATA_HUB_I2S_TX0_CFG, 0x01, 0x01}, + {WCD934X_DATA_HUB_I2S_TX0_CFG, 0x04, 0x01}, + {WCD934X_DATA_HUB_I2S_TX1_0_CFG, 0x01, 0x01}, + {WCD934X_DATA_HUB_I2S_TX1_1_CFG, 0x05, 0x05}, + {WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN, 0x1, 0x1}, +}; + static void tavil_update_reg_defaults(struct tavil_priv *tavil) { u32 i; @@ -8758,6 +9460,15 @@ static void tavil_update_reg_defaults(struct tavil_priv *tavil) tavil_codec_reg_defaults[i].reg, tavil_codec_reg_defaults[i].mask, tavil_codec_reg_defaults[i].val); + + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_i2c_defaults); i++) { + regmap_update_bits(wcd9xxx->regmap, + tavil_codec_reg_i2c_defaults[i].reg, + tavil_codec_reg_i2c_defaults[i].mask, + tavil_codec_reg_i2c_defaults[i].val); + } + } } static void tavil_update_cpr_defaults(struct tavil_priv *tavil) @@ -9402,19 +10113,33 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) goto err_hwdep; } - snd_soc_dapm_add_routes(dapm, tavil_slim_audio_map, - ARRAY_SIZE(tavil_slim_audio_map)); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + snd_soc_dapm_new_controls(dapm, tavil_dapm_i2s_widgets, + ARRAY_SIZE(tavil_dapm_i2s_widgets)); + else + snd_soc_dapm_new_controls(dapm, tavil_dapm_slim_widgets, + ARRAY_SIZE(tavil_dapm_slim_widgets)); + + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + snd_soc_dapm_add_routes(dapm, tavil_slim_audio_map, + ARRAY_SIZE(tavil_slim_audio_map)); + else + snd_soc_dapm_add_routes(dapm, tavil_i2s_audio_map, + ARRAY_SIZE(tavil_i2s_audio_map)); + for (i = 0; i < NUM_CODEC_DAIS; i++) { INIT_LIST_HEAD(&tavil->dai[i].wcd9xxx_ch_list); init_waitqueue_head(&tavil->dai[i].dai_wait); } - tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = - control->slim_slave->laddr; - tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = - control->slim->laddr; - tavil_slimbus_slave_port_cfg.slave_port_mapping[0] = - WCD934X_TX13; - tavil_init_slim_slave_cfg(codec); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = + control->slim_slave->laddr; + tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = + control->slim->laddr; + tavil_slimbus_slave_port_cfg.slave_port_mapping[0] = + WCD934X_TX13; + tavil_init_slim_slave_cfg(codec); + } control->num_rx_port = WCD934X_RX_MAX; control->rx_chs = ptr; @@ -9469,9 +10194,11 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); - snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); - snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); - snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); + snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); + snd_soc_dapm_ignore_suspend(dapm, "VIfeed"); + } snd_soc_dapm_sync(dapm); @@ -9577,6 +10304,39 @@ static const struct dev_pm_ops tavil_pm_ops = { }; #endif +static int wcd9xxx_swrm_i2c_bulk_write(struct wcd9xxx *wcd9xxx, + struct wcd9xxx_reg_val *bulk_reg, + size_t len) +{ + int i, ret = 0; + unsigned short swr_wr_addr_base; + unsigned short swr_wr_data_base; + + swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0; + swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0; + + for (i = 0; i < (len * 2); i += 2) { + /* First Write the Data to register */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_data_base, bulk_reg[i].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Data Failure\n", + __func__); + break; + } + /* Next Write Address */ + ret = regmap_bulk_write(wcd9xxx->regmap, + swr_wr_addr_base, + bulk_reg[i+1].buf, 4); + if (ret < 0) { + dev_err(wcd9xxx->dev, "%s: WR Addr Failure\n", + __func__); + break; + } + } + return ret; +} + static int tavil_swrm_read(void *handle, int reg) { struct tavil_priv *tavil; @@ -9655,8 +10415,11 @@ static int tavil_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len) } mutex_lock(&tavil->swr.write_mutex); - ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, - (len * 2), false); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, + (len * 2), false); + else + ret = wcd9xxx_swrm_i2c_bulk_write(wcd9xxx, bulk_reg, len); if (ret) { dev_err(tavil->dev, "%s: swrm bulk write failed, ret: %d\n", __func__, ret); @@ -9695,7 +10458,10 @@ static int tavil_swrm_write(void *handle, int reg, int val) bulk_reg[1].bytes = 4; mutex_lock(&tavil->swr.write_mutex); - ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) + ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false); + else + ret = wcd9xxx_swrm_i2c_bulk_write(wcd9xxx, bulk_reg, 1); if (ret < 0) dev_err(tavil->dev, "%s: WR Data Failure\n", __func__); mutex_unlock(&tavil->swr.write_mutex); @@ -10081,6 +10847,21 @@ static int tavil_probe(struct platform_device *pdev) if (!tavil) return -ENOMEM; + tavil->intf_type = wcd9xxx_get_intf_type(); + if (tavil->intf_type != WCD9XXX_INTERFACE_TYPE_I2C && + tavil->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + devm_kfree(&pdev->dev, tavil); + return -EPROBE_DEFER; + } + + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { + if (apr_get_subsys_state() == APR_SUBSYS_DOWN) { + dev_err(&pdev->dev, "%s: dsp down\n", __func__); + devm_kfree(&pdev->dev, tavil); + return -EPROBE_DEFER; + } + } + platform_set_drvdata(pdev, tavil); tavil->wcd9xxx = dev_get_drvdata(pdev->dev.parent); @@ -10161,8 +10942,15 @@ static int tavil_probe(struct platform_device *pdev) tavil_update_cpr_defaults(tavil); /* Register with soc framework */ - ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil, - tavil_dai, ARRAY_SIZE(tavil_dai)); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil, + tavil_i2s_dai, + ARRAY_SIZE(tavil_i2s_dai)); + else + ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil, + tavil_slim_dai, + ARRAY_SIZE(tavil_slim_dai)); + if (ret) { dev_err(&pdev->dev, "%s: Codec registration failed\n", __func__); From 268fa36699b43c20c1ab279584ba09d5360986f9 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Sun, 10 Dec 2017 23:38:31 -0800 Subject: [PATCH 138/276] config: sdxpoorwills: Enable dsp configs Enable qdsp6 notifier, ssr and pdr configs that are needed to communicate with modem and qdsp6. Change-Id: Iae7b4dbd3f0d7fb616c4bc11b39a2c360b9a3b43 Signed-off-by: Karthikeyan Mani --- config/sdxpoorwillsauto.conf | 3 +++ config/sdxpoorwillsautoconf.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/config/sdxpoorwillsauto.conf b/config/sdxpoorwillsauto.conf index dee34a08be5d..1d0c19d0e627 100644 --- a/config/sdxpoorwillsauto.conf +++ b/config/sdxpoorwillsauto.conf @@ -10,6 +10,9 @@ CONFIG_SND_SOC_WCD934X_MBHC=y CONFIG_SND_SOC_WCD934X_DSD=y CONFIG_MSM_QDSP6V2_CODECS=y CONFIG_MSM_QDSP6_APRV3_GLINK=y +CONFIG_MSM_QDSP6_NOTIFIER=y +CONFIG_MSM_QDSP6_SSR=y +CONFIG_MSM_QDSP6_PDR=y CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y CONFIG_MSM_ADSP_LOADER=y CONFIG_REGMAP_SWR=y diff --git a/config/sdxpoorwillsautoconf.h b/config/sdxpoorwillsautoconf.h index 7b7dac403b63..2bce8d48b61c 100644 --- a/config/sdxpoorwillsautoconf.h +++ b/config/sdxpoorwillsautoconf.h @@ -23,6 +23,9 @@ #define CONFIG_SND_SOC_WCD934X_DSD 1 #define CONFIG_MSM_QDSP6V2_CODECS 1 #define CONFIG_MSM_QDSP6_APRV3_GLINK 1 +#define CONFIG_MSM_QDSP6_NOTIFIER 1 +#define CONFIG_MSM_QDSP6_SSR 1 +#define CONFIG_MSM_QDSP6_PDR 1 #define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 #define CONFIG_MSM_ADSP_LOADER 1 #define CONFIG_REGMAP_SWR 1 From 8d02d74ac65209958f44e7c0d31602d3648e67c4 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Wed, 20 Dec 2017 19:47:32 -0800 Subject: [PATCH 139/276] asoc: codecs: Fix tavil i2s max rate and cleanup Change i2s max rate supported for tavil to 384000Hz. Cleanup usage of intf_type to optimize differentiation between slim and i2s. Change-Id: If6451dae7c61e8f61be3d5a13549f98b382e5054 Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd934x/wcd934x.c | 53 +++++++++++++++-------------------- 1 file changed, 23 insertions(+), 30 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 1be2f66ec7af..5e67cde28e1f 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -6682,19 +6682,19 @@ static const char *const slim_rx_mux_text[] = { }; static const char *const i2s_rx01_mux_text[] = { - "ZERO", "AIF1_PB" + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB" }; static const char *const i2s_rx23_mux_text[] = { - "ZERO", "AIF1_PB", "AIF2_PB" + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB" }; static const char *const i2s_rx45_mux_text[] = { - "ZERO", "AIF1_PB", "AIF3_PB" + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB" }; static const char *const i2s_rx67_mux_text[] = { - "ZERO", "AIF1_PB" + "ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB" }; static const char *const cdc_if_rx0_mux_text[] = { @@ -8600,7 +8600,6 @@ static int tavil_hw_params(struct snd_pcm_substream *substream, static int tavil_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec); u32 i2s_reg; switch (dai->id) { @@ -8624,13 +8623,11 @@ static int tavil_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: /* CPU is master */ - if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) - snd_soc_update_bits(dai->codec, i2s_reg, 0x2, 0x0); + snd_soc_update_bits(dai->codec, i2s_reg, 0x2, 0x0); break; case SND_SOC_DAIFMT_CBM_CFM: /* CPU is slave */ - if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) - snd_soc_update_bits(dai->codec, i2s_reg, 0x2, 0x2); + snd_soc_update_bits(dai->codec, i2s_reg, 0x2, 0x2); break; default: return -EINVAL; @@ -8799,7 +8796,7 @@ static struct snd_soc_dai_driver tavil_i2s_dai[] = { .rates = WCD934X_RATES_MASK, .formats = WCD934X_FORMATS_S16_S24_S32_LE, .rate_min = 8000, - .rate_max = 192000, + .rate_max = 384000, .channels_min = 1, .channels_max = 2, }, @@ -8813,7 +8810,7 @@ static struct snd_soc_dai_driver tavil_i2s_dai[] = { .rates = WCD934X_RATES_MASK, .formats = WCD934X_FORMATS_S16_S24_LE, .rate_min = 8000, - .rate_max = 192000, + .rate_max = 384000, .channels_min = 1, .channels_max = 2, }, @@ -8827,7 +8824,7 @@ static struct snd_soc_dai_driver tavil_i2s_dai[] = { .rates = WCD934X_RATES_MASK, .formats = WCD934X_FORMATS_S16_S24_S32_LE, .rate_min = 8000, - .rate_max = 192000, + .rate_max = 384000, .channels_min = 1, .channels_max = 2, }, @@ -8841,7 +8838,7 @@ static struct snd_soc_dai_driver tavil_i2s_dai[] = { .rates = WCD934X_RATES_MASK, .formats = WCD934X_FORMATS_S16_S24_LE, .rate_min = 8000, - .rate_max = 192000, + .rate_max = 384000, .channels_min = 1, .channels_max = 2, }, @@ -8855,7 +8852,7 @@ static struct snd_soc_dai_driver tavil_i2s_dai[] = { .rates = WCD934X_RATES_MASK, .formats = WCD934X_FORMATS_S16_S24_S32_LE, .rate_min = 8000, - .rate_max = 192000, + .rate_max = 384000, .channels_min = 1, .channels_max = 2, }, @@ -8869,7 +8866,7 @@ static struct snd_soc_dai_driver tavil_i2s_dai[] = { .rates = WCD934X_RATES_MASK, .formats = WCD934X_FORMATS_S16_S24_LE, .rate_min = 8000, - .rate_max = 192000, + .rate_max = 384000, .channels_min = 1, .channels_max = 2, }, @@ -10113,25 +10110,16 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) goto err_hwdep; } - if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) - snd_soc_dapm_new_controls(dapm, tavil_dapm_i2s_widgets, - ARRAY_SIZE(tavil_dapm_i2s_widgets)); - else - snd_soc_dapm_new_controls(dapm, tavil_dapm_slim_widgets, - ARRAY_SIZE(tavil_dapm_slim_widgets)); - - if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) - snd_soc_dapm_add_routes(dapm, tavil_slim_audio_map, - ARRAY_SIZE(tavil_slim_audio_map)); - else - snd_soc_dapm_add_routes(dapm, tavil_i2s_audio_map, - ARRAY_SIZE(tavil_i2s_audio_map)); - for (i = 0; i < NUM_CODEC_DAIS; i++) { INIT_LIST_HEAD(&tavil->dai[i].wcd9xxx_ch_list); init_waitqueue_head(&tavil->dai[i].dai_wait); } + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { + snd_soc_dapm_new_controls(dapm, tavil_dapm_slim_widgets, + ARRAY_SIZE(tavil_dapm_slim_widgets)); + snd_soc_dapm_add_routes(dapm, tavil_slim_audio_map, + ARRAY_SIZE(tavil_slim_audio_map)); tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la = control->slim_slave->laddr; tavil_slimbus_slave_port_cfg.slave_dev_pgd_la = @@ -10139,6 +10127,11 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) tavil_slimbus_slave_port_cfg.slave_port_mapping[0] = WCD934X_TX13; tavil_init_slim_slave_cfg(codec); + } else { + snd_soc_dapm_new_controls(dapm, tavil_dapm_i2s_widgets, + ARRAY_SIZE(tavil_dapm_i2s_widgets)); + snd_soc_dapm_add_routes(dapm, tavil_i2s_audio_map, + ARRAY_SIZE(tavil_i2s_audio_map)); } control->num_rx_port = WCD934X_RX_MAX; @@ -10856,7 +10849,7 @@ static int tavil_probe(struct platform_device *pdev) if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) { if (apr_get_subsys_state() == APR_SUBSYS_DOWN) { - dev_err(&pdev->dev, "%s: dsp down\n", __func__); + dev_dbg(&pdev->dev, "%s: dsp down\n", __func__); devm_kfree(&pdev->dev, tavil); return -EPROBE_DEFER; } From c3bbc0de90cb922bd4471e24f97c737739f9dea6 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Sun, 10 Dec 2017 23:50:37 -0800 Subject: [PATCH 140/276] dsp: afe: Add 9.6MHz clock in osr clk list Add 9.6MHz osr clk frequency to support tavil mclk request. Change-Id: I79da9cf9ddb8588ca550956d9c07f751618741ac Signed-off-by: Karthikeyan Mani --- dsp/q6afe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index bb11771ec296..42e0554befee 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -6214,6 +6214,7 @@ int q6afe_check_osr_clk_freq(u32 freq) switch (freq) { case Q6AFE_LPASS_OSR_CLK_12_P288_MHZ: + case Q6AFE_LPASS_OSR_CLK_9_P600_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: From dbacb16a1f20079db53e97978a94cf1ad2fdb999 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Mon, 11 Dec 2017 00:01:47 -0800 Subject: [PATCH 141/276] asoc: codecs: Add support for 9.6MHz lpass mclk Change lpass mclk structure to have updated clock id and frequency value to enable 9.6MHz mclk from lpass. Change-Id: I46aa726866fffb9008f0acef8759bd8e82b01287 Signed-off-by: Karthikeyan Mani --- asoc/codecs/audio-ext-clk-up.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/audio-ext-clk-up.c b/asoc/codecs/audio-ext-clk-up.c index 50a8ed6fc014..9caf13a59cfb 100644 --- a/asoc/codecs/audio-ext-clk-up.c +++ b/asoc/codecs/audio-ext-clk-up.c @@ -80,8 +80,8 @@ static struct afe_clk_set lpass_default = { static struct afe_clk_set lpass_mclk = { Q6AFE_LPASS_CLK_CONFIG_API_VERSION, - Q6AFE_LPASS_CLK_ID_MCLK_1, - Q6AFE_LPASS_OSR_CLK_11_P2896_MHZ, + Q6AFE_LPASS_CLK_ID_MCLK_3, + Q6AFE_LPASS_OSR_CLK_9_P600_MHZ, Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, Q6AFE_LPASS_CLK_ROOT_DEFAULT, 0, From 00f46fe0459e97b7f48f47e3428a8ba488fb39fd Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Wed, 13 Dec 2017 13:38:46 -0800 Subject: [PATCH 142/276] asoc: msm: Update routing and FE DAI drivers Update routing driver with port mixer commands between primary and secondary interfaces to enable AFE lopback between them. Update FE DAI driver to add hostless FEs for primary/secondary Auxpcm and secondary MI2S. Change-Id: I792e1cf4e0600d3b4726624e29e73e67828a3935 Signed-off-by: Viraja Kommaraju Signed-off-by: Karthikeyan Mani --- asoc/msm-dai-fe.c | 30 ++++++++++++++++++++++++++++++ asoc/msm-pcm-routing-v2.c | 22 ++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/asoc/msm-dai-fe.c b/asoc/msm-dai-fe.c index df24802c3404..172936a8eac6 100644 --- a/asoc/msm-dai-fe.c +++ b/asoc/msm-dai-fe.c @@ -696,6 +696,36 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .name = "AUXPCM_HOSTLESS", .probe = fe_dai_probe, }, + { + .playback = { + .stream_name = "SEC_AUXPCM_HOSTLESS Playback", + .aif_name = "SEC_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, + }, + .ops = &msm_fe_dai_ops, + .name = "SEC_AUXPCM_RX_HOSTLESS", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "SEC_AUXPCM_HOSTLESS Capture", + .aif_name = "SEC_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 = "SEC_AUXPCM_TX_HOSTLESS", + .probe = fe_dai_probe, + }, { .playback = { .stream_name = "VOICE_STUB Playback", diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 223674fff187..1f9a1da59a7d 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -8950,6 +8950,9 @@ static const struct snd_kcontrol_new aux_pcm_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_AUXPCM_RX, MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_AUXPCM_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_auxpcm_rx_port_mixer_controls[] = { @@ -8962,6 +8965,9 @@ static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = { 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), + SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_BACKEND_DAI_PRI_MI2S_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[] = { @@ -9152,6 +9158,9 @@ static const struct snd_kcontrol_new primary_mi2s_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_PRI_MI2S_RX, MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer, msm_routing_put_port_mixer), + SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_PRI_MI2S_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 usb_rx_port_mixer_controls[] = { @@ -10923,6 +10932,9 @@ static const struct snd_kcontrol_new sec_mi2s_rx_port_mixer_controls[] = { SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_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_SECONDARY_MI2S_RX, + MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer, + msm_routing_put_port_mixer), }; static const struct snd_kcontrol_new lsm1_mixer_controls[] = { @@ -12400,6 +12412,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("AUXPCM_UL_HL", "AUXPCM_HOSTLESS Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("SEC_AUXPCM_DL_HL", "SEC_AUXPCM_HOSTLESS Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("SEC_AUXPCM_UL_HL", "SEC_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("INT3_MI2S_UL_HL", @@ -15322,7 +15338,9 @@ static const struct snd_soc_dapm_route intercon[] = { {"HFP_SLIM7_UL_HL", "Switch", "SLIMBUS_7_TX"}, {"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"}, {"AUX_PCM_RX", NULL, "INTHFP_DL_HL"}, + {"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_DL_HL"}, {"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"}, + {"SEC_AUXPCM_UL_HL", NULL, "SEC_AUX_PCM_TX"}, {"MI2S_RX", NULL, "MI2S_DL_HL"}, {"MI2S_UL_HL", NULL, "MI2S_TX"}, {"PCM_RX_DL_HL", "Switch", "SLIM0_DL_HL"}, @@ -15893,11 +15911,13 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"}, + {"AUX_PCM_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_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_AUXPCM_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_RX Port Mixer"}, {"TERT_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, @@ -16038,6 +16058,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"PRI_MI2S_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"}, {"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Port Mixer"}, {"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, @@ -16048,6 +16069,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"}, + {"SEC_MI2S_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"}, {"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Port Mixer"}, {"TERT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, From 5e60ad343bb83c199c59bc62880914750cd01ef8 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Mon, 11 Dec 2017 00:06:49 -0800 Subject: [PATCH 143/276] asoc: sdxpoorwills: Fix mclk request failure Update low power audio interface offset address. Change-Id: Ie5420b01550c5506d3e8ac3473f6a5dc58cf0b48 Signed-off-by: Karthikeyan Mani --- asoc/sdxpoorwills.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/asoc/sdxpoorwills.c b/asoc/sdxpoorwills.c index b7b07d7d6624..811fec0cb10e 100644 --- a/asoc/sdxpoorwills.c +++ b/asoc/sdxpoorwills.c @@ -49,7 +49,7 @@ #define NUM_OF_BITS_PER_SAMPLE 16 #define DEV_NAME_STR_LEN 32 -#define LPAIF_OFFSET 0x07700000 +#define LPAIF_OFFSET 0x01a00000 #define LPAIF_PRI_MODE_MUXSEL (LPAIF_OFFSET + 0x2008) #define LPAIF_SEC_MODE_MUXSEL (LPAIF_OFFSET + 0x200c) #define LPASS_CSR_GP_IO_MUX_SPKR_CTL (LPAIF_OFFSET + 0x2004) @@ -1281,7 +1281,6 @@ static int sdx_mi2s_audrx_init(struct snd_soc_pcm_runtime *rtd) 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"); @@ -1296,37 +1295,26 @@ static int sdx_mi2s_audrx_init(struct snd_soc_pcm_runtime *rtd) 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, "HEADPHONE"); 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, "SPK_OUT"); - snd_soc_dapm_ignore_suspend(dapm, "ANC HEADPHONE"); 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, "AIF4 VI"); - snd_soc_dapm_ignore_suspend(dapm, "VIINPUT"); 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_sync(dapm); From eebaa0cf0d87405f7ba2b9c2c1d8c384b6406713 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Thu, 30 Nov 2017 17:55:05 +0800 Subject: [PATCH 144/276] ASOC: dsp: add spin_lock for q6asm_add_hdr During SSR, ac could get freed after unlock the session lock during the execution of q6asm_callback. If we try to get the ac->cmd_lock after ac freed, kernel panic happens. Remove mutex_lock in reset_event to avoid kernel panic. Add spin_lock_irqsave in q6asm_add_hdr and change spin_lock to spin_lock_irqsave in q6asm_callback to add synchronize between q6asm_add_hdr and q6asm_callback to avoid kernel panic. Change-Id: I72cf959fe6a764920a13d565c72243a80ac4f236 Signed-off-by: Meng Wang --- dsp/q6asm.c | 86 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index f44df16a7e15..752b261c75d8 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -563,11 +563,12 @@ static bool q6asm_is_valid_audio_client(struct audio_client *ac) static void q6asm_session_free(struct audio_client *ac) { int session_id; + unsigned long flags; pr_debug("%s: sessionid[%d]\n", __func__, ac->session); session_id = ac->session; rtac_remove_popp_from_adm_devices(ac->session); - spin_lock_bh(&(session[session_id].session_lock)); + spin_lock_irqsave(&(session[session_id].session_lock), flags); session[ac->session].ac = NULL; ac->session = 0; ac->perf_mode = LEGACY_PCM_MODE; @@ -576,7 +577,7 @@ static void q6asm_session_free(struct audio_client *ac) ac->priv = NULL; kfree(ac); ac = NULL; - spin_unlock_bh(&(session[session_id].session_lock)); + spin_unlock_irqrestore(&(session[session_id].session_lock), flags); } static uint32_t q6asm_get_next_buf(struct audio_client *ac, @@ -1520,6 +1521,7 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) uint32_t i = IN; uint32_t *payload; unsigned long dsp_flags; + unsigned long flags = 0; struct asm_buffer_node *buf_node = NULL; struct list_head *ptr, *next; union asm_token_struct asm_token; @@ -1573,7 +1575,7 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) session_id = asm_token._token.session_id; if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) - spin_lock(&(session[session_id].session_lock)); + spin_lock_irqsave(&(session[session_id].session_lock), flags); ac = q6asm_get_audio_client(session_id); dir = q6asm_get_flag_from_token(&asm_token, ASM_DIRECTION_OFFSET); @@ -1583,7 +1585,8 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) __func__, session_id); if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1636,7 +1639,8 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) } if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1672,7 +1676,8 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) ac->cb(data->opcode, data->token, data->payload, ac->priv); if ((session_id > 0 && session_id <= ASM_ACTIVE_STREAMS_ALLOWED)) - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1740,7 +1745,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) uint8_t buf_index; struct msm_adsp_event_data *pp_event_package = NULL; uint32_t payload_size = 0; - + unsigned long flags; int session_id; if (ac == NULL) { @@ -1758,12 +1763,13 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) session_id); return -EINVAL; } - spin_lock(&(session[session_id].session_lock)); + spin_lock_irqsave(&(session[session_id].session_lock), flags); if (!q6asm_is_valid_audio_client(ac)) { pr_err("%s: audio client pointer is invalid, ac = %pK\n", __func__, ac); - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return -EINVAL; } @@ -1776,9 +1782,6 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) } if (data->opcode == RESET_EVENTS) { - spin_unlock(&(session[session_id].session_lock)); - mutex_lock(&ac->cmd_lock); - spin_lock(&(session[session_id].session_lock)); atomic_set(&ac->reset, 1); if (ac->apr == NULL) { ac->apr = ac->apr2; @@ -1799,8 +1802,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) wake_up(&ac->time_wait); wake_up(&ac->cmd_wait); wake_up(&ac->mem_wait); - spin_unlock(&(session[session_id].session_lock)); - mutex_unlock(&ac->cmd_lock); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1814,7 +1817,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) { if (payload == NULL) { pr_err("%s: payload is null\n", __func__); - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return -EINVAL; } dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n", @@ -1840,7 +1844,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) ret = q6asm_is_valid_session(data, priv); if (ret != 0) { pr_err("%s: session invalid %d\n", __func__, ret); - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return ret; } case ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2: @@ -1880,8 +1885,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) payload[1]); wake_up(&ac->cmd_wait); } - spin_unlock( - &(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return 0; } if ((is_adsp_reg_event(payload[0]) >= 0) || @@ -1912,8 +1918,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) atomic_set(&ac->mem_state, payload[1]); wake_up(&ac->mem_wait); } - spin_unlock( - &(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return 0; } if (atomic_read(&ac->mem_state) && wakeup_flag) { @@ -1962,7 +1969,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) break; } - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -1977,8 +1985,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (port->buf == NULL) { pr_err("%s: Unexpected Write Done\n", __func__); - spin_unlock( - &(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return -EINVAL; } spin_lock_irqsave(&port->dsp_lock, dsp_flags); @@ -1993,8 +2002,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) __func__, payload[0], payload[1]); spin_unlock_irqrestore(&port->dsp_lock, dsp_flags); - spin_unlock( - &(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return -EINVAL; } port->buf[buf_index].used = 1; @@ -2065,8 +2075,9 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (ac->io_mode & SYNC_IO_MODE) { if (port->buf == NULL) { pr_err("%s: Unexpected Write Done\n", __func__); - spin_unlock( - &(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); return -EINVAL; } spin_lock_irqsave(&port->dsp_lock, dsp_flags); @@ -2142,7 +2153,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) __func__, payload[0], payload[1]); i = is_adsp_raise_event(data->opcode); if (i < 0) { - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -2154,7 +2166,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) + sizeof(struct msm_adsp_event_data), GFP_ATOMIC); if (!pp_event_package) { - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return -ENOMEM; } @@ -2165,7 +2178,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) ac->cb(data->opcode, data->token, (void *)pp_event_package, ac->priv); kfree(pp_event_package); - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; case ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2: pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n", @@ -2191,7 +2205,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) if (ac->cb) ac->cb(data->opcode, data->token, data->payload, ac->priv); - spin_unlock(&(session[session_id].session_lock)); + spin_unlock_irqrestore( + &(session[session_id].session_lock), flags); return 0; } @@ -2371,11 +2386,16 @@ int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac) 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) { + unsigned long flags; + 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); + spin_lock_irqsave(&(session[ac->session].session_lock), flags); if (ac->apr == NULL) { pr_err("%s: AC APR handle NULL", __func__); + spin_unlock_irqrestore( + &(session[ac->session].session_lock), flags); mutex_unlock(&ac->cmd_lock); return; } @@ -2398,6 +2418,8 @@ static void __q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr, WAIT_CMD); hdr->pkt_size = pkt_size; + spin_unlock_irqrestore( + &(session[ac->session].session_lock), flags); mutex_unlock(&ac->cmd_lock); } From 16b9713d6611e3d2bcf3ba04a37c7d0ff5d86fc7 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Mon, 8 Jan 2018 13:16:32 +0530 Subject: [PATCH 145/276] dsp: fix low volume in audio recording ADM calibration is not sent in audio recording use case which leads to low volume issue. Since the LSM port ID is same as audio recording port ID in case of internal codec, driver tries to send LSM calibration for non LSM use case in which case no calibration will be sent. Instead of using port ID, use the pass_thr mode to differentiate between LSM and non LSM use cases. CRs-Fixed: 2167317 Change-Id: I1b6ea51d83330f6439791cf1bb6170306f6895b0 Signed-off-by: Aditya Bavanari --- asoc/msm-pcm-routing-v2.c | 3 +-- dsp/q6adm.c | 16 ++++++---------- include/dsp/q6adm-v2.h | 3 +-- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 223674fff187..b3fe228cd8f6 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -2572,7 +2572,6 @@ static int msm_routing_lsm_func_put(struct snd_kcontrol *kcontrol, pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id, mad_type); - adm_set_lsm_port_id(port_id); return afe_port_set_mad_type(port_id, mad_type); } diff --git a/dsp/q6adm.c b/dsp/q6adm.c index fd8b89a4ec46..1144dbcd35a3 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -104,7 +104,6 @@ struct adm_ctl { int num_ec_ref_rx_chans; int ec_ref_rx_bit_width; int ec_ref_rx_sampling_rate; - int lsm_port_id; }; static struct adm_ctl this_adm; @@ -2163,17 +2162,13 @@ static int get_cal_path(int path) return TX_DEVICE; } -void adm_set_lsm_port_id(int port_id) -{ - this_adm.lsm_port_id = port_id; -} - 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) + int app_type, int acdb_id, int sample_rate, + int passthr_mode) { pr_debug("%s: port id 0x%x copp_idx %d\n", __func__, port_id, copp_idx); - if (port_id != this_adm.lsm_port_id) + if (passthr_mode != LISTEN) send_adm_cal_type(ADM_AUDPROC_CAL, path, port_id, copp_idx, perf_mode, app_type, acdb_id, sample_rate); else @@ -2890,7 +2885,8 @@ int adm_matrix_map(int path, struct route_payload payload_map, int perf_mode, get_cal_path(path), perf_mode, payload_map.app_type[i], payload_map.acdb_dev_id[i], - payload_map.sample_rate[i]); + payload_map.sample_rate[i], + passthr_mode); /* ADM COPP calibration is already sent */ clear_bit(ADM_STATUS_CALIBRATION_REQUIRED, (void *)&this_adm.copp. diff --git a/include/dsp/q6adm-v2.h b/include/dsp/q6adm-v2.h index 6d1f4f69c5a4..69de7c902511 100644 --- a/include/dsp/q6adm-v2.h +++ b/include/dsp/q6adm-v2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -185,5 +185,4 @@ int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id, int session_type, struct msm_pcm_channel_mixer *ch_mixer, int channel_index); -void adm_set_lsm_port_id(int port_id); #endif /* __Q6_ADM_V2_H__ */ From 4396a0bb904aa4d20414c37261637033e0a8e700 Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Thu, 4 Jan 2018 13:49:12 -0800 Subject: [PATCH 146/276] dsp: Avoid excessive list iterations Exit list iteration after the required conditions for the loop has been satisfied to avoid additional list iteration. Change-Id: I96872d3be469420e613ec0244588a0a8ecb50e58 Signed-off-by: Vignesh Kulothungan --- dsp/q6asm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index f44df16a7e15..8400d8295db7 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -7608,8 +7608,10 @@ static int __q6asm_read(struct audio_client *ac, bool is_custom_len_reqd, 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) + if (buf_node->buf_phys_addr == ab->phys) { read.mem_map_handle = buf_node->mmap_hdl; + break; + } } dev_vdbg(ac->dev, "memory_map handle in q6asm_read: [%0x]:", read.mem_map_handle); From e5a6e77cf327bc081597fd8b293bac71b51acefd Mon Sep 17 00:00:00 2001 From: Satya Krishna Pindiproli Date: Wed, 10 Jan 2018 16:21:40 +0530 Subject: [PATCH 147/276] dsp: codecs: use native API to configure PCM output block in multiaac Usage of q6asm_enc_cfg_blk_pcm() does not trigger PortSettingsChanged event which is required for multi aac component. Configure the PCM output block using q6asm_enc_cfg_blk_pcm_native() which ensures that the event gets triggered. CRs-Fixed: 2169255 Change-Id: I168aa0156c9233d81e99ca2aa6142f33ef10805c Signed-off-by: Satya Krishna Pindiproli --- dsp/codecs/audio_multi_aac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dsp/codecs/audio_multi_aac.c b/dsp/codecs/audio_multi_aac.c index a1e50bc1f14b..2a54cc5da7f9 100644 --- a/dsp/codecs/audio_multi_aac.c +++ b/dsp/codecs/audio_multi_aac.c @@ -2,7 +2,7 @@ * * Copyright (C) 2008 Google, Inc. * Copyright (C) 2008 HTC Corporation - * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2011-2018, 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 @@ -59,7 +59,7 @@ static long audio_ioctl_shared(struct file *file, unsigned int cmd, audio->ac->session); if (audio->feedback == NON_TUNNEL_MODE) { /* Configure PCM output block */ - rc = q6asm_enc_cfg_blk_pcm(audio->ac, + rc = q6asm_enc_cfg_blk_pcm_native(audio->ac, audio->pcm_cfg.sample_rate, audio->pcm_cfg.channel_count); if (rc < 0) { From 0e01dcccff8e0c9bded001c7d963ea382c73c5e7 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Tue, 9 Jan 2018 12:27:29 -0800 Subject: [PATCH 148/276] asoc: wcd-dsp-utils: add size check for WDSP ELF files Add size check to make sure the data sizes from WDSP ELF metadata and the split firmware ELF are the same. Change-Id: I913087bc352850ceac2fcc07067ea8f480563ae2 Signed-off-by: Xiaoyu Ye --- asoc/codecs/wcd-dsp-utils.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wcd-dsp-utils.c b/asoc/codecs/wcd-dsp-utils.c index 4eafd55894c5..1c95d48a1a81 100644 --- a/asoc/codecs/wcd-dsp-utils.c +++ b/asoc/codecs/wcd-dsp-utils.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, 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 @@ -82,6 +82,15 @@ static int wdsp_add_segment_to_list(struct device *dev, goto bad_seg; } + if (phdr->p_filesz != seg->split_fw->size) { + dev_err(dev, + "%s: %s size mismatch, phdr_size: 0x%x fw_size: 0x%zx", + __func__, seg->split_fname, phdr->p_filesz, + seg->split_fw->size); + ret = -EINVAL; + goto bad_elf; + } + seg->load_addr = phdr->p_paddr; seg->size = phdr->p_filesz; seg->data = (u8 *) seg->split_fw->data; @@ -89,6 +98,8 @@ static int wdsp_add_segment_to_list(struct device *dev, list_add_tail(&seg->list, seg_list); done: return ret; +bad_elf: + release_firmware(seg->split_fw); bad_seg: kfree(seg); return ret; From 190cc811466eceda30b4c173005ae2180f221756 Mon Sep 17 00:00:00 2001 From: Vidyakumar Athota Date: Wed, 20 Dec 2017 14:20:40 -0800 Subject: [PATCH 149/276] asoc: codecs: update IIR cofficient values before IIR band enable Currently IIR coefficient values are written to HW everytime when digital core comes out of power collapse. This increases playback cold start latency. So update IIR coefficient values before IIR band enable. Change-Id: I57ad54b4674ed0a49fdfc55d77c519568f5a7893 Signed-off-by: Vidyakumar Athota --- asoc/codecs/wcd934x/wcd934x.c | 87 ++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 37 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 8e3981c9fdf5..ffc970e2658b 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -127,6 +127,7 @@ static const struct snd_kcontrol_new name##_mux = \ #define WCD934X_STRING_LEN 100 #define WCD934X_CDC_SIDETONE_IIR_COEFF_MAX 5 +#define WCD934X_CDC_REPEAT_WRITES_MAX 16 #define WCD934X_DIG_CORE_REG_MIN WCD934X_CDC_ANC0_CLK_RESET_CTL #define WCD934X_DIG_CORE_REG_MAX 0xFFF @@ -628,8 +629,8 @@ struct tavil_priv { struct tavil_idle_detect_config idle_det_cfg; int power_active_ref; - int sidetone_coeff_array[IIR_MAX][BAND_MAX] - [WCD934X_CDC_SIDETONE_IIR_COEFF_MAX]; + u8 sidetone_coeff_array[IIR_MAX][BAND_MAX] + [WCD934X_CDC_SIDETONE_IIR_COEFF_MAX * 4]; struct spi_device *spi; struct platform_device *pdev_child_devices @@ -4927,6 +4928,36 @@ static int tavil_codec_reset_hph_registers(struct snd_soc_dapm_widget *w, return 0; } +static void tavil_restore_iir_coeff(struct tavil_priv *tavil, int iir_idx, + int band_idx) +{ + u16 reg_add; + int no_of_reg = 0; + + regmap_write(tavil->wcd9xxx->regmap, + (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), + (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); + + reg_add = WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx; + + if (tavil->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) + return; + /* + * Since wcd9xxx_slim_write_repeat() supports only maximum of 16 + * registers at a time, split total 20 writes(5 coefficients per + * band and 4 writes per coefficient) into 16 and 4. + */ + no_of_reg = WCD934X_CDC_REPEAT_WRITES_MAX; + wcd9xxx_slim_write_repeat(tavil->wcd9xxx, reg_add, no_of_reg, + &tavil->sidetone_coeff_array[iir_idx][band_idx][0]); + + no_of_reg = (WCD934X_CDC_SIDETONE_IIR_COEFF_MAX * 4) - + WCD934X_CDC_REPEAT_WRITES_MAX; + wcd9xxx_slim_write_repeat(tavil->wcd9xxx, reg_add, no_of_reg, + &tavil->sidetone_coeff_array[iir_idx][band_idx] + [WCD934X_CDC_REPEAT_WRITES_MAX]); +} + static int tavil_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -4951,6 +4982,7 @@ static int tavil_iir_enable_audio_mixer_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec); int iir_idx = ((struct soc_multi_mixer_control *) kcontrol->private_value)->reg; int band_idx = ((struct soc_multi_mixer_control *) @@ -4959,6 +4991,8 @@ static int tavil_iir_enable_audio_mixer_put(struct snd_kcontrol *kcontrol, int value = ucontrol->value.integer.value[0]; u16 iir_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx; + tavil_restore_iir_coeff(tavil, iir_idx, band_idx); + /* Mask first 5 bits, 6-8 are reserved */ snd_soc_update_bits(codec, iir_reg, (1 << band_idx), (value << band_idx)); @@ -5085,7 +5119,7 @@ static int tavil_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol, kcontrol->private_value)->reg; int band_idx = ((struct soc_multi_mixer_control *) kcontrol->private_value)->shift; - int coeff_idx; + int coeff_idx, idx = 0; /* * Mask top bit it is reserved @@ -5098,11 +5132,19 @@ static int tavil_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol, /* Store the coefficients in sidetone coeff array */ for (coeff_idx = 0; coeff_idx < WCD934X_CDC_SIDETONE_IIR_COEFF_MAX; coeff_idx++) { - tavil->sidetone_coeff_array[iir_idx][band_idx][coeff_idx] = - ucontrol->value.integer.value[coeff_idx]; - set_iir_band_coeff(codec, iir_idx, band_idx, - tavil->sidetone_coeff_array[iir_idx][band_idx] - [coeff_idx]); + uint32_t value = ucontrol->value.integer.value[coeff_idx]; + + set_iir_band_coeff(codec, iir_idx, band_idx, value); + + /* Four 8 bit values(one 32 bit) per coefficient */ + tavil->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value & 0xFF); + tavil->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 8) & 0xFF; + tavil->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 16) & 0xFF; + tavil->sidetone_coeff_array[iir_idx][band_idx][idx++] = + (value >> 24) & 0xFF; } pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n" @@ -5123,33 +5165,6 @@ static int tavil_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol, return 0; } -static void tavil_restore_iir_coeff(struct tavil_priv *tavil, int iir_idx) -{ - int band_idx = 0, coeff_idx = 0; - struct snd_soc_codec *codec = tavil->codec; - - /* - * snd_soc_write call crashes at rmmod if there is no machine - * driver and hence no codec pointer available - */ - if (!codec) - return; - - for (band_idx = 0; band_idx < BAND_MAX; band_idx++) { - snd_soc_write(codec, - (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx), - (band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F); - - for (coeff_idx = 0; - coeff_idx < WCD934X_CDC_SIDETONE_IIR_COEFF_MAX; - coeff_idx++) { - set_iir_band_coeff(codec, iir_idx, band_idx, - tavil->sidetone_coeff_array[iir_idx][band_idx] - [coeff_idx]); - } - } -} - static int tavil_compander_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -8250,8 +8265,6 @@ static int tavil_dig_core_remove_power_collapse(struct tavil_priv *tavil) WCD934X_DIG_CORE_REG_MIN, WCD934X_DIG_CORE_REG_MAX); - tavil_restore_iir_coeff(tavil, IIR0); - tavil_restore_iir_coeff(tavil, IIR1); return 0; } From 068c73d70ba4e2e9d6b4688360681cbdfa787545 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Wed, 20 Dec 2017 17:55:12 -0800 Subject: [PATCH 150/276] asoc: sdxpoorwills: fix usage of ignore_pmdown_time Set ignore_pmdown_time for dai links that involve playback path and remove for the ones that involve only capture path. Change-Id: Ida3761757a78577be39bff773bd05a7f0c2e37fe Signed-off-by: Karthikeyan Mani --- asoc/sdxpoorwills.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/asoc/sdxpoorwills.c b/asoc/sdxpoorwills.c index 811fec0cb10e..2d1a0db2d611 100644 --- a/asoc/sdxpoorwills.c +++ b/asoc/sdxpoorwills.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, 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 @@ -1516,7 +1516,6 @@ static struct snd_soc_dai_link sdx_common_dai_links[] = { 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", }, @@ -1684,7 +1683,6 @@ static struct snd_soc_dai_link sdx_common_dai_links[] = { 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", }, @@ -1715,7 +1713,6 @@ static struct snd_soc_dai_link sdx_common_dai_links[] = { 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", }, @@ -1746,7 +1743,6 @@ static struct snd_soc_dai_link sdx_common_dai_links[] = { 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", }, @@ -1786,6 +1782,7 @@ static struct snd_soc_dai_link sdx_common_be_dai_links[] = { .dpcm_playback = 1, .id = MSM_BACKEND_DAI_AFE_PCM_RX, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, { .name = LPASS_BE_AFE_PCM_TX, @@ -1840,6 +1837,7 @@ static struct snd_soc_dai_link sdx_common_be_dai_links[] = { .id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX, .be_hw_params_fixup = sdx_be_hw_params_fixup, .ignore_suspend = 1, + .ignore_pmdown_time = 1, }, }; @@ -1872,7 +1870,6 @@ static struct snd_soc_dai_link sdx_mi2s_be_dai_links[] = { .id = MSM_BACKEND_DAI_PRI_MI2S_TX, .be_hw_params_fixup = &sdx_mi2s_tx_be_hw_params_fixup, .ops = &sdx_mi2s_be_ops, - .ignore_pmdown_time = 1, .ignore_suspend = 1, }, { @@ -1902,7 +1899,6 @@ static struct snd_soc_dai_link sdx_mi2s_be_dai_links[] = { .id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX, .be_hw_params_fixup = &sdx_sec_mi2s_tx_be_hw_params_fixup, .ops = &sdx_sec_mi2s_be_ops, - .ignore_pmdown_time = 1, .ignore_suspend = 1, }, }; From 79298baced76e489b7d997fbd90d8ae6ee731563 Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Mon, 8 Jan 2018 18:41:02 -0800 Subject: [PATCH 151/276] ASoC: wcd-spi: ignore failure from clearing the CLK_REQUEST It is possible that the spi transfer that clears the clock request on the slave may fail. In such case, it is not safe to assume that the clock request is still enabled as it can cause failures for further spi transfers if the clock request is cleared. Fix such issues by resetting the clock state in the driver even the clock request clearing fails. CRs-Fixed: 2169087 Change-Id: I699e72b59b4cb049dfacaa190823796a545f2dbb Signed-off-by: Bhalchandra Gajare --- asoc/codecs/wcd-spi.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/asoc/codecs/wcd-spi.c b/asoc/codecs/wcd-spi.c index 072a7538ef1d..a25c9a6141ab 100644 --- a/asoc/codecs/wcd-spi.c +++ b/asoc/codecs/wcd-spi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, 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 @@ -603,8 +603,11 @@ static int wcd_spi_clk_disable(struct spi_device *spi) if (ret < 0) dev_err(&spi->dev, "%s: Failed, err = %d\n", __func__, ret); - else - clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask); + /* + * clear this bit even if clock disable failed + * as the source clocks might get turned off. + */ + clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask); return ret; } From bd9254daf2557d759e8d9d7a1ba975f006389060 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Tue, 23 Jan 2018 12:42:52 +0800 Subject: [PATCH 152/276] ASoC: dsp: correct return value check When the return value of adm_populate_channel_weight is 0, it should keep running, not return error. Change-Id: I447b81d6edfc89db6cb3742c1719e745c6071c12 Signed-off-by: Meng Wang --- dsp/q6adm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsp/q6adm.c b/dsp/q6adm.c index 1144dbcd35a3..dc443c6045dd 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -737,7 +737,7 @@ int adm_programable_channel_mixer(int port_id, int copp_idx, int session_id, index = index + ch_mixer->input_channels[channel_index]; ret = adm_populate_channel_weight(&adm_pspd_params[index], ch_mixer, channel_index); - if (!ret) { + if (ret) { pr_err("%s: fail to get channel weight with error %d\n", __func__, ret); goto fail_cmd; From 129367694a9d8ca04a3bd25c1e3bb136ba9ff4a2 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Wed, 27 Dec 2017 15:34:52 -0800 Subject: [PATCH 153/276] autoconf: sdxpoorwills: remove unused configs Remove duplicates and unused configs from the config header files. Change-Id: I12e59b3259a921fcd6366d603b4abfe004061478 Signed-off-by: Xiaoyu Ye Signed-off-by: Karthikeyan Mani --- config/sdxpoorwillsauto.conf | 5 ----- config/sdxpoorwillsautoconf.h | 5 ----- 2 files changed, 10 deletions(-) diff --git a/config/sdxpoorwillsauto.conf b/config/sdxpoorwillsauto.conf index 1d0c19d0e627..4a9b1cfb76a2 100644 --- a/config/sdxpoorwillsauto.conf +++ b/config/sdxpoorwillsauto.conf @@ -1,11 +1,8 @@ CONFIG_PINCTRL_WCD=y CONFIG_SND_SOC_WCD934X=y -CONFIG_AUDIO_EXT_CLK=y CONFIG_SND_SOC_WCD9XXX_V2=y CONFIG_SND_SOC_WCD_MBHC=y CONFIG_SND_SOC_WSA881X=y -CONFIG_SND_SOC_WCD_DSP_MGR=y -CONFIG_SND_SOC_WCD934X=y CONFIG_SND_SOC_WCD934X_MBHC=y CONFIG_SND_SOC_WCD934X_DSD=y CONFIG_MSM_QDSP6V2_CODECS=y @@ -17,12 +14,10 @@ CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y CONFIG_MSM_ADSP_LOADER=y CONFIG_REGMAP_SWR=y CONFIG_SND_SOC_MSM_HOSTLESS_PCM=y -CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y CONFIG_SND_SOC_POORWILLS=y CONFIG_SOUNDWIRE=y CONFIG_SOUNDWIRE_WCD_CTRL=y CONFIG_SND_SOC_QDSP6V2=y -CONFIG_SND_SOC_MSM_QDSP6V2_INTF=y CONFIG_MSM_CDC_PINCTRL=y CONFIG_WCD9XXX_CODEC_CORE=y CONFIG_SND_SOC_WCD_MBHC_ADC=y diff --git a/config/sdxpoorwillsautoconf.h b/config/sdxpoorwillsautoconf.h index 2bce8d48b61c..f6eca51246b6 100644 --- a/config/sdxpoorwillsautoconf.h +++ b/config/sdxpoorwillsautoconf.h @@ -13,12 +13,9 @@ #define CONFIG_PINCTRL_WCD 1 #define CONFIG_SND_SOC_WCD934X 1 -#define CONFIG_AUDIO_EXT_CLK 1 #define CONFIG_SND_SOC_WCD9XXX_V2 1 #define CONFIG_SND_SOC_WCD_MBHC 1 #define CONFIG_SND_SOC_WSA881X 1 -#define CONFIG_SND_SOC_WCD_DSP_MGR 1 -#define CONFIG_SND_SOC_WCD934X 1 #define CONFIG_SND_SOC_WCD934X_MBHC 1 #define CONFIG_SND_SOC_WCD934X_DSD 1 #define CONFIG_MSM_QDSP6V2_CODECS 1 @@ -30,12 +27,10 @@ #define CONFIG_MSM_ADSP_LOADER 1 #define CONFIG_REGMAP_SWR 1 #define CONFIG_SND_SOC_MSM_HOSTLESS_PCM 1 -#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 #define CONFIG_SND_SOC_POORWILLS 1 #define CONFIG_SOUNDWIRE 1 #define CONFIG_SOUNDWIRE_WCD_CTRL 1 #define CONFIG_SND_SOC_QDSP6V2 1 -#define CONFIG_SND_SOC_MSM_QDSP6V2_INTF 1 #define CONFIG_MSM_CDC_PINCTRL 1 #define CONFIG_WCD9XXX_CODEC_CORE 1 #define CONFIG_SND_SOC_WCD_MBHC_ADC 1 From 68b2409fe94fbc66c91f60ce300a77fde71160f4 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Wed, 17 Jan 2018 11:30:35 -0800 Subject: [PATCH 154/276] asoc: wcd_cpe_core: add size check for WDSP ELF files Add size check to make sure the data sizes from WDSP ELF metadata and the split firmware ELF are the same. Change-Id: I7a1404cd15601d8d503e3355e6dc70e2949df72d Signed-off-by: Xiaoyu Ye --- asoc/codecs/wcd_cpe_core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wcd_cpe_core.c b/asoc/codecs/wcd_cpe_core.c index a7fcce316e6d..2ce5652422d2 100644 --- a/asoc/codecs/wcd_cpe_core.c +++ b/asoc/codecs/wcd_cpe_core.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, 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 @@ -329,6 +329,14 @@ static int wcd_cpe_load_each_segment(struct wcd_cpe_core *core, goto done; } + if (phdr->p_filesz != split_fw->size) { + dev_err(core->dev, + "%s: %s size mismatch, phdr_size: 0x%x fw_size: 0x%zx", + __func__, split_fname, phdr->p_filesz, split_fw->size); + ret = -EINVAL; + goto done; + } + segment->cpe_addr = phdr->p_paddr; segment->size = phdr->p_filesz; segment->data = (u8 *) split_fw->data; From 6ea259b345ae48244a29ee8b5722dd727eb1bff0 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 8 Jan 2018 18:09:29 -0800 Subject: [PATCH 155/276] asoc: avoid excessive logging Excessive logging by pr_err() could cause audio thread to stuck in the printing session which results PD down notification timeout. This eventually can cause ADSP PDR failure. Fix this issue by using pr_err_ratelimited and dev_err_ratelimited to limit the number of error messages during the ADSP PDR. Change-Id: I68b617fef53ebd03ba79fd919bffd8c35e6eb048 Signed-off-by: Xiaoyu Ye --- asoc/msm-compress-q6-v2.c | 6 ++---- dsp/q6asm.c | 9 +++++---- ipc/apr.c | 4 ++-- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 775663926916..b436cda23c81 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -2658,12 +2658,10 @@ static int msm_compr_pointer(struct snd_compr_stream *cstream, 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; + return rc; } } } else { diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 8400d8295db7..c874de5c4050 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -8072,15 +8072,16 @@ int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp) 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; + dev_err_ratelimited(ac->dev, "%s: Get Session Time failed %d\n", + __func__, rc); + return rc; } + 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__); + __func__); goto fail_cmd; } diff --git a/ipc/apr.c b/ipc/apr.c index 02fddef4ad1d..c7eab98fb69b 100644 --- a/ipc/apr.c +++ b/ipc/apr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2014, 2016-2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2014, 2016-2018 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 @@ -334,7 +334,7 @@ int apr_send_pkt(void *handle, uint32_t *buf) return -EINVAL; } if (svc->need_reset) { - pr_err("apr: send_pkt service need reset\n"); + pr_err_ratelimited("apr: send_pkt service need reset\n"); return -ENETRESET; } From 586420991a15dd50a0746126ce8e73150c2dcb29 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Wed, 24 Jan 2018 14:50:17 -0800 Subject: [PATCH 156/276] asoc: wcd934x: avoid RT throttling Warning message could cause RT throttling due to long processing time when it gets printed multiple times. Replace the warning message in function tavil_slimbus_irq with dev_err to avoid RT throttling. Change-Id: I7dc4d37c4543f13d5df512cf1bbeef60c9c2cc88 Signed-off-by: Xiaoyu Ye --- asoc/codecs/wcd934x/wcd934x.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index d428c5aa6a4b..0bc9c796666f 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -9616,9 +9616,10 @@ static irqreturn_t tavil_slimbus_irq(int irq, void *data) */ } } - WARN(!cleared, - "Couldn't find slimbus %s port %d for closing\n", - (tx ? "TX" : "RX"), port_id); + if (!cleared) + dev_err(tavil->dev, + "%s: Couldn't find slimbus %s port %d for closing\n", + __func__, (tx ? "TX" : "RX"), port_id); } wcd9xxx_interface_reg_write(tavil->wcd9xxx, WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 + From b845cd542c943448e4a7834c14bd7b7571f08939 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Mon, 22 Jan 2018 15:21:17 +0800 Subject: [PATCH 157/276] ASoC: wcd934x: WDSP debug enhancement Add interface to trigger WDSP dumps for debug use. Important register dump and WDSP ramdump are both collected when DEBUG_DUMP string is sent from userspace via WDSP misc node or when WDSP boot timeout happens. CRs-Fixed: 2117755 Change-Id: I8b91a8939201a54512a5e3557ce771b4ff2ff075 Signed-off-by: Xiaojun Sang --- asoc/codecs/wcd-dsp-mgr.c | 44 +++++++++- asoc/codecs/wcd934x/wcd934x-dsp-cntl.c | 111 ++++++++++++++++++++++++- asoc/codecs/wcd934x/wcd934x-dsp-cntl.h | 6 +- 3 files changed, 156 insertions(+), 5 deletions(-) diff --git a/asoc/codecs/wcd-dsp-mgr.c b/asoc/codecs/wcd-dsp-mgr.c index 6cc9f8c79d21..ce9a6972451c 100644 --- a/asoc/codecs/wcd-dsp-mgr.c +++ b/asoc/codecs/wcd-dsp-mgr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, 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 @@ -838,6 +838,45 @@ static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg, return 0; } +#ifdef CONFIG_DEBUG_FS +static int wdsp_debug_dump_handler(struct wdsp_mgr_priv *wdsp, void *arg) +{ + struct wdsp_err_signal_arg *err_data; + int ret = 0; + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + /* If there is no SSR, set the SSR type to collect ramdumps */ + if (wdsp->ssr_type == WDSP_SSR_TYPE_NO_SSR) { + wdsp->ssr_type = WDSP_SSR_TYPE_WDSP_DOWN; + } else { + WDSP_DBG(wdsp, "SSR handling is running, skip debug ramdump"); + ret = 0; + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + goto done; + } + if (arg) { + err_data = (struct wdsp_err_signal_arg *) arg; + memcpy(&wdsp->dump_data.err_data, err_data, + sizeof(*err_data)); + } else { + WDSP_DBG(wdsp, "Invalid input, arg is NULL"); + ret = -EINVAL; + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + goto done; + } + wdsp_collect_ramdumps(wdsp); + wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); +done: + return ret; +} +#else +static int wdsp_debug_dump_handler(struct wdsp_mgr_priv *wdsp, void *arg) +{ + return 0; +} +#endif + static int wdsp_signal_handler(struct device *wdsp_dev, enum wdsp_signal signal, void *arg) { @@ -866,6 +905,9 @@ static int wdsp_signal_handler(struct device *wdsp_dev, case WDSP_CDC_UP_SIGNAL: ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_UP); break; + case WDSP_DEBUG_DUMP: + ret = wdsp_debug_dump_handler(wdsp, arg); + break; default: ret = -EINVAL; break; diff --git a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c index 031940ecbaf9..85b38654a51c 100644 --- a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, 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 @@ -31,6 +31,7 @@ #define WCD_PROCFS_ENTRY_MAX_LEN 16 #define WCD_934X_RAMDUMP_START_ADDR 0x20100000 #define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128) +#define WCD_MISCDEV_CMD_MAX_LEN 11 #define WCD_CNTL_MUTEX_LOCK(codec, lock) \ { \ @@ -76,6 +77,95 @@ static u8 mem_enable_values[] = { 0xE0, 0xC0, 0x80, 0x00, }; +#ifdef CONFIG_DEBUG_FS +#define WCD_CNTL_SET_ERR_IRQ_FLAG(cntl)\ + atomic_cmpxchg(&cntl->err_irq_flag, 0, 1) +#define WCD_CNTL_CLR_ERR_IRQ_FLAG(cntl)\ + atomic_set(&cntl->err_irq_flag, 0) + +static u16 wdsp_reg_for_debug_dump[] = { + WCD934X_CPE_SS_CPE_CTL, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0, + WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1, + WCD934X_CPE_SS_PWR_CPEFLL_CTL, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4, + WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5, + WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN, + WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, + WCD934X_CPE_SS_MAD_CTL, + WCD934X_CPE_SS_CPAR_CTL, + WCD934X_CPE_SS_WDOG_CFG, + WCD934X_CPE_SS_STATUS, + WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, + WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, + WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A, + WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B, + WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A, + WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B, + WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A, + WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B, +}; + +static void wcd_cntl_collect_debug_dumps(struct wcd_dsp_cntl *cntl) +{ + struct snd_soc_codec *codec = cntl->codec; + struct wdsp_err_signal_arg arg; + int i; + u8 val; + + /* If WDSP SSR happens, skip collecting debug dumps */ + if (WCD_CNTL_SET_ERR_IRQ_FLAG(cntl) != 0) + return; + + /* Mask all error interrupts */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, + 0xFF); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, + 0xFF); + + /* Collect important WDSP registers dump for debug use */ + pr_err("%s: Dump the WDSP registers for debug use\n", __func__); + for (i = 0; i < sizeof(wdsp_reg_for_debug_dump)/sizeof(u16); i++) { + val = snd_soc_read(codec, wdsp_reg_for_debug_dump[i]); + pr_err("%s: reg = 0x%x, val = 0x%x\n", __func__, + wdsp_reg_for_debug_dump[i], val); + } + + /* Trigger NMI in WDSP to sync and update the memory */ + snd_soc_write(codec, WCD934X_CPE_SS_BACKUP_INT, 0x02); + + /* Collect WDSP ramdump for debug use */ + if (cntl->m_dev && cntl->m_ops && cntl->m_ops->signal_handler) { + arg.mem_dumps_enabled = cntl->ramdump_enable; + arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR; + arg.dump_size = WCD_934X_RAMDUMP_SIZE; + cntl->m_ops->signal_handler(cntl->m_dev, WDSP_DEBUG_DUMP, + &arg); + } + + /* Unmask the fatal irqs */ + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, + ~(cntl->irqs.fatal_irqs & 0xFF)); + snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, + ~((cntl->irqs.fatal_irqs >> 8) & 0xFF)); + + WCD_CNTL_CLR_ERR_IRQ_FLAG(cntl); +} +#else +#define WCD_CNTL_SET_ERR_IRQ_FLAG(cntl) 0 +#define WCD_CNTL_CLR_ERR_IRQ_FLAG(cntl) do {} while (0) +static void wcd_cntl_collect_debug_dumps(struct wcd_dsp_cntl *cntl) +{ +} +#endif + static ssize_t wdsp_boot_show(struct wcd_dsp_cntl *cntl, char *buf) { return snprintf(buf, WCD_SYSFS_ENTRY_MAX_LEN, @@ -663,6 +753,7 @@ static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl) if (!ret) { dev_err(codec->dev, "%s: WDSP boot timed out\n", __func__); + wcd_cntl_collect_debug_dumps(cntl); ret = -ETIMEDOUT; goto err_boot; } else { @@ -719,7 +810,7 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data) struct wdsp_err_signal_arg arg; u16 status = 0; u8 reg_val; - int ret = 0; + int rc, ret = 0; reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A); status = status | reg_val; @@ -732,6 +823,12 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data) if ((status & cntl->irqs.fatal_irqs) && (cntl->m_dev && cntl->m_ops && cntl->m_ops->signal_handler)) { + /* + * If WDSP SSR happens, skip collecting debug dumps. + * If debug dumps collecting happens first, WDSP_ERR_INTR + * will be blocked in signal_handler and get processed later. + */ + rc = WCD_CNTL_SET_ERR_IRQ_FLAG(cntl); arg.mem_dumps_enabled = cntl->ramdump_enable; arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR; arg.dump_size = WCD_934X_RAMDUMP_SIZE; @@ -742,6 +839,8 @@ static irqreturn_t wcd_cntl_err_irq(int irq, void *data) "%s: Failed to handle fatal irq 0x%x\n", __func__, status & cntl->irqs.fatal_irqs); wcd_cntl_change_online_state(cntl, 0); + if (rc == 0) + WCD_CNTL_CLR_ERR_IRQ_FLAG(cntl); } else { dev_err(cntl->codec->dev, "%s: Invalid signal_handler\n", __func__); @@ -912,7 +1011,7 @@ static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, bool vote; int ret = 0; - if (count == 0 || count > 2) { + if (count == 0 || count > WCD_MISCDEV_CMD_MAX_LEN) { pr_err("%s: Invalid count = %zd\n", __func__, count); ret = -EINVAL; goto done; @@ -939,6 +1038,11 @@ static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, } cntl->boot_reqs--; vote = false; + } else if (!strcmp(val, "DEBUG_DUMP")) { + dev_dbg(cntl->codec->dev, + "%s: Collect dumps for debug use\n", __func__); + wcd_cntl_collect_debug_dumps(cntl); + goto done; } else { dev_err(cntl->codec->dev, "%s: Invalid value %s\n", __func__, val); @@ -1309,6 +1413,7 @@ void wcd_dsp_cntl_init(struct snd_soc_codec *codec, mutex_init(&control->clk_mutex); mutex_init(&control->ssr_mutex); init_waitqueue_head(&control->ssr_entry.offline_poll_wait); + WCD_CNTL_CLR_ERR_IRQ_FLAG(control); /* * The default state of WDSP is in SVS mode. diff --git a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h index e934638cc487..c4571d05038d 100644 --- a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h +++ b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016, 2018, 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 @@ -109,6 +109,10 @@ struct wcd_dsp_cntl { /* Misc device related */ char miscdev_name[256]; struct miscdevice miscdev; +#ifdef CONFIG_DEBUG_FS + /* Debug dump related */ + atomic_t err_irq_flag; +#endif }; void wcd_dsp_cntl_init(struct snd_soc_codec *codec, From 540625c39fb1aaa4e72ce8fac35bd6c162990d0c Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Mon, 22 Jan 2018 15:25:26 +0800 Subject: [PATCH 158/276] ASoC: wcd934x: skip mutex lock for WDSP boot timeout debug dump In the case of WDSP boot timeout, api_mutex and ssr_mutex have already been acquired. There is no need to do mutex lock again during debug dump. Check the signal enum to see if it's the internal WDSP boot timeout case. Change-Id: I6fe5e77b1bff72ed5ad463bb1df76c6b02c84c92 Signed-off-by: Xiaojun Sang --- asoc/codecs/wcd-dsp-mgr.c | 37 ++++++++++++++++++++++---- asoc/codecs/wcd934x/wcd934x-dsp-cntl.c | 16 ++++++----- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/asoc/codecs/wcd-dsp-mgr.c b/asoc/codecs/wcd-dsp-mgr.c index ce9a6972451c..058a6cf0c23d 100644 --- a/asoc/codecs/wcd-dsp-mgr.c +++ b/asoc/codecs/wcd-dsp-mgr.c @@ -839,21 +839,20 @@ static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg, } #ifdef CONFIG_DEBUG_FS -static int wdsp_debug_dump_handler(struct wdsp_mgr_priv *wdsp, void *arg) +static int __wdsp_dbg_dump_locked(struct wdsp_mgr_priv *wdsp, void *arg) { struct wdsp_err_signal_arg *err_data; int ret = 0; - WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); /* If there is no SSR, set the SSR type to collect ramdumps */ if (wdsp->ssr_type == WDSP_SSR_TYPE_NO_SSR) { wdsp->ssr_type = WDSP_SSR_TYPE_WDSP_DOWN; } else { WDSP_DBG(wdsp, "SSR handling is running, skip debug ramdump"); ret = 0; - WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); goto done; } + if (arg) { err_data = (struct wdsp_err_signal_arg *) arg; memcpy(&wdsp->dump_data.err_data, err_data, @@ -861,16 +860,29 @@ static int wdsp_debug_dump_handler(struct wdsp_mgr_priv *wdsp, void *arg) } else { WDSP_DBG(wdsp, "Invalid input, arg is NULL"); ret = -EINVAL; - WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); goto done; } wdsp_collect_ramdumps(wdsp); wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR; - WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); done: return ret; } +static int wdsp_debug_dump_handler(struct wdsp_mgr_priv *wdsp, void *arg) +{ + int ret = 0; + + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex); + ret = __wdsp_dbg_dump_locked(wdsp, arg); + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex); + + return ret; +} #else +static int __wdsp_dbg_dump_locked(struct wdsp_mgr_priv *wdsp, void *arg) +{ + return 0; +} + static int wdsp_debug_dump_handler(struct wdsp_mgr_priv *wdsp, void *arg) { return 0; @@ -887,7 +899,13 @@ static int wdsp_signal_handler(struct device *wdsp_dev, return -EINVAL; wdsp = dev_get_drvdata(wdsp_dev); + +#ifdef CONFIG_DEBUG_FS + if (signal != WDSP_DEBUG_DUMP_INTERNAL) + WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); +#else WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex); +#endif WDSP_DBG(wdsp, "Raised signal %d", signal); @@ -908,6 +926,9 @@ static int wdsp_signal_handler(struct device *wdsp_dev, case WDSP_DEBUG_DUMP: ret = wdsp_debug_dump_handler(wdsp, arg); break; + case WDSP_DEBUG_DUMP_INTERNAL: + ret = __wdsp_dbg_dump_locked(wdsp, arg); + break; default: ret = -EINVAL; break; @@ -916,7 +937,13 @@ static int wdsp_signal_handler(struct device *wdsp_dev, if (ret < 0) WDSP_ERR(wdsp, "handling signal %d failed with error %d", signal, ret); + +#ifdef CONFIG_DEBUG_FS + if (signal != WDSP_DEBUG_DUMP_INTERNAL) + WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); +#else WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex); +#endif return ret; } diff --git a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c index 85b38654a51c..98223b3b9dd6 100644 --- a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -101,6 +101,7 @@ static u16 wdsp_reg_for_debug_dump[] = { WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL, WCD934X_CPE_SS_MAD_CTL, WCD934X_CPE_SS_CPAR_CTL, + WCD934X_CPE_SS_CPAR_CFG, WCD934X_CPE_SS_WDOG_CFG, WCD934X_CPE_SS_STATUS, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, @@ -113,10 +114,12 @@ static u16 wdsp_reg_for_debug_dump[] = { WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B, }; -static void wcd_cntl_collect_debug_dumps(struct wcd_dsp_cntl *cntl) +static void wcd_cntl_collect_debug_dumps(struct wcd_dsp_cntl *cntl, + bool internal) { struct snd_soc_codec *codec = cntl->codec; struct wdsp_err_signal_arg arg; + enum wdsp_signal signal; int i; u8 val; @@ -146,8 +149,8 @@ static void wcd_cntl_collect_debug_dumps(struct wcd_dsp_cntl *cntl) arg.mem_dumps_enabled = cntl->ramdump_enable; arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR; arg.dump_size = WCD_934X_RAMDUMP_SIZE; - cntl->m_ops->signal_handler(cntl->m_dev, WDSP_DEBUG_DUMP, - &arg); + signal = internal ? WDSP_DEBUG_DUMP_INTERNAL : WDSP_DEBUG_DUMP; + cntl->m_ops->signal_handler(cntl->m_dev, signal, &arg); } /* Unmask the fatal irqs */ @@ -161,7 +164,8 @@ static void wcd_cntl_collect_debug_dumps(struct wcd_dsp_cntl *cntl) #else #define WCD_CNTL_SET_ERR_IRQ_FLAG(cntl) 0 #define WCD_CNTL_CLR_ERR_IRQ_FLAG(cntl) do {} while (0) -static void wcd_cntl_collect_debug_dumps(struct wcd_dsp_cntl *cntl) +static void wcd_cntl_collect_debug_dumps(struct wcd_dsp_cntl *cntl, + bool internal) { } #endif @@ -753,7 +757,7 @@ static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl) if (!ret) { dev_err(codec->dev, "%s: WDSP boot timed out\n", __func__); - wcd_cntl_collect_debug_dumps(cntl); + wcd_cntl_collect_debug_dumps(cntl, true); ret = -ETIMEDOUT; goto err_boot; } else { @@ -1041,7 +1045,7 @@ static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, } else if (!strcmp(val, "DEBUG_DUMP")) { dev_dbg(cntl->codec->dev, "%s: Collect dumps for debug use\n", __func__); - wcd_cntl_collect_debug_dumps(cntl); + wcd_cntl_collect_debug_dumps(cntl, false); goto done; } else { dev_err(cntl->codec->dev, "%s: Invalid value %s\n", From 40f12144c094da318310da8ad7cde5b0098e5629 Mon Sep 17 00:00:00 2001 From: Vikram Panduranga Date: Wed, 10 Jan 2018 18:04:54 -0800 Subject: [PATCH 159/276] asoc: Fix read offset for compress capture with timestamp In a compress capture usecase, read offset guides to move buffer pointer based on buffer length. When timestamp is enabled read offset also needs to include timestamp header offset along with buffer length. This fix addresses the issue of null memory map handle due to incorrect read offset. Change-Id: I93c3ec588e77d535b6c7a4a0d832c1e7ea5f62a7 Signed-off-by: Vikram Panduranga --- asoc/msm-compress-q6-v2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 775663926916..0de3969efe75 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -541,7 +541,7 @@ static int msm_compr_read_buffer(struct msm_compr_audio *prtd) return ret; } prtd->bytes_read += buffer_length; - prtd->bytes_read_offset += buffer_length; + prtd->bytes_read_offset += buffer_length + prtd->ts_header_offset; if (prtd->bytes_read_offset >= prtd->buffer_size) prtd->bytes_read_offset -= prtd->buffer_size; From 90991dbf98068ea8f9b01aba6c903769ca9b9fd2 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 15 Jan 2018 15:00:42 -0800 Subject: [PATCH 160/276] asoc: wcd934x: optimize ANC enablement logic for Tavil codec In Tavil driver, both ANC0 and ANC1 channel registers are written when each ANC channel is enabled. This logic results in high latency during ANC enablement on Tavil codec. Optimize this logic by only writing the corresponding channel registers when enabling ANC0 or ANC1. Change-Id: I62e8572967e6ca6c851cbaaad5f1b8c19e5b1a5f Signed-off-by: Xiaoyu Ye --- asoc/codecs/wcd934x/wcd934x.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 47595ad23c82..82cadb5547da 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -897,7 +897,6 @@ static int tavil_codec_enable_anc(struct snd_soc_dapm_widget *w, struct wcd9xxx_anc_header *anc_head; struct firmware_cal *hwdep_cal = NULL; u32 anc_writes_size = 0; - u32 anc_cal_size = 0; int anc_size_remaining; u32 *anc_ptr; u16 reg; @@ -986,8 +985,16 @@ static int tavil_codec_enable_anc(struct snd_soc_dapm_widget *w, goto err; } - anc_cal_size = anc_writes_size; - for (i = 0; i < anc_writes_size; i++) { + i = 0; + + if (!strcmp(w->name, "RX INT1 DAC") || + !strcmp(w->name, "RX INT3 DAC")) + anc_writes_size = anc_writes_size / 2; + else if (!strcmp(w->name, "RX INT2 DAC") || + !strcmp(w->name, "RX INT4 DAC")) + i = anc_writes_size / 2; + + for (; i < anc_writes_size; i++) { WCD934X_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val); snd_soc_write(codec, reg, (val & mask)); } From 48fcdcf8ac05be8b3d0a245368b65cf3cf92b2ff Mon Sep 17 00:00:00 2001 From: Vidyakumar Athota Date: Mon, 22 Jan 2018 15:44:58 -0800 Subject: [PATCH 161/276] swr-wcd-ctrl: update soundwire slave logical device number Soundwire slave logical device number is not updated properly for all the devices. Because of this, slave devices are pointed to wrong register regmap. Update logical device number properly for all the slave devices. Change-Id: Ic480585301d8e53500714bc136e23e95ac52b13c Signed-off-by: Vidyakumar Athota --- soc/swr-wcd-ctrl.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/soc/swr-wcd-ctrl.c b/soc/swr-wcd-ctrl.c index bb78372aa9e5..00ff9a02aa60 100644 --- a/soc/swr-wcd-ctrl.c +++ b/soc/swr-wcd-ctrl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -1291,6 +1291,7 @@ static int swrm_get_logical_dev_num(struct swr_master *mstr, u64 dev_id, u64 id = 0; int ret = -EINVAL; struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(mstr); + struct swr_device *swr_dev; if (!swrm) { pr_err("%s: Invalid handle to swr controller\n", @@ -1304,20 +1305,30 @@ static int swrm_get_logical_dev_num(struct swr_master *mstr, u64 dev_id, SWRM_ENUMERATOR_SLAVE_DEV_ID_2(i))) << 32); id |= swrm->read(swrm->handle, SWRM_ENUMERATOR_SLAVE_DEV_ID_1(i)); - if ((id & SWR_DEV_ID_MASK) == dev_id) { - if (swrm_get_device_status(swrm, i) == 0x01) { - *dev_num = i; - ret = 0; - } else { - dev_err(swrm->dev, "%s: device is not ready\n", - __func__); + /* + * As pm_runtime_get_sync() brings all slaves out of reset + * update logical device number for all slaves. + */ + list_for_each_entry(swr_dev, &mstr->devices, dev_list) { + if (swr_dev->addr == (id & SWR_DEV_ID_MASK)) { + u32 status = swrm_get_device_status(swrm, i); + + if ((status == 0x01) || (status == 0x02)) { + swr_dev->dev_num = i; + if ((id & SWR_DEV_ID_MASK) == dev_id) { + *dev_num = i; + ret = 0; + } + dev_dbg(swrm->dev, "%s: devnum %d is assigned for dev addr %lx\n", + __func__, i, swr_dev->addr); + } } - goto found; } } - dev_err(swrm->dev, "%s: device id 0x%llx does not match with 0x%llx\n", - __func__, id, dev_id); -found: + if (ret) + dev_err(swrm->dev, "%s: device 0x%llx is not ready\n", + __func__, dev_id); + pm_runtime_mark_last_busy(&swrm->pdev->dev); pm_runtime_put_autosuspend(&swrm->pdev->dev); return ret; From e9f2a44eac9b8be954a99a5a0728b9c01b1cecb5 Mon Sep 17 00:00:00 2001 From: Vaishnavi Kommaraju Date: Tue, 23 Jan 2018 18:10:21 +0530 Subject: [PATCH 162/276] asoc: codecs: Add mutex lock for CPE session Add mutex lock to ensure atomic access to core handle in CPE alloc and dealloc sessions. CRs-Fixed: 2169403 Change-Id: I7e046f349cc56ee06706cf15651dac3fdfe9d9a6 Signed-off-by: Vaishnavi Kommaraju --- asoc/codecs/wcd_cpe_core.c | 11 +++++++++++ asoc/codecs/wcd_cpe_core.h | 5 ++++- asoc/codecs/wcd_cpe_services.c | 4 +++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/wcd_cpe_core.c b/asoc/codecs/wcd_cpe_core.c index 2ce5652422d2..2e154e701c8c 100644 --- a/asoc/codecs/wcd_cpe_core.c +++ b/asoc/codecs/wcd_cpe_core.c @@ -1944,6 +1944,7 @@ struct wcd_cpe_core *wcd_cpe_init(const char *img_fname, init_completion(&core->online_compl); init_waitqueue_head(&core->ssr_entry.offline_poll_wait); mutex_init(&core->ssr_lock); + mutex_init(&core->session_lock); core->cpe_users = 0; core->cpe_clk_ref = 0; @@ -3395,6 +3396,7 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( * If this is the first session to be allocated, * only then register the afe service. */ + WCD_CPE_GRAB_LOCK(&core->session_lock, "session_lock"); if (!wcd_cpe_lsm_session_active()) afe_register_service = true; @@ -3409,6 +3411,7 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( dev_err(core->dev, "%s: max allowed sessions already allocated\n", __func__); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return NULL; } @@ -3417,6 +3420,7 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( dev_err(core->dev, "%s: Failed to enable cpe, err = %d\n", __func__, ret); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return NULL; } @@ -3459,6 +3463,8 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( init_completion(&session->cmd_comp); lsm_sessions[session_id] = session; + + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return session; err_afe_mode_cmd: @@ -3473,6 +3479,7 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session( err_session_alloc: wcd_cpe_vote(core, false); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return NULL; } @@ -3623,9 +3630,11 @@ static int wcd_cpe_dealloc_lsm_session(void *core_handle, struct wcd_cpe_core *core = core_handle; int ret = 0; + WCD_CPE_GRAB_LOCK(&core->session_lock, "session_lock"); if (!session) { dev_err(core->dev, "%s: Invalid lsm session\n", __func__); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return -EINVAL; } @@ -3636,6 +3645,7 @@ static int wcd_cpe_dealloc_lsm_session(void *core_handle, "%s: Wrong session id %d max allowed = %d\n", __func__, session->id, WCD_CPE_LSM_MAX_SESSIONS); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return -EINVAL; } @@ -3656,6 +3666,7 @@ static int wcd_cpe_dealloc_lsm_session(void *core_handle, "%s: Failed to un-vote cpe, err = %d\n", __func__, ret); + WCD_CPE_REL_LOCK(&core->session_lock, "session_lock"); return ret; } diff --git a/asoc/codecs/wcd_cpe_core.h b/asoc/codecs/wcd_cpe_core.h index 3d672b860ff4..5884a528541f 100644 --- a/asoc/codecs/wcd_cpe_core.h +++ b/asoc/codecs/wcd_cpe_core.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2018, 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 @@ -172,6 +172,9 @@ struct wcd_cpe_core { /* mutex to protect cpe ssr status variables */ struct mutex ssr_lock; + /* mutex to protect cpe session status variables */ + struct mutex session_lock; + /* Store the calibration data needed for cpe */ struct cal_type_data *cal_data[WCD_CPE_LSM_CAL_MAX]; diff --git a/asoc/codecs/wcd_cpe_services.c b/asoc/codecs/wcd_cpe_services.c index 522ce7abacd6..fc8242a91275 100644 --- a/asoc/codecs/wcd_cpe_services.c +++ b/asoc/codecs/wcd_cpe_services.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, 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 @@ -606,8 +606,10 @@ static enum cpe_svc_result cpe_deregister_generic(struct cpe_info *t_info, return CPE_SVC_INVALID_HANDLE; } + CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); list_del(&(n->list)); kfree(reg_handle); + CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc"); return CPE_SVC_SUCCESS; } From f29b82ad5c3784015a8e0d69c0d9b7c34d3a7234 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Mon, 29 Jan 2018 17:49:38 -0800 Subject: [PATCH 163/276] dsp: msm_audio_ion: modify the logic of audio ION memory allocation The current audio ION memory allocation logic will always try to allocate memory from audio heap first and then try to use system heap if the audio heap allocation failed. This logic is not correct since targets with SMMU will not use audio heap. Modify the calling sequence to check SMMU flag first before calling ion_alloc(). Change-Id: I8c5a17cbae904d548e3236857811a6a254fdfdfe Signed-off-by: Xiaoyu Ye --- dsp/msm_audio_ion.c | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/dsp/msm_audio_ion.c b/dsp/msm_audio_ion.c index 2d84ddf12fa8..e1e769e2d7d3 100644 --- a/dsp/msm_audio_ion.c +++ b/dsp/msm_audio_ion.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2018, 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 @@ -41,7 +41,6 @@ struct msm_audio_ion_private { bool smmu_enabled; - bool audioheap_enabled; struct device *cb_dev; struct dma_iommu_mapping *mapping; u8 device_status; @@ -112,26 +111,23 @@ int msm_audio_ion_alloc(const char *name, struct ion_client **client, 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; - } + if (msm_audio_ion_data.smmu_enabled == true) { + pr_debug("%s: system heap is used\n", __func__); + *handle = ion_alloc(*client, bufsz, SZ_4K, + ION_HEAP(ION_SYSTEM_HEAP_ID), 0); } else { - pr_debug("audio heap is used"); - msm_audio_ion_data.audioheap_enabled = 1; + pr_debug("%s: audio heap is used\n", __func__); + *handle = ion_alloc(*client, bufsz, SZ_4K, + ION_HEAP(ION_AUDIO_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; } rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len); From 4c6fc44d85c341bd0542da2c42e21b0f47c2b08f Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Tue, 6 Feb 2018 15:22:37 -0800 Subject: [PATCH 164/276] asoc: codecs: add null pointer check for swr control data Null check is needed before accessing swr control data to ensure proper error handling in case speaker path is issued when swr is not enabled. Change-Id: I5037a912652189b3ae58f1119d0534777a9c264a Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd934x/wcd934x.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 70800fa49c2e..812f0cbb54bc 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -2930,6 +2930,11 @@ static int __tavil_codec_enable_swr(struct snd_soc_dapm_widget *w, int event) tavil = snd_soc_codec_get_drvdata(codec); + if (!tavil->swr.ctrl_data) + return -EINVAL; + if (!tavil->swr.ctrl_data[0].swr_pdev) + return -EINVAL; + switch (event) { case SND_SOC_DAPM_PRE_PMU: if (((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) || From ba84ebdaae04b495cd859f4045d39ee225938d6c Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Thu, 1 Feb 2018 14:07:41 -0800 Subject: [PATCH 165/276] asoc: mbhc: fix for fake ins irq Check for in2p clamp state in insertion irq and ignore the interrupt if the clamp is already set, which means the mechanical removal is about to happen next. Change-Id: I55691d427cf07d5b61862b6dbd39f3ec34d873e5 Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd-mbhc-adc.c | 18 +++++++++++++++++- asoc/codecs/wcd-mbhc-v2.h | 3 ++- asoc/codecs/wcd934x/wcd934x-mbhc.c | 4 +++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/asoc/codecs/wcd-mbhc-adc.c b/asoc/codecs/wcd-mbhc-adc.c index 920796f2e39d..2be3a8db8494 100644 --- a/asoc/codecs/wcd-mbhc-adc.c +++ b/asoc/codecs/wcd-mbhc-adc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, 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 @@ -33,6 +33,7 @@ #define WCD_MBHC_ADC_HS_THRESHOLD_MV 1700 #define WCD_MBHC_ADC_HPH_THRESHOLD_MV 75 #define WCD_MBHC_ADC_MICBIAS_MV 1800 +#define WCD_MBHC_FAKE_INS_RETRY 4 static int wcd_mbhc_get_micbias(struct wcd_mbhc *mbhc) { @@ -1012,6 +1013,8 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data) { struct wcd_mbhc *mbhc = data; + u8 clamp_state = 0; + u8 clamp_retry = WCD_MBHC_FAKE_INS_RETRY; pr_debug("%s: enter\n", __func__); @@ -1026,6 +1029,19 @@ static irqreturn_t wcd_mbhc_adc_hs_ins_irq(int irq, void *data) return IRQ_HANDLED; } + do { + WCD_MBHC_REG_READ(WCD_MBHC_IN2P_CLAMP_STATE, clamp_state); + if (clamp_state) { + pr_debug("%s: fake insertion irq, leave\n", __func__); + return IRQ_HANDLED; + } + /* + * check clamp for 120ms but at 30ms chunks to leave + * room for other interrupts to be processed + */ + usleep_range(30000, 30100); + } while (--clamp_retry); + WCD_MBHC_RSC_LOCK(mbhc); /* * If current plug is headphone then there is no chance to diff --git a/asoc/codecs/wcd-mbhc-v2.h b/asoc/codecs/wcd-mbhc-v2.h index 94f096adcfbb..b9c178f0e532 100644 --- a/asoc/codecs/wcd-mbhc-v2.h +++ b/asoc/codecs/wcd-mbhc-v2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, 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 @@ -189,6 +189,7 @@ enum wcd_mbhc_register_function { WCD_MBHC_BTN_DBNC, WCD_MBHC_HS_VREF, WCD_MBHC_HS_COMP_RESULT, + WCD_MBHC_IN2P_CLAMP_STATE, WCD_MBHC_MIC_SCHMT_RESULT, WCD_MBHC_HPHL_SCHMT_RESULT, WCD_MBHC_HPHR_SCHMT_RESULT, diff --git a/asoc/codecs/wcd934x/wcd934x-mbhc.c b/asoc/codecs/wcd934x/wcd934x-mbhc.c index ae147ca0f411..bfe1e17a4534 100644 --- a/asoc/codecs/wcd934x/wcd934x-mbhc.c +++ b/asoc/codecs/wcd934x/wcd934x-mbhc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, 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 @@ -86,6 +86,8 @@ static struct wcd_mbhc_register WCD934X_MBHC_NEW_CTL_2, 0x03, 0, 0), WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", WCD934X_ANA_MBHC_RESULT_3, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_IN2P_CLAMP_STATE", + WCD934X_ANA_MBHC_RESULT_3, 0x10, 4, 0), WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", WCD934X_ANA_MBHC_RESULT_3, 0x20, 5, 0), WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", From 9fc4ca1a862e3be325fa9b1950df4a3d85891676 Mon Sep 17 00:00:00 2001 From: Yunfei Zhang Date: Thu, 8 Feb 2018 17:53:27 +0800 Subject: [PATCH 166/276] asoc:sdm845 check refcnt of MI2S when changing its state of pinctrl When both TX and RX of MI2S port are used, need check the refcnt before changing the pinctrl state, or it will be closed when there is still active stream on it. Change-Id: Ie588d946a9975475c8795c23e1b3154b194666f7 Signed-off-by: Yunfei Zhang --- asoc/sdm845.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index d0bb334a8fe1..dd9d371fc36c 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, 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 @@ -4698,12 +4698,6 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) __func__, cpu_dai->id); goto err; } - if (index == QUAT_MI2S) { - ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_MI2S_ACTIVE); - if (ret_pinctrl) - pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", - __func__, ret_pinctrl); - } /* * Muxtex protection in case the same MI2S * interface using for both TX and RX so @@ -4741,6 +4735,13 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) __func__, index, ret); goto clk_off; } + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, + STATE_MI2S_ACTIVE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } } clk_off: if (ret < 0) @@ -4776,15 +4777,16 @@ static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream) if (ret < 0) pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n", __func__, index, ret); + if (index == QUAT_MI2S) { + ret_pinctrl = msm_set_pinctrl(pinctrl_info, + STATE_DISABLE); + if (ret_pinctrl) + pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", + __func__, ret_pinctrl); + } } mutex_unlock(&mi2s_intf_conf[index].lock); - if (index == QUAT_MI2S) { - ret_pinctrl = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); - if (ret_pinctrl) - pr_err("%s: MI2S TLMM pinctrl set failed with %d\n", - __func__, ret_pinctrl); - } } static struct snd_soc_ops msm_mi2s_be_ops = { From 78193fa06b267c1d6582e5e6f9fb779cf067015e Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Thu, 15 Feb 2018 17:46:25 -0800 Subject: [PATCH 167/276] dsp: q6asm: check for buffer size before read Check for debugfs ops buf size passed before reading to avoid reading out of bounds. Change-Id: Idd5e755ac16448af5751e2f3381097f234e74a74 Signed-off-by: Karthikeyan Mani --- dsp/q6asm.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 1c9c689c307c..8663f777590a 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -284,6 +284,11 @@ static ssize_t audio_output_latency_dbgfs_read(struct file *file, pr_err("%s: out_buffer is null\n", __func__); return 0; } + if (count < OUT_BUFFER_SIZE) { + pr_err("%s: read size %d exceeds buf size %zd\n", __func__, + OUT_BUFFER_SIZE, count); + 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); @@ -336,6 +341,11 @@ static ssize_t audio_input_latency_dbgfs_read(struct file *file, pr_err("%s: in_buffer is null\n", __func__); return 0; } + if (count < IN_BUFFER_SIZE) { + pr_err("%s: read size %d exceeds buf size %zd\n", __func__, + IN_BUFFER_SIZE, count); + 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, From d5bffcf0b39de874b8fed534d8ad69e4f2498d20 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Wed, 14 Feb 2018 16:54:06 -0800 Subject: [PATCH 168/276] asoc: wcd-mbhc: disable button current source after removal After headset removal disable button current source to not have any voltages output in micbias after headset removal from extension cable. Change-Id: I2a751c16c704176381bb26f375aebf351686e911 Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd-mbhc-adc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/asoc/codecs/wcd-mbhc-adc.c b/asoc/codecs/wcd-mbhc-adc.c index 2be3a8db8494..b0e2091a4fbc 100644 --- a/asoc/codecs/wcd-mbhc-adc.c +++ b/asoc/codecs/wcd-mbhc-adc.c @@ -997,6 +997,7 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_MODE, 0); WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ADC_EN, 0); wcd_mbhc_elec_hs_report_unplug(mbhc); + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0); if (hphpa_on) { hphpa_on = false; From 031248d9720de8da4bf7b3c7b12bf64517bff380 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Fri, 23 Feb 2018 12:58:57 +0530 Subject: [PATCH 169/276] asoc: msm: add check for integer overflow Add check for integer overflow of user supplied data for ADSP stream command. CRs-Fixed: 2173850 Change-Id: Ic70093e890b7a6dd07529d77d10fff003282a8ea Signed-off-by: Aditya Bavanari --- asoc/msm-compress-q6-v2.c | 10 ++++++++++ asoc/msm-pcm-q6-v2.c | 12 +++++++++++- asoc/msm-transcode-loopback-q6-v2.c | 12 +++++++++++- dsp/q6asm.c | 14 ++++++++++++-- 4 files changed, 44 insertions(+), 4 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 85e8af8525fd..d36b50af62a9 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -3641,6 +3641,7 @@ static int msm_compr_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, struct msm_compr_audio *prtd; int ret = 0; struct msm_adsp_event_data *event_data = NULL; + uint64_t actual_payload_len = 0; if (fe_id >= MSM_FRONTEND_DAI_MAX) { pr_err("%s Received invalid fe_id %lu\n", @@ -3678,6 +3679,15 @@ static int msm_compr_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, goto done; } + actual_payload_len = sizeof(struct msm_adsp_event_data) + + event_data->payload_len; + if (actual_payload_len >= U32_MAX) { + pr_err("%s payload length 0x%X exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= sizeof(ucontrol->value.bytes.data)) { pr_err("%s param length=%d exceeds limit", diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index f1b2ba521d68..59258c2e010b 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -1106,6 +1106,7 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, struct msm_audio *prtd; int ret = 0; struct msm_adsp_event_data *event_data = NULL; + uint64_t actual_payload_len = 0; if (!pdata) { pr_err("%s pdata is NULL\n", __func__); @@ -1142,6 +1143,15 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, goto done; } + actual_payload_len = sizeof(struct msm_adsp_event_data) + + event_data->payload_len; + if (actual_payload_len >= U32_MAX) { + pr_err("%s payload length 0x%X exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= sizeof(ucontrol->value.bytes.data)) { pr_err("%s param length=%d exceeds limit", diff --git a/asoc/msm-transcode-loopback-q6-v2.c b/asoc/msm-transcode-loopback-q6-v2.c index eaef498c49b7..3932a18434d2 100644 --- a/asoc/msm-transcode-loopback-q6-v2.c +++ b/asoc/msm-transcode-loopback-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2017-2018, 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 @@ -502,6 +502,7 @@ static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol, struct msm_transcode_loopback *prtd; int ret = 0; struct msm_adsp_event_data *event_data = NULL; + uint64_t actual_payload_len = 0; if (fe_id >= MSM_FRONTEND_DAI_MAX) { pr_err("%s Received invalid fe_id %lu\n", @@ -539,6 +540,15 @@ static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol, goto done; } + actual_payload_len = sizeof(struct msm_adsp_event_data) + + event_data->payload_len; + if (actual_payload_len >= U32_MAX) { + pr_err("%s payload length 0x%X exceeds limit", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= sizeof(ucontrol->value.bytes.data)) { pr_err("%s param length=%d exceeds limit", diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 1c9c689c307c..a8efab9bc7a7 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -1146,7 +1146,9 @@ int q6asm_send_stream_cmd(struct audio_client *ac, { char *asm_params = NULL; struct apr_hdr hdr; - int sz, rc; + int rc; + uint32_t sz = 0; + uint64_t actual_sz = 0; if (!data || !ac) { pr_err("%s: %s is NULL\n", __func__, @@ -1163,7 +1165,15 @@ int q6asm_send_stream_cmd(struct audio_client *ac, goto done; } - sz = sizeof(struct apr_hdr) + data->payload_len; + actual_sz = sizeof(struct apr_hdr) + data->payload_len; + if (actual_sz > U32_MAX) { + pr_err("%s: payload size 0x%X exceeds limit\n", + __func__, data->payload_len); + rc = -EINVAL; + goto done; + } + + sz = (uint32_t)actual_sz; asm_params = kzalloc(sz, GFP_KERNEL); if (!asm_params) { rc = -ENOMEM; From 1752880aadbe481621dda6a3b4362229b7ef8d41 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Tue, 19 Dec 2017 16:56:24 +0800 Subject: [PATCH 170/276] soc: soundwire: use pm runtime function to tear down soundwire master Use pm_runtime_put_sync_suspend to tear down soundwire master. It makes sure the process is synced with the autosuspend call flow. Change-Id: Ib4feccd905bcbf046e58bd5eaffde40ee391feda Signed-off-by: Xiaojun Sang --- soc/swr-wcd-ctrl.c | 45 +++++++++++++++++++++++---------------------- soc/swr-wcd-ctrl.h | 5 ++++- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/soc/swr-wcd-ctrl.c b/soc/swr-wcd-ctrl.c index 00ff9a02aa60..722b19a7193a 100644 --- a/soc/swr-wcd-ctrl.c +++ b/soc/swr-wcd-ctrl.c @@ -1476,6 +1476,7 @@ static int swrm_probe(struct platform_device *pdev) mutex_init(&swrm->mlock); INIT_LIST_HEAD(&swrm->mport_list); mutex_init(&swrm->reslock); + mutex_init(&swrm->force_down_lock); ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm, SWR_IRQ_REGISTER); @@ -1539,6 +1540,9 @@ static int swrm_probe(struct platform_device *pdev) swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm, SWR_IRQ_FREE); err_irq_fail: + mutex_destroy(&swrm->mlock); + mutex_destroy(&swrm->reslock); + mutex_destroy(&swrm->force_down_lock); err_pdata_fail: kfree(swrm); err_memory_fail: @@ -1562,6 +1566,7 @@ static int swrm_remove(struct platform_device *pdev) swr_unregister_master(&swrm->master); mutex_destroy(&swrm->mlock); mutex_destroy(&swrm->reslock); + mutex_destroy(&swrm->force_down_lock); kfree(swrm); return 0; } @@ -1625,13 +1630,20 @@ static int swrm_runtime_suspend(struct device *dev) int ret = 0; struct swr_master *mstr = &swrm->master; struct swr_device *swr_dev; + int current_state = 0; dev_dbg(dev, "%s: pm_runtime: suspend state: %d\n", __func__, swrm->state); mutex_lock(&swrm->reslock); - if ((swrm->state == SWR_MSTR_RESUME) || - (swrm->state == SWR_MSTR_UP)) { - if (swrm_is_port_en(&swrm->master)) { + mutex_lock(&swrm->force_down_lock); + current_state = swrm->state; + mutex_unlock(&swrm->force_down_lock); + if ((current_state == SWR_MSTR_RESUME) || + (current_state == SWR_MSTR_UP) || + (current_state == SWR_MSTR_SSR)) { + + if ((current_state != SWR_MSTR_SSR) && + swrm_is_port_en(&swrm->master)) { dev_dbg(dev, "%s ports are enabled\n", __func__); ret = -EBUSY; goto exit; @@ -1660,27 +1672,16 @@ static int swrm_device_down(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev); int ret = 0; - struct swr_master *mstr = &swrm->master; - struct swr_device *swr_dev; dev_dbg(dev, "%s: swrm state: %d\n", __func__, swrm->state); - mutex_lock(&swrm->reslock); - if ((swrm->state == SWR_MSTR_RESUME) || - (swrm->state == SWR_MSTR_UP)) { - list_for_each_entry(swr_dev, &mstr->devices, dev_list) { - ret = swr_device_down(swr_dev); - if (ret) - dev_err(dev, - "%s: failed to shutdown swr dev %d\n", - __func__, swr_dev->dev_num); - } - dev_dbg(dev, "%s: Shutting down SWRM\n", __func__); - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_enable(dev); - swrm_clk_request(swrm, false); - } - mutex_unlock(&swrm->reslock); + + mutex_lock(&swrm->force_down_lock); + swrm->state = SWR_MSTR_SSR; + mutex_unlock(&swrm->force_down_lock); + /* Use pm runtime function to tear down */ + ret = pm_runtime_put_sync_suspend(dev); + pm_runtime_get_noresume(dev); + return ret; } diff --git a/soc/swr-wcd-ctrl.h b/soc/swr-wcd-ctrl.h index 52a60a3c6087..06958e0c9949 100644 --- a/soc/swr-wcd-ctrl.h +++ b/soc/swr-wcd-ctrl.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -32,6 +32,7 @@ enum { SWR_MSTR_RESUME, SWR_MSTR_UP, SWR_MSTR_DOWN, + SWR_MSTR_SSR, }; enum { @@ -102,6 +103,8 @@ struct swr_mstr_ctrl { struct platform_device *pdev; int num_rx_chs; u8 num_cfg_devs; + struct mutex force_down_lock; + int force_down_state; }; #endif /* _SWR_WCD_CTRL_H */ From e75ac98aa3f9389b0bf7449294d7dc5830894169 Mon Sep 17 00:00:00 2001 From: Sudheer Papothi Date: Mon, 4 Dec 2017 12:01:29 +0530 Subject: [PATCH 171/276] ASOC: wcd934x: Fix finding of correct AMIC During Tx path enablement, amic information is needed for proper power level settings and for better performance. Existing API returns incorrect amic information. Changes provide the correct amic information. Change-Id: I18f3f35212cae47e5d944c2e075f03889147722b Signed-off-by: Sudheer Papothi --- asoc/codecs/wcd934x/wcd934x.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 0662304e5e31..4934390b578f 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -4124,7 +4124,7 @@ static int tavil_codec_find_amic_input(struct snd_soc_codec *codec, if (adc_mux_n < 3) { adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + - adc_mux_n; + 2 * adc_mux_n; mask = 0x03; shift = 0; amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 + @@ -4137,7 +4137,7 @@ static int tavil_codec_find_amic_input(struct snd_soc_codec *codec, 2 * adc_mux_n; } else if (adc_mux_n < 7) { adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + - (adc_mux_n - 4); + 2 * (adc_mux_n - 4); mask = 0x0C; shift = 2; amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + @@ -4150,24 +4150,25 @@ static int tavil_codec_find_amic_input(struct snd_soc_codec *codec, adc_mux_n - 4; } else if (adc_mux_n < 12) { adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 + - ((adc_mux_n == 8) ? (adc_mux_n - 8) : - (adc_mux_n - 9)); + 2 * (((adc_mux_n == 8) ? (adc_mux_n - 8) : + (adc_mux_n - 9))); mask = 0x30; shift = 4; - amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + - adc_mux_n - 4; + amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0 + + ((adc_mux_n == 8) ? (adc_mux_n - 8) : + (adc_mux_n - 9)); } else if (adc_mux_n < 13) { adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1; mask = 0x30; shift = 4; amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + - adc_mux_n - 4; + adc_mux_n - 5; } else { adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1; mask = 0xC0; shift = 6; amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 + - adc_mux_n - 4; + adc_mux_n - 5; } is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask) >> shift) From b6a2b3c568ebd185efa0e2123274d320b58d3061 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Fri, 12 Jan 2018 13:17:59 +0800 Subject: [PATCH 172/276] ASoC: wcd-mbhc: correct special headset detection logic When detecting special headset, vref for special headset is set based on headset threshold reference 1800mv or based on regular micbias settings from device tree. Scale threshold reference as per the special headset micbias voltage. Change-Id: Ic4cc24e18efeb420b53a2154707c9399eb886181 Signed-off-by: Meng Wang --- asoc/codecs/wcd-mbhc-adc.c | 88 ++++++++++++++++++------------ asoc/codecs/wcd-mbhc-v2.h | 1 + asoc/codecs/wcd934x/wcd934x-mbhc.c | 12 +++- 3 files changed, 65 insertions(+), 36 deletions(-) diff --git a/asoc/codecs/wcd-mbhc-adc.c b/asoc/codecs/wcd-mbhc-adc.c index 2be3a8db8494..92784b8224ee 100644 --- a/asoc/codecs/wcd-mbhc-adc.c +++ b/asoc/codecs/wcd-mbhc-adc.c @@ -350,6 +350,42 @@ static int wcd_check_cross_conn(struct wcd_mbhc *mbhc) return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false; } +static int wcd_mbhc_adc_get_hs_thres(struct wcd_mbhc *mbhc) +{ + int hs_threshold, micbias_mv; + + micbias_mv = wcd_mbhc_get_micbias(mbhc); + if (mbhc->hs_thr) { + if (mbhc->micb_mv == micbias_mv) + hs_threshold = mbhc->hs_thr; + else + hs_threshold = (mbhc->hs_thr * + micbias_mv) / mbhc->micb_mv; + } else { + hs_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * + micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV); + } + return hs_threshold; +} + +static int wcd_mbhc_adc_get_hph_thres(struct wcd_mbhc *mbhc) +{ + int hph_threshold, micbias_mv; + + micbias_mv = wcd_mbhc_get_micbias(mbhc); + if (mbhc->hph_thr) { + if (mbhc->micb_mv == micbias_mv) + hph_threshold = mbhc->hph_thr; + else + hph_threshold = (mbhc->hph_thr * + micbias_mv) / mbhc->micb_mv; + } else { + hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV * + micbias_mv) / WCD_MBHC_ADC_MICBIAS_MV); + } + return hph_threshold; +} + static bool wcd_mbhc_adc_check_for_spl_headset(struct wcd_mbhc *mbhc, int *spl_hs_cnt) { @@ -371,18 +407,8 @@ static bool wcd_mbhc_adc_check_for_spl_headset(struct wcd_mbhc *mbhc, * btn press/relesae for HEADSET type during correct work. */ output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); - if (mbhc->hs_thr) - adc_threshold = mbhc->hs_thr; - else - adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * - wcd_mbhc_get_micbias(mbhc))/WCD_MBHC_ADC_MICBIAS_MV); - - if (mbhc->hph_thr) - adc_hph_threshold = mbhc->hph_thr; - else - adc_hph_threshold = ((WCD_MBHC_ADC_HPH_THRESHOLD_MV * - wcd_mbhc_get_micbias(mbhc))/ - WCD_MBHC_ADC_MICBIAS_MV); + adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); + adc_hph_threshold = wcd_mbhc_adc_get_hph_thres(mbhc); if (output_mv > adc_threshold || output_mv < adc_hph_threshold) { spl_hs = false; @@ -434,12 +460,8 @@ static bool wcd_is_special_headset(struct wcd_mbhc *mbhc) return false; } } - if (mbhc->hs_thr) - adc_threshold = mbhc->hs_thr; - else - adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * - wcd_mbhc_get_micbias(mbhc)) / - WCD_MBHC_ADC_MICBIAS_MV); + + adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); while (!is_spl_hs) { if (mbhc->hs_detect_work_stop) { @@ -572,15 +594,8 @@ static int wcd_mbhc_get_plug_from_adc(struct wcd_mbhc *mbhc, int adc_result) enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID; u32 hph_thr = 0, hs_thr = 0; - if (mbhc->hs_thr) - hs_thr = mbhc->hs_thr; - else - hs_thr = WCD_MBHC_ADC_HS_THRESHOLD_MV; - - if (mbhc->hph_thr) - hph_thr = mbhc->hph_thr; - else - hph_thr = WCD_MBHC_ADC_HPH_THRESHOLD_MV; + hs_thr = wcd_mbhc_adc_get_hs_thres(mbhc); + hph_thr = wcd_mbhc_adc_get_hph_thres(mbhc); if (adc_result < hph_thr) plug_type = MBHC_PLUG_TYPE_HEADPHONE; @@ -608,12 +623,16 @@ static void wcd_correct_swch_plug(struct work_struct *work) int output_mv = 0; int cross_conn; int try = 0; + int hs_threshold, micbias_mv; pr_debug("%s: enter\n", __func__); mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch); codec = mbhc->codec; + micbias_mv = wcd_mbhc_get_micbias(mbhc); + hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); + WCD_MBHC_RSC_LOCK(mbhc); /* Mask ADC COMPLETE interrupt */ wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS, false); @@ -690,13 +709,15 @@ static void wcd_correct_swch_plug(struct work_struct *work) */ plug_type = wcd_mbhc_get_plug_from_adc(mbhc, output_mv); - if ((output_mv > WCD_MBHC_ADC_HS_THRESHOLD_MV) && + if ((output_mv > hs_threshold) && (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) { spl_hs = wcd_mbhc_adc_check_for_spl_headset(mbhc, &spl_hs_count); + output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) { - output_mv = WCD_MBHC_ADC_HS_THRESHOLD_MV; + hs_threshold = (hs_threshold * + wcd_mbhc_get_micbias(mbhc)) / micbias_mv; spl_hs = true; mbhc->micbias_enable = true; } @@ -705,7 +726,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) if (mbhc->mbhc_cb->hph_pa_on_status) is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec); - if ((output_mv <= WCD_MBHC_ADC_HS_THRESHOLD_MV) && + if ((output_mv <= hs_threshold) && (!is_pa_on)) { /* Check for cross connection*/ ret = wcd_check_cross_conn(mbhc); @@ -759,7 +780,7 @@ static void wcd_correct_swch_plug(struct work_struct *work) } } - if (output_mv > WCD_MBHC_ADC_HS_THRESHOLD_MV) { + if (output_mv > hs_threshold) { pr_debug("%s: cable is extension cable\n", __func__); plug_type = MBHC_PLUG_TYPE_HIGH_HPH; wrk_complete = true; @@ -924,9 +945,8 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); - adc_threshold = ((WCD_MBHC_ADC_HS_THRESHOLD_MV * - wcd_mbhc_get_micbias(mbhc)) / - WCD_MBHC_ADC_MICBIAS_MV); + adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); + do { retry++; /* diff --git a/asoc/codecs/wcd-mbhc-v2.h b/asoc/codecs/wcd-mbhc-v2.h index b9c178f0e532..f362f48171ca 100644 --- a/asoc/codecs/wcd-mbhc-v2.h +++ b/asoc/codecs/wcd-mbhc-v2.h @@ -526,6 +526,7 @@ struct wcd_mbhc { bool gnd_swh; /*track GND switch NC / NO */ u32 hs_thr; u32 hph_thr; + u32 micb_mv; u32 swap_thr; u32 moist_vref; u32 moist_iref; diff --git a/asoc/codecs/wcd934x/wcd934x-mbhc.c b/asoc/codecs/wcd934x/wcd934x-mbhc.c index bfe1e17a4534..c89ea3aa4303 100644 --- a/asoc/codecs/wcd934x/wcd934x-mbhc.c +++ b/asoc/codecs/wcd934x/wcd934x-mbhc.c @@ -1,5 +1,4 @@ -/* - * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -1079,6 +1078,7 @@ int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, struct snd_soc_codec *codec, struct wcd934x_mbhc *wcd934x_mbhc; struct wcd_mbhc *wcd_mbhc; int ret; + struct wcd9xxx_pdata *pdata; wcd934x_mbhc = devm_kzalloc(codec->dev, sizeof(struct wcd934x_mbhc), GFP_KERNEL); @@ -1099,6 +1099,14 @@ int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, struct snd_soc_codec *codec, /* Setting default mbhc detection logic to ADC for Tavil */ wcd_mbhc->mbhc_detection_logic = WCD_DETECTION_ADC; + pdata = dev_get_platdata(codec->dev->parent); + if (!pdata) { + dev_err(codec->dev, "%s: pdata pointer is NULL\n", __func__); + ret = -EINVAL; + goto err; + } + wcd_mbhc->micb_mv = pdata->micbias.micb2_mv; + ret = wcd_mbhc_init(wcd_mbhc, codec, &mbhc_cb, &intr_ids, wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED); From 284bff41d50610bb19ec529d004d44b7dbb96f49 Mon Sep 17 00:00:00 2001 From: Vikram Panduranga Date: Wed, 27 Sep 2017 12:17:36 -0700 Subject: [PATCH 173/276] dsp: Mark cal block after use After applying calibration on DSP, cal block is marked stale to ensure same calibration is not reused for future usecase. Change-Id: I9f446c0602f4ab34ca71b9d2611319624fb19cd2 Signed-off-by: Vikram Panduranga --- asoc/msm-pcm-routing-v2.c | 27 +++++++-- dsp/audio_cal_utils.c | 33 ++++++++++- dsp/q6adm.c | 18 ++++-- dsp/q6afe.c | 35 +++++++++--- dsp/q6asm.c | 103 ++++++++++++++++++---------------- include/dsp/audio_cal_utils.h | 7 ++- include/dsp/q6asm-v2.h | 5 ++ 7 files changed, 159 insertions(+), 69 deletions(-) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 240d5c97b98b..396d21f4b6e2 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -896,6 +896,9 @@ static struct cal_block_data *msm_routing_find_topology_by_path(int path, cal_block = list_entry(ptr, struct cal_block_data, list); + if (cal_utils_is_cal_stale(cal_block)) + continue; + if (((struct audio_cal_info_adm_top *)cal_block ->cal_info)->path == path) { return cal_block; @@ -922,6 +925,9 @@ static struct cal_block_data *msm_routing_find_topology(int path, cal_block = list_entry(ptr, struct cal_block_data, list); + if (cal_utils_is_cal_stale(cal_block)) + continue; + cal_info = (struct audio_cal_info_adm_top *) cal_block->cal_info; if ((cal_info->path == path) && @@ -932,9 +938,14 @@ static struct cal_block_data *msm_routing_find_topology(int path, } pr_debug("%s: Can't find topology for path %d, app %d, acdb_id %d defaulting to search by path\n", __func__, path, app_type, acdb_id); - return msm_routing_find_topology_by_path(cal_index, path); + return msm_routing_find_topology_by_path(path, cal_index); } +/* + * Retrieving cal_block will mark cal_block as stale. + * Hence it cannot be reused or resent unless the flag + * is reset. + */ static int msm_routing_get_adm_topology(int fedai_id, int session_type, int be_id) { @@ -956,20 +967,24 @@ static int msm_routing_get_adm_topology(int fedai_id, int session_type, cal_block = msm_routing_find_topology(session_type, app_type, acdb_dev_id, ADM_TOPOLOGY_CAL_TYPE_IDX); - if (cal_block != NULL) + if (cal_block != NULL) { topology = ((struct audio_cal_info_adm_top *) cal_block->cal_info)->topology; - mutex_unlock(&cal_data[ADM_TOPOLOGY_CAL_TYPE_IDX]->lock); + cal_utils_mark_cal_used(cal_block); + mutex_unlock(&cal_data[ADM_TOPOLOGY_CAL_TYPE_IDX]->lock); + } else { + mutex_unlock(&cal_data[ADM_TOPOLOGY_CAL_TYPE_IDX]->lock); - if (cal_block == NULL) { pr_debug("%s: Check for LSM topology\n", __func__); mutex_lock(&cal_data[ADM_LSM_TOPOLOGY_CAL_TYPE_IDX]->lock); cal_block = msm_routing_find_topology(session_type, app_type, acdb_dev_id, ADM_LSM_TOPOLOGY_CAL_TYPE_IDX); - if (cal_block != NULL) + if (cal_block != NULL) { topology = ((struct audio_cal_info_adm_top *) cal_block->cal_info)->topology; + cal_utils_mark_cal_used(cal_block); + } mutex_unlock(&cal_data[ADM_LSM_TOPOLOGY_CAL_TYPE_IDX]->lock); } @@ -1427,7 +1442,7 @@ int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) { pr_err("%s: adm open failed copp_idx:%d\n", - __func__, copp_idx); + __func__, copp_idx); mutex_unlock(&routing_lock); return -EINVAL; } diff --git a/dsp/audio_cal_utils.c b/dsp/audio_cal_utils.c index f6b66d3ce5a7..c8956e4cdc22 100644 --- a/dsp/audio_cal_utils.c +++ b/dsp/audio_cal_utils.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2018, 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 @@ -1030,9 +1030,40 @@ int cal_utils_set_cal(size_t data_size, void *data, ((uint8_t *)data + sizeof(struct audio_cal_type_basic)), data_size - sizeof(struct audio_cal_type_basic)); + /* reset buffer stale flag */ + cal_block->cal_stale = false; + err: mutex_unlock(&cal_type->lock); done: return ret; } EXPORT_SYMBOL(cal_utils_set_cal); + +/** + * cal_utils_mark_cal_used + * + * @cal_block: pointer to cal block + */ +void cal_utils_mark_cal_used(struct cal_block_data *cal_block) +{ + if (cal_block) + cal_block->cal_stale = true; +} +EXPORT_SYMBOL(cal_utils_mark_cal_used); + +/** + * cal_utils_is_cal_stale + * + * @cal_block: pointer to cal block + * + * Returns true if cal block is stale, false otherwise + */ +bool cal_utils_is_cal_stale(struct cal_block_data *cal_block) +{ + if ((cal_block) && (cal_block->cal_stale)) + return true; + + return false; +} +EXPORT_SYMBOL(cal_utils_is_cal_stale); diff --git a/dsp/q6adm.c b/dsp/q6adm.c index 1144dbcd35a3..b0cdae795b43 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -1827,7 +1827,7 @@ static void send_adm_custom_topology(void) this_adm.set_custom_topology = 0; cal_block = cal_utils_get_only_cal_block(this_adm.cal_data[cal_index]); - if (cal_block == NULL) + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) goto unlock; pr_debug("%s: Sending cal_index %d\n", __func__, cal_index); @@ -2008,8 +2008,11 @@ static struct cal_block_data *adm_find_cal_by_path(int cal_index, int path) cal_block = list_entry(ptr, struct cal_block_data, list); + if (cal_utils_is_cal_stale(cal_block)) + continue; + if (cal_index == ADM_AUDPROC_CAL || - cal_index == ADM_LSM_AUDPROC_CAL) { + cal_index == ADM_LSM_AUDPROC_CAL) { audproc_cal_info = cal_block->cal_info; if ((audproc_cal_info->path == path) && (cal_block->cal_data.size > 0)) @@ -2042,8 +2045,11 @@ static struct cal_block_data *adm_find_cal_by_app_type(int cal_index, int path, cal_block = list_entry(ptr, struct cal_block_data, list); + if (cal_utils_is_cal_stale(cal_block)) + continue; + if (cal_index == ADM_AUDPROC_CAL || - cal_index == ADM_LSM_AUDPROC_CAL) { + cal_index == ADM_LSM_AUDPROC_CAL) { audproc_cal_info = cal_block->cal_info; if ((audproc_cal_info->path == path) && (audproc_cal_info->app_type == app_type) && @@ -2079,6 +2085,8 @@ static struct cal_block_data *adm_find_cal(int cal_index, int path, cal_block = list_entry(ptr, struct cal_block_data, list); + if (cal_utils_is_cal_stale(cal_block)) + continue; if (cal_index == ADM_AUDPROC_CAL || cal_index == ADM_LSM_AUDPROC_CAL) { @@ -2148,6 +2156,8 @@ static void send_adm_cal_type(int cal_index, int path, int port_id, ret = adm_remap_and_send_cal_block(cal_index, port_id, copp_idx, cal_block, perf_mode, app_type, acdb_id, sample_rate); + + cal_utils_mark_cal_used(cal_block); unlock: mutex_unlock(&this_adm.cal_data[cal_index]->lock); done: @@ -3077,7 +3087,7 @@ int send_rtac_audvol_cal(void) cal_block = cal_utils_get_only_cal_block( this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]); - if (cal_block == NULL) { + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) { pr_err("%s: can't find cal block!\n", __func__); goto unlock; } diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 42e0554befee..bc5bfdeb2f0b 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -886,7 +886,7 @@ static void afe_send_custom_topology(void) 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) { + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) { pr_err("%s cal_block not found!!\n", __func__); goto unlock; } @@ -1346,7 +1346,9 @@ static struct cal_block_data *afe_find_cal_topo_id_by_port( &cal_type->cal_blocks) { cal_block = list_entry(ptr, struct cal_block_data, list); - + /* Skip cal_block if it is already marked stale */ + if (cal_utils_is_cal_stale(cal_block)) + continue; path = ((afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX)?(TX_DEVICE):(RX_DEVICE)); afe_top = @@ -1374,6 +1376,11 @@ static struct cal_block_data *afe_find_cal_topo_id_by_port( return NULL; } +/* + * Retrieving cal_block will mark cal_block as stale. + * Hence it cannot be reused or resent unless the flag + * is reset. + */ static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id, int cal_type_index) { @@ -1383,7 +1390,8 @@ static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id, struct audio_cal_info_afe_top *afe_top_info = NULL; if (this_afe.cal_data[cal_type_index] == NULL) { - pr_err("%s: [AFE_TOPOLOGY_CAL] not initialized\n", __func__); + pr_err("%s: cal_type %d not initialized\n", __func__, + cal_type_index); return -EINVAL; } if (topology_id == NULL) { @@ -1396,8 +1404,8 @@ static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id, cal_block = afe_find_cal_topo_id_by_port( this_afe.cal_data[cal_type_index], port_id); if (cal_block == NULL) { - pr_err("%s: [AFE_TOPOLOGY_CAL] not initialized for this port %d\n", - __func__, port_id); + pr_err("%s: cal_type %d not initialized for this port %d\n", + __func__, cal_type_index, port_id); ret = -EINVAL; goto unlock; } @@ -1411,6 +1419,7 @@ static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id, goto unlock; } *topology_id = (u32)afe_top_info->topology; + cal_utils_mark_cal_used(cal_block); pr_debug("%s: port_id = %u acdb_id = %d topology_id = %u ret=%d\n", __func__, port_id, afe_top_info->acdb_id, @@ -1589,7 +1598,7 @@ static int send_afe_cal_type(int cal_index, int port_id) cal_block = cal_utils_get_only_cal_block( this_afe.cal_data[cal_index]); - if (cal_block == NULL) { + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) { pr_err("%s cal_block not found!!\n", __func__); ret = -EINVAL; goto unlock; @@ -1608,6 +1617,9 @@ static int send_afe_cal_type(int cal_index, int port_id) 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); + + cal_utils_mark_cal_used(cal_block); + unlock: mutex_unlock(&this_afe.cal_data[cal_index]->lock); done: @@ -5183,7 +5195,7 @@ static int afe_sidetone_iir(u16 tx_port_id) } 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) { + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) { pr_err("%s: cal_block not found\n ", __func__); mutex_unlock(&this_afe.cal_data[cal_index]->lock); ret = -EINVAL; @@ -5289,7 +5301,7 @@ static int afe_sidetone(u16 tx_port_id, u16 rx_port_id, bool enable) } 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) { + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) { pr_err("%s: cal_block not found\n", __func__); mutex_unlock(&this_afe.cal_data[cal_index]->lock); ret = -EINVAL; @@ -6754,6 +6766,9 @@ static struct cal_block_data *afe_find_hw_delay_by_path( cal_block = list_entry(ptr, struct cal_block_data, list); + if (cal_utils_is_cal_stale(cal_block)) + continue; + if (((struct audio_cal_info_hw_delay *)cal_block->cal_info) ->path == path) { return cal_block; @@ -6817,6 +6832,8 @@ static int afe_get_cal_hw_delay(int32_t path, ret = -EFAULT; goto unlock; } + + cal_utils_mark_cal_used(cal_block); pr_debug("%s: Path = %d samplerate = %u usec = %u status %d\n", __func__, path, entry->sample_rate, entry->delay_usec, ret); unlock: diff --git a/dsp/q6asm.c b/dsp/q6asm.c index e688914b3a93..6421ae84c358 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -124,8 +124,7 @@ static int q6asm_map_channels(u8 *channel_mapping, uint32_t channels, 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); +static int q6asm_get_asm_topology_apptype(struct q6asm_cal_info *cal_info); /* for ASM custom topology */ static struct cal_type_data *cal_data[ASM_MAX_CAL_TYPES]; @@ -759,7 +758,7 @@ int send_asm_custom_topology(struct audio_client *ac) set_custom_topology = 0; cal_block = cal_utils_get_only_cal_block(cal_data[ASM_CUSTOM_TOP_CAL]); - if (cal_block == NULL) + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) goto unlock; if (cal_block->cal_data.size == 0) { @@ -2537,6 +2536,7 @@ static int __q6asm_open_read(struct audio_client *ac, { int rc = 0x00; struct asm_stream_cmd_open_read_v3 open; + struct q6asm_cal_info cal_info; config_debug_fs_reset_index(); @@ -2556,12 +2556,15 @@ static int __q6asm_open_read(struct audio_client *ac, /* 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(); + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.preprocopo_id = cal_info.topology_id; + + 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(); + ac->app_type = cal_info.app_type; 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; @@ -2802,6 +2805,7 @@ static int __q6asm_open_write(struct audio_client *ac, uint32_t format, { int rc = 0x00; struct asm_stream_cmd_open_write_v3 open; + struct q6asm_cal_info cal_info; if (ac == NULL) { pr_err("%s: APR handle NULL\n", __func__); @@ -2850,7 +2854,9 @@ static int __q6asm_open_write(struct audio_client *ac, uint32_t format, open.sink_endpointype = ASM_END_POINT_DEVICE_MATRIX; open.bits_per_sample = bits_per_sample; - open.postprocopo_id = q6asm_get_asm_topology_cal(); + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.postprocopo_id = cal_info.topology_id; + if (ac->perf_mode != LEGACY_PCM_MODE) open.postprocopo_id = ASM_STREAM_POSTPROCOPO_ID_NONE; @@ -2863,7 +2869,7 @@ static int __q6asm_open_write(struct audio_client *ac, uint32_t format, */ if (!ac->topology) { ac->topology = open.postprocopo_id; - ac->app_type = q6asm_get_asm_app_type_cal(); + ac->app_type = cal_info.app_type; } switch (format) { case FORMAT_LINEAR_PCM: @@ -3049,6 +3055,7 @@ static int __q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format, { int rc = 0x00; struct asm_stream_cmd_open_readwrite_v2 open; + struct q6asm_cal_info cal_info; if (ac == NULL) { pr_err("%s: APR handle NULL\n", __func__); @@ -3070,12 +3077,13 @@ static int __q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format, 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(); + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.postprocopo_id = cal_info.topology_id; open.postprocopo_id = overwrite_topology ? topology : open.postprocopo_id; ac->topology = open.postprocopo_id; - ac->app_type = q6asm_get_asm_app_type_cal(); + ac->app_type = cal_info.app_type; switch (wr_format) { @@ -3233,6 +3241,7 @@ int q6asm_open_read_write_v2(struct audio_client *ac, uint32_t rd_format, int q6asm_open_loopback_v2(struct audio_client *ac, uint16_t bits_per_sample) { int rc = 0x00; + struct q6asm_cal_info cal_info; if (ac == NULL) { pr_err("%s: APR handle NULL\n", __func__); @@ -3257,9 +3266,10 @@ int q6asm_open_loopback_v2(struct audio_client *ac, uint16_t bits_per_sample) open.src_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; open.sink_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; /* source endpoint : matrix */ - open.audproc_topo_id = q6asm_get_asm_topology_cal(); + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.audproc_topo_id = cal_info.topology_id; - ac->app_type = q6asm_get_asm_app_type_cal(); + ac->app_type = cal_info.app_type; if (ac->perf_mode == LOW_LATENCY_PCM_MODE) open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION; else @@ -3288,9 +3298,10 @@ int q6asm_open_loopback_v2(struct audio_client *ac, uint16_t bits_per_sample) open.src_endpointype = 0; open.sink_endpointype = 0; /* source endpoint : matrix */ - open.postprocopo_id = q6asm_get_asm_topology_cal(); + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.postprocopo_id = cal_info.topology_id; - ac->app_type = q6asm_get_asm_app_type_cal(); + ac->app_type = cal_info.app_type; ac->topology = open.postprocopo_id; open.bits_per_sample = bits_per_sample; open.reserved = 0; @@ -3334,6 +3345,7 @@ int q6asm_open_transcode_loopback(struct audio_client *ac, { int rc = 0x00; struct asm_stream_cmd_open_transcode_loopback_t open; + struct q6asm_cal_info cal_info; if (ac == NULL) { pr_err("%s: APR handle NULL\n", __func__); @@ -3381,9 +3393,11 @@ int q6asm_open_transcode_loopback(struct audio_client *ac, } /* source endpoint : matrix */ - open.audproc_topo_id = q6asm_get_asm_topology_cal(); + rc = q6asm_get_asm_topology_apptype(&cal_info); + open.audproc_topo_id = cal_info.topology_id; + - ac->app_type = q6asm_get_asm_app_type_cal(); + ac->app_type = cal_info.app_type; if (ac->perf_mode == LOW_LATENCY_PCM_MODE) open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION; else @@ -3552,6 +3566,7 @@ int q6asm_open_shared_io(struct audio_client *ac, struct asm_stream_cmd_open_shared_io *open; u8 *channel_mapping; int i, size_of_open, num_watermarks, bufsz, bufcnt, rc, flags = 0; + struct q6asm_cal_info cal_info; if (!ac || !config) return -EINVAL; @@ -3618,7 +3633,8 @@ int q6asm_open_shared_io(struct audio_client *ac, 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(); + rc = q6asm_get_asm_topology_apptype(&cal_info); + open->topo_id = cal_info.topology_id; if (config->format == FORMAT_LINEAR_PCM) open->fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3; @@ -9098,51 +9114,39 @@ int q6asm_get_asm_app_type(int session_id) return app_type; } -static int q6asm_get_asm_topology_cal(void) +/* + * Retrieving cal_block will mark cal_block as stale. + * Hence it cannot be reused or resent unless the flag + * is reset. + */ +static int q6asm_get_asm_topology_apptype(struct q6asm_cal_info *cal_info) { - 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; + cal_info->topology_id = DEFAULT_POPP_TOPOLOGY; + cal_info->app_type = DEFAULT_APP_TYPE; 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) + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) goto unlock; - - app_type = ((struct audio_cal_info_asm_top *) + cal_info->topology_id = ((struct audio_cal_info_asm_top *) + cal_block->cal_info)->topology; + cal_info->app_type = ((struct audio_cal_info_asm_top *) cal_block->cal_info)->app_type; - if (app_type == 0) - app_type = DEFAULT_APP_TYPE; + cal_utils_mark_cal_used(cal_block); + unlock: mutex_unlock(&cal_data[ASM_TOPOLOGY_CAL]->lock); done: - pr_debug("%s: Using app_type %d\n", __func__, app_type); - return app_type; + pr_debug("%s: Using topology %d app_type %d\n", __func__, + cal_info->topology_id, cal_info->app_type); + + return 0; } int q6asm_send_cal(struct audio_client *ac) @@ -9178,8 +9182,9 @@ int q6asm_send_cal(struct audio_client *ac) 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", + if (cal_block == NULL || cal_utils_is_cal_stale(cal_block)) { + rc = 0; /* not error case */ + pr_err("%s: cal_block is NULL or stale\n", __func__); goto unlock; } @@ -9250,6 +9255,8 @@ int q6asm_send_cal(struct audio_client *ac) goto free; } + if (cal_block) + cal_utils_mark_cal_used(cal_block); rc = 0; free: diff --git a/include/dsp/audio_cal_utils.h b/include/dsp/audio_cal_utils.h index e12d8c1a9890..b25a96d8ef77 100644 --- a/include/dsp/audio_cal_utils.h +++ b/include/dsp/audio_cal_utils.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014, 2018, 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 @@ -38,6 +38,7 @@ struct cal_block_data { void *cal_info; struct list_head list; struct cal_data cal_data; + bool cal_stale; struct mem_map_data map_data; int32_t buffer_number; }; @@ -99,4 +100,8 @@ 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); + +void cal_utils_mark_cal_used(struct cal_block_data *cal_block); + +bool cal_utils_is_cal_stale(struct cal_block_data *cal_block); #endif diff --git a/include/dsp/q6asm-v2.h b/include/dsp/q6asm-v2.h index 157c243185b6..44b33ea16d53 100644 --- a/include/dsp/q6asm-v2.h +++ b/include/dsp/q6asm-v2.h @@ -246,6 +246,11 @@ struct audio_client { struct shared_io_config config; }; +struct q6asm_cal_info { + int topology_id; + int app_type; +}; + void q6asm_audio_client_free(struct audio_client *ac); struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv); From 2c819b646fccb9d23517561a2d602a97d45d98f9 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Tue, 27 Feb 2018 11:16:32 +0800 Subject: [PATCH 174/276] dsp: add null check for temp handle Variable mem_state in audio client(ac) is set to zero when there is a successful memory mapping. However the same variable is updated for various mapping commands. Ensure to check for both memstate and specific mem_handle to be updated in the wait condition to wake up the right waiting command. Change-Id: Iabba61cef0a90f636de50e9d27eecf886dc59a27 Signed-off-by: Meng Wang --- dsp/q6asm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 8663f777590a..b1c84a5146ac 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -6683,8 +6683,8 @@ static int q6asm_memory_map_regions(struct audio_client *ac, int dir, } rc = wait_event_timeout(ac->mem_wait, - (atomic_read(&ac->mem_state) >= 0) - , 5*HZ); + (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; From ee7ca6e8a3821063820a6bda1cc214763aaf06e3 Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Thu, 22 Feb 2018 14:46:09 -0800 Subject: [PATCH 175/276] asoc: dsp: Add support for adaptive bitrate Add support for adaptive bitrate with A2DP offload. Set up Tx feedback path on SLIMBUS_7_TX from BT SoC to LPASS. Configure AFE encoder and decoder for ABR. Add bit width support to configure AFE port with 32bit for BT A2DP. Change-Id: I8e0afaf52561e5dc70318240ba238fc42844501f Signed-off-by: Aniket Kumar Lata --- asoc/msm-dai-q6-v2.c | 123 +++++++++++++++++++-- asoc/msm-pcm-routing-v2.c | 38 +++++++ dsp/q6afe.c | 219 +++++++++++++++++++++++++++++++------ include/dsp/apr_audio-v2.h | 143 +++++++++++++++++++++++- include/dsp/q6afe-v2.h | 5 +- 5 files changed, 483 insertions(+), 45 deletions(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index b9734936df8c..d16cad04fb1c 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -46,6 +46,7 @@ enum { ENC_FMT_NONE, + DEC_FMT_NONE = 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, @@ -200,6 +201,7 @@ struct msm_dai_q6_dai_data { u32 afe_in_channels; u16 afe_in_bitformat; struct afe_enc_config enc_config; + struct afe_dec_config dec_config; union afe_port_config port_config; u16 vi_feed_mono; }; @@ -1538,22 +1540,46 @@ static int msm_dai_q6_prepare(struct snd_pcm_substream *substream, if (dai_data->enc_config.format != ENC_FMT_NONE) { int bitwidth = 0; - if (dai_data->afe_in_bitformat == - SNDRV_PCM_FORMAT_S24_LE) + switch (dai_data->afe_in_bitformat) { + case SNDRV_PCM_FORMAT_S32_LE: + bitwidth = 32; + break; + case SNDRV_PCM_FORMAT_S24_LE: bitwidth = 24; - else if (dai_data->afe_in_bitformat == - SNDRV_PCM_FORMAT_S16_LE) + break; + case SNDRV_PCM_FORMAT_S16_LE: + default: bitwidth = 16; + break; + } 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); + &dai_data->enc_config, NULL); if (rc < 0) pr_err("%s: afe_port_start_v2 failed error: %d\n", __func__, rc); + } else if (dai_data->dec_config.format != DEC_FMT_NONE) { + /* + * A dummy Tx session is established in LPASS to + * get the link statistics from BTSoC. + * Depacketizer extracts the bit rate levels and + * transmits them to the encoder on the Rx path. + * Since this is a dummy decoder - channels, bit + * width are sent as 0 and encoder config is NULL. + * This could be updated in the future if there is + * a complete Tx path set up that uses this decoder. + */ + rc = afe_port_start_v2(dai->id, &dai_data->port_config, + dai_data->rate, 0, 0, NULL, + &dai_data->dec_config); + if (rc < 0) { + pr_err("%s: fail to open AFE port 0x%x\n", + __func__, dai->id); + } } else { rc = afe_port_start(dai->id, &dai_data->port_config, dai_data->rate); @@ -2233,7 +2259,7 @@ static int msm_dai_q6_afe_enc_cfg_get(struct snd_kcontrol *kcontrol, if (dai_data) { int format_size = sizeof(dai_data->enc_config.format); - pr_debug("%s:encoder config for %d format\n", + 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, @@ -2345,10 +2371,11 @@ 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 char *const afe_input_bit_format_text[] = {"S16_LE", "S24_LE", + "S32_LE"}; static const struct soc_enum afe_input_bit_format_enum[] = { - SOC_ENUM_SINGLE_EXT(2, afe_input_bit_format_text), + SOC_ENUM_SINGLE_EXT(3, afe_input_bit_format_text), }; static int msm_dai_q6_afe_input_channel_get(struct snd_kcontrol *kcontrol, @@ -2391,6 +2418,9 @@ static int msm_dai_q6_afe_input_bit_format_get( } switch (dai_data->afe_in_bitformat) { + case SNDRV_PCM_FORMAT_S32_LE: + ucontrol->value.integer.value[0] = 2; + break; case SNDRV_PCM_FORMAT_S24_LE: ucontrol->value.integer.value[0] = 1; break; @@ -2416,6 +2446,9 @@ static int msm_dai_q6_afe_input_bit_format_put( return -EINVAL; } switch (ucontrol->value.integer.value[0]) { + case 2: + dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S32_LE; + break; case 1: dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S24_LE; break; @@ -2483,6 +2516,73 @@ static const struct snd_kcontrol_new afe_enc_config_controls[] = { msm_dai_q6_afe_scrambler_mode_put), }; +static int msm_dai_q6_afe_dec_cfg_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = sizeof(struct afe_dec_config); + + return 0; +} + +static int msm_dai_q6_afe_dec_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + int format_size = 0; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + + format_size = sizeof(dai_data->dec_config.format); + memcpy(ucontrol->value.bytes.data, + &dai_data->dec_config.format, + format_size); + memcpy(ucontrol->value.bytes.data + format_size, + &dai_data->dec_config.abr_dec_cfg, + sizeof(struct afe_abr_dec_cfg_t)); + + return 0; +} + +static int msm_dai_q6_afe_dec_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data; + int format_size = 0; + + if (!dai_data) { + pr_err("%s: Invalid dai data\n", __func__); + return -EINVAL; + } + + memset(&dai_data->dec_config, 0x0, + sizeof(struct afe_dec_config)); + format_size = sizeof(dai_data->dec_config.format); + memcpy(&dai_data->dec_config.format, + ucontrol->value.bytes.data, + format_size); + memcpy(&dai_data->dec_config.abr_dec_cfg, + ucontrol->value.bytes.data + format_size, + sizeof(struct afe_abr_dec_cfg_t)); + + return 0; +} + +static const struct snd_kcontrol_new afe_dec_config_controls[] = { + { + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE), + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "SLIM_7_TX Decoder Config", + .info = msm_dai_q6_afe_dec_cfg_info, + .get = msm_dai_q6_afe_dec_cfg_get, + .put = msm_dai_q6_afe_dec_cfg_put, + }, +}; + static int msm_dai_q6_slim_rx_drift_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -2650,6 +2750,11 @@ static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) snd_ctl_new1(&avd_drift_config_controls[2], dai)); break; + case SLIMBUS_7_TX: + rc = snd_ctl_add(dai->component->card->snd_card, + snd_ctl_new1(&afe_dec_config_controls[0], + 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], diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 240d5c97b98b..1151dba00c03 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -61,6 +61,7 @@ static struct cal_type_data *cal_data[MAX_ROUTING_CAL_TYPES]; static int fm_switch_enable; static int hfp_switch_enable; +static int a2dp_switch_enable; static int int0_mi2s_switch_enable; static int int4_mi2s_switch_enable; static int pri_mi2s_switch_enable; @@ -2138,6 +2139,34 @@ static int msm_routing_put_hfp_switch_mixer(struct snd_kcontrol *kcontrol, return 1; } +static int msm_routing_a2dp_switch_mixer_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = a2dp_switch_enable; + pr_debug("%s: A2DP Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + +static int msm_routing_a2dp_switch_mixer_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dapm_widget *widget = + snd_soc_dapm_kcontrol_widget(kcontrol); + struct snd_soc_dapm_update *update = NULL; + + pr_debug("%s: A2DP Switch enable %ld\n", __func__, + ucontrol->value.integer.value[0]); + a2dp_switch_enable = ucontrol->value.integer.value[0]; + if (a2dp_switch_enable) + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, + 1, update); + else + snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, + 0, update); + return 1; +} + static int msm_routing_get_int0_mi2s_switch_mixer(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -11242,6 +11271,11 @@ static const struct snd_kcontrol_new usb_switch_mixer_controls = 0, 1, 0, msm_routing_get_usb_switch_mixer, msm_routing_put_usb_switch_mixer); +static const struct snd_kcontrol_new a2dp_slim7_switch_mixer_controls = + SOC_SINGLE_EXT("Switch", SND_SOC_NOPM, + 0, 1, 0, msm_routing_a2dp_switch_mixer_get, + msm_routing_a2dp_switch_mixer_put); + static const struct soc_enum lsm_port_enum = SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lsm_port_text), lsm_port_text); @@ -13025,6 +13059,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { &hfp_slim7_switch_mixer_controls), SND_SOC_DAPM_SWITCH("USB_DL_HL", SND_SOC_NOPM, 0, 0, &usb_switch_mixer_controls), + SND_SOC_DAPM_SWITCH("A2DP_SLIM7_UL_HL", SND_SOC_NOPM, 0, 0, + &a2dp_slim7_switch_mixer_controls), /* Mixer definitions */ SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0, @@ -15337,6 +15373,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"HFP_SLIM7_UL_HL", "Switch", "SLIMBUS_7_TX"}, {"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"}, {"AUX_PCM_RX", NULL, "INTHFP_DL_HL"}, + {"SLIM7_UL_HL", NULL, "A2DP_SLIM7_UL_HL"}, + {"A2DP_SLIM7_UL_HL", "Switch", "SLIMBUS_7_TX"}, {"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_DL_HL"}, {"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"}, {"SEC_AUXPCM_UL_HL", NULL, "SEC_AUX_PCM_TX"}, diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 42e0554befee..e3fa21ccb0e8 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -2870,6 +2870,92 @@ int afe_port_send_usb_dev_param(u16 port_id, union afe_port_config *afe_config) return ret; } +static int q6afe_send_dec_config(u16 port_id, + union afe_port_config afe_config, + struct afe_dec_config *cfg) +{ + struct afe_audioif_config_command config; + int index; + int ret; + size_t payload_size = sizeof(config) - sizeof(struct apr_hdr) - + sizeof(config.param) - sizeof(config.port); + + index = q6audio_get_port_index(port_id); + if (index < 0) { + pr_err("%s: Invalid index number: %d\n", __func__, index); + 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_address_lsw = 0x00; + config.param.payload_address_msw = 0x00; + config.param.mem_map_handle = 0x00; + config.pdata.module_id = AFE_MODULE_ID_DECODER; + config.param.payload_size = + payload_size + sizeof(config.port.dec_depkt_id_param); + pr_debug("%s:sending AFE_DECODER_PARAM_ID_DEPACKETIZER to DSP payload = %d", + __func__, config.param.payload_size); + config.pdata.param_id = AFE_DECODER_PARAM_ID_DEPACKETIZER_ID; + config.pdata.param_size = sizeof(config.port.dec_depkt_id_param); + config.port.dec_depkt_id_param.dec_depacketizer_id = + AFE_MODULE_ID_DEPACKETIZER_COP; + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE_DECODER_PARAM_ID_DEPACKETIZER for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + + config.param.payload_size = + payload_size + sizeof(config.port.imc_info_param); + pr_debug("%s:sending AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION to DSP payload = %d\n", + __func__, config.param.payload_size); + config.pdata.param_id = AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION; + config.pdata.param_size = sizeof(config.port.imc_info_param); + config.port.imc_info_param.imc_info = cfg->abr_dec_cfg.imc_info; + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION 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; + config.port.media_type.bit_width = + afe_config.slim_sch.bit_width; + 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 q6afe_send_enc_config(u16 port_id, union afe_enc_config_data *cfg, u32 format, union afe_port_config afe_config, @@ -2921,15 +3007,25 @@ static int q6afe_send_enc_config(u16 port_id, __func__); goto exit; } - - config.param.payload_size = payload_size + if (format == ASM_MEDIA_FMT_LDAC) { + config.param.payload_size = payload_size + + sizeof(config.port.enc_blk_param) + - sizeof(struct afe_abr_enc_cfg_t); + config.pdata.param_size = sizeof(config.port.enc_blk_param) + - sizeof(struct afe_abr_enc_cfg_t); + config.port.enc_blk_param.enc_cfg_blk_size = + sizeof(config.port.enc_blk_param.enc_blk_config) + - sizeof(struct afe_abr_enc_cfg_t); + } else { + config.param.payload_size = payload_size + sizeof(config.port.enc_blk_param); + 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); + } 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) { @@ -2985,6 +3081,39 @@ static int q6afe_send_enc_config(u16 port_id, goto exit; } + if (format == ASM_MEDIA_FMT_LDAC) { + config.param.payload_size = + payload_size + sizeof(config.port.map_param); + pr_debug("%s:sending AFE_ENCODER_PARAM_ID_BIT_RATE_LEVEL_MAP to DSP payload = %d\n", + __func__, config.param.payload_size); + config.pdata.param_id = AFE_ENCODER_PARAM_ID_BIT_RATE_LEVEL_MAP; + config.pdata.param_size = sizeof(config.port.map_param); + config.port.map_param.mapping_table = + cfg->ldac_config.abr_config.mapping_info; + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE_ENCODER_PARAM_ID_BIT_RATE_LEVEL_MAP for port 0x%x failed %d\n", + __func__, port_id, ret); + goto exit; + } + + config.param.payload_size = + payload_size + sizeof(config.port.imc_info_param); + pr_debug("%s:sending AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION to DSP payload = %d\n", + __func__, config.param.payload_size); + config.pdata.param_id = + AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION; + config.pdata.param_size = sizeof(config.port.imc_info_param); + config.port.imc_info_param.imc_info = + cfg->ldac_config.abr_config.imc_info; + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION 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); @@ -2993,14 +3122,12 @@ static int q6afe_send_enc_config(u16 port_id, 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; - if (format == ASM_MEDIA_FMT_LDAC) { + if (format == ASM_MEDIA_FMT_LDAC) config.port.media_type.sample_rate = - config.port.enc_blk_param.enc_blk_config.ldac_config. - custom_config.sample_rate; - } else { + cfg->ldac_config.custom_config.sample_rate; + else 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; @@ -3029,8 +3156,9 @@ static int q6afe_send_enc_config(u16 port_id, 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, - u32 scrambler_mode) + union afe_enc_config_data *enc_cfg, + u32 codec_format, u32 scrambler_mode, + struct afe_dec_config *dec_cfg) { struct afe_audioif_config_command config; int ret = 0; @@ -3275,7 +3403,8 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config, config.pdata.param_size = sizeof(config.port); config.port = *afe_config; - if ((enc_format != ASM_MEDIA_FMT_NONE) && + if (((enc_cfg != NULL) || (dec_cfg != NULL)) && + (codec_format != ASM_MEDIA_FMT_NONE) && (cfg_type == AFE_PARAM_ID_SLIMBUS_CONFIG)) { config.port.slim_sch.data_format = AFE_SB_DATA_FORMAT_GENERIC_COMPRESSED; @@ -3287,18 +3416,31 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config, goto fail_cmd; } - if ((enc_format != ASM_MEDIA_FMT_NONE) && + if ((codec_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, - scrambler_mode); - if (ret) { - pr_err("%s: AFE encoder config for port 0x%x failed %d\n", - __func__, port_id, ret); - goto fail_cmd; + if (enc_cfg != NULL) + pr_debug("%s: Found AFE encoder support for SLIMBUS enc_format = %d\n", + __func__, codec_format); + ret = q6afe_send_enc_config(port_id, enc_cfg, + codec_format, *afe_config, + afe_in_channels, + afe_in_bit_width, + scrambler_mode); + if (ret) { + pr_err("%s: AFE encoder config for port 0x%x failed %d\n", + __func__, port_id, ret); + goto fail_cmd; + } + if (dec_cfg != NULL) { + pr_debug("%s: Found AFE decoder support for SLIMBUS dec_format = %d\n", + __func__, codec_format); + ret = q6afe_send_dec_config(port_id, *afe_config, + dec_cfg); + if (ret) { + pr_err("%s: AFE decoder config for port 0x%x failed %d\n", + __func__, port_id, ret); + goto fail_cmd; + } } } @@ -3347,31 +3489,42 @@ 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, 0); + 0, 0, NULL, ASM_MEDIA_FMT_NONE, 0, NULL); } EXPORT_SYMBOL(afe_port_start); /** * afe_port_start_v2 - to configure AFE session with - * specified port configuration and encoder params + * specified port configuration and encoder /decoder params * * @port_id: AFE port id number * @afe_config: port configutation * @rate: sampling rate of port - * @cfg: AFE encoder configuration information to setup encoder + * @enc_cfg: AFE enc configuration information to setup encoder * @afe_in_channels: AFE input channel configuration, this needs * update only if input channel is differ from AFE output + * @dec_cfg: AFE dec configuration information to set up decoder * * 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) + struct afe_enc_config *enc_cfg, + struct afe_dec_config *dec_cfg) { - return __afe_port_start(port_id, afe_config, rate, - afe_in_channels, afe_in_bit_width, - &enc_cfg->data, enc_cfg->format, - enc_cfg->scrambler_mode); + int ret = 0; + + if (enc_cfg != NULL) + ret = __afe_port_start(port_id, afe_config, rate, + afe_in_channels, afe_in_bit_width, + &enc_cfg->data, enc_cfg->format, + enc_cfg->scrambler_mode, dec_cfg); + else if (dec_cfg != NULL) + ret = __afe_port_start(port_id, afe_config, rate, + afe_in_channels, afe_in_bit_width, + NULL, dec_cfg->format, 0, dec_cfg); + + return ret; } EXPORT_SYMBOL(afe_port_start_v2); diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index 4cef82ef065d..9b67afd16674 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -3074,6 +3074,72 @@ struct afe_param_id_set_topology_cfg { u32 topology_id; } __packed; +#define MAX_ABR_LEVELS 5 + +struct afe_bit_rate_level_map_t { + /* + * Key value pair for link quality level to bitrate + * mapping in AFE + */ + uint32_t link_quality_level; + uint32_t bitrate; +} __packed; + +struct afe_quality_level_to_bitrate_info { + /* + * Number of quality levels being mapped. + * This will be equal to the size of mapping table. + */ + uint32_t num_levels; + /* + * Quality level to bitrate mapping table + */ + struct afe_bit_rate_level_map_t bit_rate_level_map[MAX_ABR_LEVELS]; +} __packed; + +struct afe_imc_dec_enc_info { + /* + * Decoder to encoder communication direction. + * Transmit = 0 / Receive = 1 + */ + uint32_t direction; + /* + * Enable / disable IMC between decoder and encoder + */ + uint32_t enable; + /* + * Purpose of IMC being set up between decoder and encoder. + * Param ID defined for link quality feedback in LPASS will + * be the default value sent as purpose. + * Supported values: + * AFE_ENCDEC_PURPOSE_ID_BT_INFO + */ + uint32_t purpose; + /* + * Unique communication instance ID. + * Data type a2dp_abr_instance used to set instance ID. + * purpose and comm_instance together form the actual key + * used in IMC registration, which must be the same for + * encoder and decoder for which IMC is being set up. + */ + uint32_t comm_instance; +} __packed; + +struct afe_abr_dec_cfg_t { + struct afe_imc_dec_enc_info imc_info; +} __packed; + +struct afe_abr_enc_cfg_t { + /* + * Link quality level to bitrate mapping info sent to DSP. + */ + struct afe_quality_level_to_bitrate_info mapping_info; + /* + * Information to set up IMC between decoder and encoder. + */ + struct afe_imc_dec_enc_info imc_info; +} __packed; + #define AFE_PARAM_ID_APTX_SYNC_MODE 0x00013205 struct afe_param_id_aptx_sync_mode { @@ -3124,6 +3190,39 @@ struct afe_param_id_aptx_sync_mode { */ #define AFE_ENCODER_PARAM_ID_ENABLE_SCRAMBLING 0x0001323C +/* + * Link quality level to bitrate mapping info sent to AFE Encoder. + * This parameter may be set runtime. + */ +#define AFE_ENCODER_PARAM_ID_BIT_RATE_LEVEL_MAP 0x000132E1 + +/* + * Parameter to set up Inter Module Communication (IMC) between + * AFE Decoder and Encoder. + * This parameter may be set runtime. + */ +#define AFE_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION 0x0001323D + +/* + * Purpose of IMC set up between encoder and decoder. + * Communication instance and purpose together form the + * actual key used for IMC registration. + */ +#define AFE_ENCDEC_PURPOSE_ID_BT_INFO 0x000132E2 + +#define AFE_MODULE_ID_DECODER 0x00013231 + +/* + * Macro for defining the depacketizer ID: COP. + */ +#define AFE_MODULE_ID_DEPACKETIZER_COP 0x00013233 + +/* + * Depacketizer type parameter for the #AVS_MODULE_ID_DECODER module. + * This parameter cannot be set runtime. + */ +#define AFE_DECODER_PARAM_ID_DEPACKETIZER_ID 0x00013235 + /* * Data format to send compressed data * is transmitted/received over Slimbus lines. @@ -3425,6 +3524,7 @@ struct asm_ldac_specific_enc_cfg_t { struct asm_ldac_enc_cfg_t { struct asm_custom_enc_cfg_t custom_config; struct asm_ldac_specific_enc_cfg_t ldac_specific_config; + struct afe_abr_enc_cfg_t abr_config; } __packed; struct afe_enc_fmt_id_param_t { @@ -3506,6 +3606,11 @@ struct afe_enc_config { union afe_enc_config_data data; }; +struct afe_dec_config { + u32 format; + struct afe_abr_dec_cfg_t abr_dec_cfg; +}; + struct afe_enc_cfg_blk_param_t { uint32_t enc_cfg_blk_size; /* @@ -3538,6 +3643,39 @@ struct avs_enc_set_scrambler_param_t { uint32_t enable_scrambler; }; +/* + * Payload of the AVS_ENCODER_PARAM_ID_BIT_RATE_LEVEL_MAP parameter. + */ +struct afe_enc_level_to_bitrate_map_param_t { + /* + * Parameter for mapping link quality level to bitrate. + */ + struct afe_quality_level_to_bitrate_info mapping_table; +}; + +/* + * Payload of the AVS_ENCDEC_PARAM_ID_DEC_TO_ENC_COMMUNICATION parameter. + */ +struct afe_enc_dec_imc_info_param_t { + /* + * Parameter to set up Inter Module Communication (IMC) between + * AFE Decoder and Encoder. + */ + struct afe_imc_dec_enc_info imc_info; +}; + +/* + * Payload of the AVS_DECODER_PARAM_ID_DEPACKETIZER_ID parameter. + */ +struct avs_dec_depacketizer_id_param_t { + /* + * Supported values: + * #AVS_MODULE_ID_DEPACKETIZER_COP + * Any OpenDSP supported values + */ + uint32_t dec_depacketizer_id; +}; + union afe_port_config { struct afe_param_id_pcm_cfg pcm; struct afe_param_id_i2s_cfg i2s; @@ -3557,6 +3695,9 @@ union afe_port_config { struct afe_enc_cfg_blk_param_t enc_blk_param; struct avs_enc_packetizer_id_param_t enc_pkt_id_param; struct avs_enc_set_scrambler_param_t enc_set_scrambler_param; + struct avs_dec_depacketizer_id_param_t dec_depkt_id_param; + struct afe_enc_level_to_bitrate_map_param_t map_param; + struct afe_enc_dec_imc_info_param_t imc_info_param; } __packed; struct afe_audioif_config_command_no_payload { diff --git a/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h index 6fb9c442ec30..ef20aad7de44 100644 --- a/include/dsp/q6afe-v2.h +++ b/include/dsp/q6afe-v2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -319,7 +319,8 @@ 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); + struct afe_enc_config *enc_config, + struct afe_dec_config *dec_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); From 3eb732a93756161f37174911877245c8c70188e0 Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Thu, 8 Mar 2018 16:28:26 -0800 Subject: [PATCH 176/276] asoc: sdm845: add configuration for adaptive bitrate Add hostless front end DAI to trigger backend configuration of BT backend dai required for configuring BT ABR statistics. Add mixer ctrls to independently configure TX and RX sample rates to allow BT RX and TX backends. Change-Id: Iba274253bca510e3899658306787e2ba15324bf2 Signed-off-by: Aniket Kumar Lata --- asoc/sdm845.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index dd9d371fc36c..a03b2ce7b670 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -407,6 +407,12 @@ static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_44P1", "KHZ_48", "KHZ_88P2", "KHZ_96"}; +static char const *bt_sample_rate_rx_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static char const *bt_sample_rate_tx_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"}; @@ -460,6 +466,8 @@ 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(bt_sample_rate_rx, bt_sample_rate_rx_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_tx, bt_sample_rate_tx_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, @@ -1051,6 +1059,130 @@ static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, return 0; } +static int msm_bt_sample_rate_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + 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_rx_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; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_bt_sample_rate_tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim_tx_cfg[SLIM_TX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + 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_tx_cfg[SLIM_TX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_tx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_tx = %d, value = %d\n", + __func__, + 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) { @@ -2752,6 +2884,12 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, msm_bt_sample_rate_get, msm_bt_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate RX", bt_sample_rate_rx, + msm_bt_sample_rate_rx_get, + msm_bt_sample_rate_rx_put), + SOC_ENUM_EXT("BT SampleRate TX", bt_sample_rate_tx, + msm_bt_sample_rate_tx_get, + msm_bt_sample_rate_tx_put), SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate, usb_audio_rx_sample_rate_get, usb_audio_rx_sample_rate_put), @@ -5466,6 +5604,22 @@ static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", }, + { + .name = "SLIMBUS_7 Hostless", + .stream_name = "SLIMBUS_7 Hostless", + .cpu_dai_name = "SLIMBUS7_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, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, }; static struct snd_soc_dai_link msm_common_be_dai_links[] = { From c92b0f272782e5eb4dde4314b569a588a989d6a9 Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Thu, 8 Mar 2018 17:57:38 -0800 Subject: [PATCH 177/276] uapi: wcd-spi-ac: add interface for client driver The wcd-spi-ac (access control) driver exposes character device to userspace to indicate information regarding WCD SPI usage. Add uapi header file with the commands and data expected by the character driver. Change-Id: I41dfc3181041c7e7a1bad8695fa723d3a965036b Signed-off-by: Bhalchandra Gajare --- include/uapi/linux/Kbuild | 1 + include/uapi/linux/wcd-spi-ac-params.h | 52 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 include/uapi/linux/wcd-spi-ac-params.h diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 7647f052b7f4..77cbbc1ca324 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -17,3 +17,4 @@ 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 += wcd-spi-ac-params.h diff --git a/include/uapi/linux/wcd-spi-ac-params.h b/include/uapi/linux/wcd-spi-ac-params.h new file mode 100644 index 000000000000..e397bf215ab8 --- /dev/null +++ b/include/uapi/linux/wcd-spi-ac-params.h @@ -0,0 +1,52 @@ +#ifndef __UAPI_WCD_SPI_AC_PARAMS_H__ +#define __UAPI_WCD_SPI_AC_PARAMS_H__ + +#include + +#define WCD_SPI_AC_CMD_CONC_BEGIN 0x01 +#define WCD_SPI_AC_CMD_CONC_END 0x02 +#define WCD_SPI_AC_CMD_BUF_DATA 0x03 + +#define WCD_SPI_AC_MAX_BUFFERS 2 +#define WCD_SPI_AC_MAX_CH_PER_BUF 8 + +#define WCD_SPI_AC_CLIENT_CDEV_NAME "wcd-spi-ac-client" +#define WCD_SPI_AC_PROCFS_DIR_NAME "wcd-spi-ac" +#define WCD_SPI_AC_PROCFS_STATE_NAME "svc-state" + +/* + * wcd_spi_ac_buf_data: + * Buffer address for one buffer. Should have data + * for all the channels. If channels are unused, the + * value must be NULL. + * + * @addr: + * Address where each channel of the buffer starts. + */ +struct wcd_spi_ac_buf_data { + __u32 addr[WCD_SPI_AC_MAX_CH_PER_BUF]; +} __packed; + +/* + * wcd_spi_ac_write_cmd: + * Data sent to the driver's write interface should + * be packed in this format. + * + * @cmd_type: + * Indicates the type of command that is sent. Should + * be one of the valid commands defined with + * WCD_SPI_AC_CMD_* + * @payload: + * No payload for: + * WCD_SPI_AC_CMD_CONC_BEGIN + * WCD_SPI_AC_CMD_CONC_END + * Upto WCD_SPI_AC_MAX_BUFFERS of type + * struct wcd_spi_ac_buf_data for: + * WCD_SPI_AC_CMD_BUF_DATA + */ +struct wcd_spi_ac_write_cmd { + __u32 cmd_type; + __u8 payload[0]; +} __packed; + +#endif /* end of __UAPI_WCD_SPI_AC_PARAMS_H__ */ From 25ed8a8ae925efd68472d9fb98e299832f409c3c Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Fri, 9 Mar 2018 17:22:33 -0800 Subject: [PATCH 178/276] soc: wcd-spi-ac: add wcd spi access control driver Add driver to perform WCD SPI bus arbitration between two masters. The two masters could reside on two different processors and QMI is used to arbitrate the bus access. This driver also exposes character driver interface to userspace to indicate use case start/stop, etc. Change-Id: I4f6fe6bb0bca524f10a34cf89149c6b2055b00e1 Signed-off-by: Bhalchandra Gajare --- include/soc/wcd-spi-ac.h | 44 ++ soc/Makefile | 3 + soc/wcd-spi-ac.c | 1000 ++++++++++++++++++++++++++++++++++++++ soc/wcd_spi_ctl_v01.c | 138 ++++++ soc/wcd_spi_ctl_v01.h | 71 +++ 5 files changed, 1256 insertions(+) create mode 100644 include/soc/wcd-spi-ac.h create mode 100644 soc/wcd-spi-ac.c create mode 100644 soc/wcd_spi_ctl_v01.c create mode 100644 soc/wcd_spi_ctl_v01.h diff --git a/include/soc/wcd-spi-ac.h b/include/soc/wcd-spi-ac.h new file mode 100644 index 000000000000..59fe2171fe59 --- /dev/null +++ b/include/soc/wcd-spi-ac.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2018, 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 __WCD_SPI_AC_H__ +#define __WCD_SPI_AC_H__ + +#include +#include + +enum wcd_spi_acc_req { + WCD_SPI_ACCESS_REQUEST, + WCD_SPI_ACCESS_RELEASE, + WCD_SPI_ACCESS_MAX, +}; + +#define WCD_SPI_AC_DATA_TRANSFER BIT(0) +#define WCD_SPI_AC_CONCURRENCY BIT(1) +#define WCD_SPI_AC_REMOTE_DOWN BIT(2) +#define WCD_SPI_AC_SVC_OFFLINE BIT(3) +#define WCD_SPI_AC_UNINITIALIZED BIT(4) + +#if defined(CONFIG_WCD_SPI_AC) +int wcd_spi_access_ctl(struct device *dev, + enum wcd_spi_acc_req req, + u32 reason); +#else +int wcd_spi_access_ctl(struct device *dev, + enum wcd_spi_acc_req req, + u32 reason) +{ + return 0; +} +#endif /* end of CONFIG_WCD_SPI_AC */ + +#endif /* end of __WCD_SPI_AC_H__ */ diff --git a/soc/Makefile b/soc/Makefile index c563e76ecdce..7a2dadd374d8 100644 --- a/soc/Makefile +++ b/soc/Makefile @@ -4,3 +4,6 @@ obj-$(CONFIG_PINCTRL_WCD) += pinctrl-wcd.o obj-$(CONFIG_PINCTRL_LPI) += pinctrl-lpi.o obj-$(CONFIG_SOUNDWIRE) += soundwire.o obj-$(CONFIG_SOUNDWIRE_WCD_CTRL) += swr-wcd-ctrl.o + +wcd-spi-acc-ctl-objs += wcd-spi-ac.o wcd_spi_ctl_v01.o +obj-$(CONFIG_WCD_SPI_AC) += wcd-spi-acc-ctl.o diff --git a/soc/wcd-spi-ac.c b/soc/wcd-spi-ac.c new file mode 100644 index 000000000000..4eeeaedb5774 --- /dev/null +++ b/soc/wcd-spi-ac.c @@ -0,0 +1,1000 @@ +/* Copyright (c) 2018, 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 "wcd_spi_ctl_v01.h" + +#define WCD_SPI_AC_PFS_ENTRY_MAX_LEN 16 +#define WCD_SPI_AC_WRITE_CMD_MIN_SIZE \ + (sizeof(struct wcd_spi_ac_write_cmd)) +#define WCD_SPI_AC_WRITE_CMD_MAX_SIZE \ + (WCD_SPI_AC_WRITE_CMD_MIN_SIZE + \ + (WCD_SPI_AC_MAX_BUFFERS * \ + sizeof(struct wcd_spi_ac_buf_data))) + +#define WCD_SPI_AC_MUTEX_LOCK(dev, lock) \ +{ \ + dev_dbg(dev, "%s: mutex_lock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_lock(&lock); \ +} + +#define WCD_SPI_AC_MUTEX_UNLOCK(dev, lock) \ +{ \ + dev_dbg(dev, "%s: mutex_unlock(%s)\n", \ + __func__, __stringify_1(lock)); \ + mutex_unlock(&lock); \ +} + +/* + * All bits of status should be cleared for SPI access + * to be released. + */ +#define WCD_SPI_AC_STATUS_RELEASE_ACCESS 0x00 +#define WCD_SPI_AC_LOCAL_ACCESS 0x00 +#define WCD_SPI_AC_REMOTE_ACCESS 0x01 +#define WCD_SPI_CTL_INS_ID 0 +#define WCD_SPI_AC_QMI_TIMEOUT_MS 100 + +struct wcd_spi_ac_priv { + + /* Pointer to device for this driver */ + struct device *dev; + + /* Pointer to parent's device */ + struct device *parent; + + /* char dev related */ + struct class *cls; + struct device *chardev; + struct cdev cdev; + dev_t cdev_num; + + /* proc entry related */ + struct proc_dir_entry *pfs_root; + struct proc_dir_entry *pfs_status; + + /* service status related */ + u8 svc_offline; + u8 svc_offline_change; + wait_queue_head_t svc_poll_wait; + struct mutex status_lock; + + /* state maintenence related */ + u32 state; + struct mutex state_lock; + u8 current_access; + + /* qmi related */ + struct qmi_handle *qmi_hdl; + struct work_struct svc_arr_work; + struct work_struct svc_exit_work; + struct notifier_block nb; + struct mutex svc_lock; + struct workqueue_struct *qmi_wq; + struct work_struct recv_msg_work; +}; + + +static void wcd_spi_ac_status_change(struct wcd_spi_ac_priv *ac, + u8 online) +{ + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock); + ac->svc_offline = !online; + /* Make sure the write is complete */ + wmb(); + xchg(&ac->svc_offline_change, 1); + wake_up_interruptible(&ac->svc_poll_wait); + dev_dbg(ac->dev, + "%s request %u offline %u off_change %u\n", + __func__, online, ac->svc_offline, + ac->svc_offline_change); + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock); +} + +static int wcd_spi_ac_status_open(struct inode *inode, + struct file *file) +{ + struct wcd_spi_ac_priv *ac = PDE_DATA(inode); + + file->private_data = ac; + + return 0; +} + +static ssize_t wcd_spi_ac_status_read(struct file *file, + char __user *buffer, + size_t count, loff_t *offset) +{ + struct wcd_spi_ac_priv *ac; + char buf[WCD_SPI_AC_PFS_ENTRY_MAX_LEN]; + int len, ret; + u8 offline; + + ac = (struct wcd_spi_ac_priv *) file->private_data; + if (!ac) { + pr_err("%s: Invalid private data for status\n", + __func__); + return -EINVAL; + } + + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock); + offline = ac->svc_offline; + /* Make sure the read is complete */ + rmb(); + dev_dbg(ac->dev, "%s: offline = %sline\n", + __func__, offline ? "off" : "on"); + len = snprintf(buf, sizeof(buf), "%s\n", + offline ? "OFFLINE" : "ONLINE"); + ret = simple_read_from_buffer(buffer, count, offset, buf, len); + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock); + + return ret; +} + +static unsigned int wcd_spi_ac_status_poll(struct file *file, + poll_table *wait) +{ + struct wcd_spi_ac_priv *ac; + unsigned int ret = 0; + + ac = (struct wcd_spi_ac_priv *) file->private_data; + if (!ac) { + pr_err("%s: Invalid private data for status\n", + __func__); + return -EINVAL; + } + + dev_dbg(ac->dev, "%s: Poll wait, svc = %s\n", + __func__, ac->svc_offline ? "offline" : "online"); + poll_wait(file, &ac->svc_poll_wait, wait); + dev_dbg(ac->dev, "%s: Woken up Poll wait, svc = %s\n", + __func__, ac->svc_offline ? "offline" : "online"); + + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock); + if (xchg(&ac->svc_offline_change, 0)) + ret = POLLIN | POLLPRI | POLLRDNORM; + dev_dbg(ac->dev, "%s: ret (%d) from poll_wait\n", + __func__, ret); + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock); + + return ret; +} + +static const struct file_operations wcd_spi_ac_status_ops = { + .owner = THIS_MODULE, + .open = wcd_spi_ac_status_open, + .read = wcd_spi_ac_status_read, + .poll = wcd_spi_ac_status_poll, +}; + +static int wcd_spi_ac_procfs_init(struct wcd_spi_ac_priv *ac) +{ + int ret = 0; + + ac->pfs_root = proc_mkdir(WCD_SPI_AC_PROCFS_DIR_NAME, NULL); + if (!ac->pfs_root) { + dev_err(ac->dev, "%s: proc_mkdir failed\n", __func__); + return -EINVAL; + } + + ac->pfs_status = proc_create_data(WCD_SPI_AC_PROCFS_STATE_NAME, + 0444, ac->pfs_root, + &wcd_spi_ac_status_ops, + ac); + if (!ac->pfs_status) { + dev_err(ac->dev, "%s: proc_create_data failed\n", + __func__); + ret = -EINVAL; + goto rmdir_root; + } + + proc_set_size(ac->pfs_status, WCD_SPI_AC_PFS_ENTRY_MAX_LEN); + + return 0; + +rmdir_root: + proc_remove(ac->pfs_root); + return ret; + +} + +static void wcd_spi_ac_procfs_deinit(struct wcd_spi_ac_priv *ac) +{ + proc_remove(ac->pfs_status); + proc_remove(ac->pfs_root); +} + +static int wcd_spi_ac_request_access(struct wcd_spi_ac_priv *ac, + bool is_svc_locked) +{ + struct wcd_spi_req_access_msg_v01 req; + struct wcd_spi_req_access_resp_v01 rsp; + struct msg_desc req_desc, rsp_desc; + int ret = 0; + + dev_dbg(ac->dev, "%s: is_svc_locked = %s\n", + __func__, is_svc_locked ? "true" : "false"); + + memset(&req, 0, sizeof(req)); + memset(&rsp, 0, sizeof(rsp)); + + req.reason_valid = 1; + req.reason = ac->state & 0x03; + + req_desc.max_msg_len = WCD_SPI_REQ_ACCESS_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = WCD_SPI_REQ_ACCESS_MSG_V01; + req_desc.ei_array = wcd_spi_req_access_msg_v01_ei; + + rsp_desc.max_msg_len = WCD_SPI_REQ_ACCESS_RESP_V01_MAX_MSG_LEN; + rsp_desc.msg_id = WCD_SPI_REQ_ACCESS_RESP_V01; + rsp_desc.ei_array = wcd_spi_req_access_resp_v01_ei; + + if (!is_svc_locked) + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock); + + ret = qmi_send_req_wait(ac->qmi_hdl, + &req_desc, &req, sizeof(req), + &rsp_desc, &rsp, sizeof(rsp), + WCD_SPI_AC_QMI_TIMEOUT_MS); + if (ret) { + dev_err(ac->dev, "%s: msg send failed %d\n", + __func__, ret); + goto done; + } + + if (rsp.resp.result != QMI_RESULT_SUCCESS_V01) { + ret = -EIO; + dev_err(ac->dev, "%s: qmi resp error %d\n", + __func__, rsp.resp.result); + } +done: + if (!is_svc_locked) + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock); + + return ret; +} + +static int wcd_spi_ac_release_access(struct wcd_spi_ac_priv *ac, + bool is_svc_locked) +{ + struct wcd_spi_rel_access_msg_v01 req; + struct wcd_spi_rel_access_resp_v01 rsp; + struct msg_desc req_desc, rsp_desc; + int ret = 0; + + dev_dbg(ac->dev, "%s: is_svc_locked = %s\n", + __func__, is_svc_locked ? "true" : "false"); + + memset(&req, 0, sizeof(req)); + memset(&rsp, 0, sizeof(rsp)); + + req_desc.max_msg_len = WCD_SPI_REL_ACCESS_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = WCD_SPI_REL_ACCESS_MSG_V01; + req_desc.ei_array = wcd_spi_rel_access_msg_v01_ei; + + rsp_desc.max_msg_len = WCD_SPI_REL_ACCESS_RESP_V01_MAX_MSG_LEN; + rsp_desc.msg_id = WCD_SPI_REL_ACCESS_RESP_V01; + rsp_desc.ei_array = wcd_spi_rel_access_resp_v01_ei; + + if (!is_svc_locked) + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock); + + ret = qmi_send_req_wait(ac->qmi_hdl, + &req_desc, &req, sizeof(req), + &rsp_desc, &rsp, sizeof(rsp), + WCD_SPI_AC_QMI_TIMEOUT_MS); + if (ret) { + dev_err(ac->dev, "%s: msg send failed %d\n", + __func__, ret); + goto done; + } + + if (rsp.resp.result != QMI_RESULT_SUCCESS_V01) { + ret = -EIO; + dev_err(ac->dev, "%s: qmi resp error %d\n", + __func__, rsp.resp.result); + } +done: + if (!is_svc_locked) + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock); + return ret; +} + +static int wcd_spi_ac_buf_msg( + struct wcd_spi_ac_priv *ac, + u8 *data, int data_sz) +{ + struct wcd_spi_ac_buf_data *buf_data; + struct wcd_spi_buff_msg_v01 req; + struct wcd_spi_buff_resp_v01 rsp; + struct msg_desc req_desc, rsp_desc; + int ret = 0; + + memset(&req, 0, sizeof(req)); + memset(&rsp, 0, sizeof(rsp)); + + buf_data = (struct wcd_spi_ac_buf_data *) data; + memcpy(req.buff_addr_1, buf_data, + sizeof(*buf_data)); + + if (data_sz - sizeof(*buf_data) != 0) { + req.buff_addr_2_valid = 1; + buf_data++; + memcpy(req.buff_addr_2, buf_data, + sizeof(*buf_data)); + } + + req_desc.max_msg_len = WCD_SPI_BUFF_MSG_V01_MAX_MSG_LEN; + req_desc.msg_id = WCD_SPI_BUFF_MSG_V01; + req_desc.ei_array = wcd_spi_buff_msg_v01_ei; + + rsp_desc.max_msg_len = WCD_SPI_BUFF_RESP_V01_MAX_MSG_LEN; + rsp_desc.msg_id = WCD_SPI_BUFF_RESP_V01; + rsp_desc.ei_array = wcd_spi_buff_resp_v01_ei; + + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock); + ret = qmi_send_req_wait(ac->qmi_hdl, + &req_desc, &req, sizeof(req), + &rsp_desc, &rsp, sizeof(rsp), + WCD_SPI_AC_QMI_TIMEOUT_MS); + + if (ret) { + dev_err(ac->dev, "%s: msg send failed %d\n", + __func__, ret); + goto done; + } + + if (rsp.resp.result != QMI_RESULT_SUCCESS_V01) { + ret = -EIO; + dev_err(ac->dev, "%s: qmi resp error %d\n", + __func__, rsp.resp.result); + } +done: + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock); + return ret; + +} + +/* + * wcd_spi_ac_set_sync: Sets the current status of the SPI + * bus and requests access if not + * already accesible. + * @ac: pointer to the drivers private data + * @value: value to be set in the status mask + * @is_svc_locked: flag to indicate if svc_lock is acquired by caller + */ +static int wcd_spi_ac_set_sync(struct wcd_spi_ac_priv *ac, + u32 value, bool is_svc_locked) +{ + int ret = 0; + + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->state_lock); + ac->state |= value; + /* any non-zero state indicates us to request SPI access */ + wmb(); + dev_dbg(ac->dev, "%s: current state = 0x%x, current access 0x%x\n", + __func__, ac->state, ac->current_access); + if (ac->current_access == WCD_SPI_AC_REMOTE_ACCESS) { + dev_dbg(ac->dev, + "%s: requesting access, state = 0x%x\n", + __func__, ac->state); + ret = wcd_spi_ac_request_access(ac, is_svc_locked); + if (!ret) + ac->current_access = WCD_SPI_AC_LOCAL_ACCESS; + } + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->state_lock); + + return ret; +} + +/* + * wcd_spi_ac_clear_sync: Clears the current status of the SPI + * bus and releases access if applicable + * @ac: pointer to the drivers private data + * @value: value to be cleared in the status mask + * @is_svc_locked: flag to indicate if svc_lock is acquired by caller + */ +static int wcd_spi_ac_clear_sync(struct wcd_spi_ac_priv *ac, + u32 value, bool is_svc_locked) +{ + int ret = 0; + + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->state_lock); + ac->state &= ~(value); + /* make sure value is written before read */ + wmb(); + dev_dbg(ac->dev, "%s: current state = 0x%x, current access 0x%x\n", + __func__, ac->state, ac->current_access); + /* state should be zero to release SPI access */ + if (!ac->state && + ac->current_access == WCD_SPI_AC_LOCAL_ACCESS) { + dev_dbg(ac->dev, + "%s: releasing access, state = 0x%x\n", + __func__, ac->state); + ret = wcd_spi_ac_release_access(ac, is_svc_locked); + if (!ret) + ac->current_access = WCD_SPI_AC_REMOTE_ACCESS; + } + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->state_lock); + + return ret; + +} + +/* + * wcd_spi_access_ctl: API to request/release the access + * to wcd-spi bus. + * @dev: handle to the wcd-spi-ac device + * @request: enum to indicate access request or access release + * @reason: reason for request/release. Must be one of the + * valid reasons. + * Returns success if the access handover was sucessful, + * negative error code otherwise. + */ +int wcd_spi_access_ctl(struct device *dev, + enum wcd_spi_acc_req request, + u32 reason) +{ + struct wcd_spi_ac_priv *ac; + int ret = 0; + + if (!dev) { + pr_err("%s: invalid device\n", __func__); + return -EINVAL; + } + + /* only data_transfer and remote_down are valid reasons */ + if (reason != WCD_SPI_AC_DATA_TRANSFER && + reason != WCD_SPI_AC_REMOTE_DOWN) { + pr_err("%s: Invalid reason 0x%x\n", + __func__, reason); + return -EINVAL; + } + + ac = (struct wcd_spi_ac_priv *) dev_get_drvdata(dev); + if (!ac) { + dev_err(dev, "%s: invalid driver data\n", __func__); + return -EINVAL; + } + + dev_dbg(dev, "%s: request = 0x%x, reason = 0x%x\n", + __func__, request, reason); + + switch (request) { + case WCD_SPI_ACCESS_REQUEST: + ret = wcd_spi_ac_set_sync(ac, reason, false); + if (ret) + dev_err(dev, "%s: set_sync(0x%x) failed %d\n", + __func__, reason, ret); + break; + case WCD_SPI_ACCESS_RELEASE: + ret = wcd_spi_ac_clear_sync(ac, reason, false); + if (ret) + dev_err(dev, "%s: clear_sync(0x%x) failed %d\n", + __func__, reason, ret); + break; + default: + dev_err(dev, "%s: invalid request 0x%x\n", + __func__, request); + break; + } + + return ret; +} +EXPORT_SYMBOL(wcd_spi_access_ctl); + +static int wcd_spi_ac_cdev_open(struct inode *inode, + struct file *file) +{ + struct wcd_spi_ac_priv *ac; + int ret = 0; + + ac = container_of(inode->i_cdev, struct wcd_spi_ac_priv, cdev); + if (!ac) { + pr_err("%s: Invalid private data\n", __func__); + return -EINVAL; + } + + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->status_lock); + if (ac->svc_offline) { + dev_err(ac->dev, "%s: SVC is not online, cannot open driver\n", + __func__); + ret = -ENODEV; + goto done; + } + + file->private_data = ac; + +done: + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->status_lock); + return ret; +} + +static ssize_t wcd_spi_ac_cdev_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + struct wcd_spi_ac_priv *ac; + struct wcd_spi_ac_write_cmd *cmd_buf; + int ret = 0; + int data_sz; + + ac = (struct wcd_spi_ac_priv *) file->private_data; + if (!ac) { + pr_err("%s: Invalid private data\n", __func__); + return -EINVAL; + } + + if (count < WCD_SPI_AC_WRITE_CMD_MIN_SIZE || + count > WCD_SPI_AC_WRITE_CMD_MAX_SIZE) { + dev_err(ac->dev, "%s: Invalid write count %zd\n", + __func__, count); + return -EINVAL; + } + + cmd_buf = kzalloc(count, GFP_KERNEL); + if (!cmd_buf) + return -ENOMEM; + + if (get_user(cmd_buf->cmd_type, buf)) { + dev_err(ac->dev, "%s: get_user failed\n", __func__); + ret = -EFAULT; + goto free_cmd_buf; + } + + dev_dbg(ac->dev, "%s: write cmd type 0x%x\n", + __func__, cmd_buf->cmd_type); + + switch (cmd_buf->cmd_type) { + + case WCD_SPI_AC_CMD_CONC_BEGIN: + ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_CONCURRENCY, false); + if (ret) { + dev_err(ac->dev, "%s: set_sync(CONC) fail %d\n", + __func__, ret); + goto free_cmd_buf; + } + + break; + + case WCD_SPI_AC_CMD_CONC_END: + ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_CONCURRENCY, false); + if (ret) { + dev_err(ac->dev, "%s: clear_sync(CONC) fail %d\n", + __func__, ret); + goto free_cmd_buf; + } + + break; + + case WCD_SPI_AC_CMD_BUF_DATA: + + /* Read the buffer details and send to service */ + data_sz = count - sizeof(cmd_buf->cmd_type); + + if (!data_sz || + (data_sz % sizeof(struct wcd_spi_ac_buf_data))) { + dev_err(ac->dev, "%s: size %d not multiple of %ld\n", + __func__, data_sz, + sizeof(struct wcd_spi_ac_buf_data)); + goto free_cmd_buf; + } + + if (data_sz / sizeof(struct wcd_spi_ac_buf_data) > + WCD_SPI_AC_MAX_BUFFERS) { + dev_err(ac->dev, "%s: invalid size %d\n", + __func__, data_sz); + goto free_cmd_buf; + } + + if (copy_from_user(cmd_buf->payload, + buf + sizeof(cmd_buf->cmd_type), + data_sz)) { + dev_err(ac->dev, "%s: copy_from_user failed\n", + __func__); + ret = -EFAULT; + goto free_cmd_buf; + } + + ret = wcd_spi_ac_buf_msg(ac, cmd_buf->payload, data_sz); + if (ret) { + dev_err(ac->dev, "%s: _buf_msg failed %d\n", + __func__, ret); + goto free_cmd_buf; + } + + ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_UNINITIALIZED, + false); + if (ret) { + dev_err(ac->dev, "%s: clear_sync 0x%lx failed %d\n", + __func__, WCD_SPI_AC_UNINITIALIZED, ret); + goto free_cmd_buf; + } + break; + default: + dev_err(ac->dev, "%s: Invalid cmd_type 0x%x\n", + __func__, cmd_buf->cmd_type); + ret = -EINVAL; + goto free_cmd_buf; + } + +free_cmd_buf: + + kfree(cmd_buf); + if (!ret) + ret = count; + + return ret; +} + +static int wcd_spi_ac_cdev_release(struct inode *inode, + struct file *file) +{ + struct wcd_spi_ac_priv *ac; + int ret = 0; + + ac = (struct wcd_spi_ac_priv *) file->private_data; + if (!ac) { + pr_err("%s: Invalid private data\n", __func__); + return -EINVAL; + } + + ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_UNINITIALIZED, false); + if (ret) + dev_err(ac->dev, "%s: set_sync(UNINITIALIZED) failed %d\n", + __func__, ret); + return ret; +} + +static const struct file_operations wcd_spi_ac_cdev_fops = { + .owner = THIS_MODULE, + .open = wcd_spi_ac_cdev_open, + .write = wcd_spi_ac_cdev_write, + .release = wcd_spi_ac_cdev_release, +}; + +static int wcd_spi_ac_reg_chardev(struct wcd_spi_ac_priv *ac) +{ + int ret; + + ret = alloc_chrdev_region(&ac->cdev_num, 0, 1, + WCD_SPI_AC_CLIENT_CDEV_NAME); + if (ret) { + dev_err(ac->dev, "%s: alloc_chrdev_region failed %d\n", + __func__, ret); + return ret; + } + + ac->cls = class_create(THIS_MODULE, WCD_SPI_AC_CLIENT_CDEV_NAME); + if (IS_ERR(ac->cls)) { + ret = PTR_ERR(ac->cls); + dev_err(ac->dev, "%s: class_create failed %d\n", + __func__, ret); + goto unregister_chrdev; + } + + ac->chardev = device_create(ac->cls, NULL, ac->cdev_num, + NULL, WCD_SPI_AC_CLIENT_CDEV_NAME); + if (IS_ERR(ac->chardev)) { + ret = PTR_ERR(ac->chardev); + dev_err(ac->dev, "%s: device_create failed %d\n", + __func__, ret); + goto destroy_class; + } + + cdev_init(&ac->cdev, &wcd_spi_ac_cdev_fops); + ret = cdev_add(&ac->cdev, ac->cdev_num, 1); + if (ret) { + dev_err(ac->dev, "%s: cdev_add failed %d\n", + __func__, ret); + goto destroy_device; + } + + return 0; + +destroy_device: + device_destroy(ac->cls, ac->cdev_num); + +destroy_class: + class_destroy(ac->cls); + +unregister_chrdev: + unregister_chrdev_region(0, 1); + return ret; +} + +static int wcd_spi_ac_unreg_chardev(struct wcd_spi_ac_priv *ac) +{ + cdev_del(&ac->cdev); + device_destroy(ac->cls, ac->cdev_num); + class_destroy(ac->cls); + unregister_chrdev_region(0, 1); + + return 0; +} + +static void wcd_spi_ac_recv_msg(struct work_struct *work) +{ + struct wcd_spi_ac_priv *ac; + int rc = 0; + + ac = container_of(work, struct wcd_spi_ac_priv, + recv_msg_work); + if (!ac) { + pr_err("%s: Invalid private data\n", __func__); + return; + } + + do { + dev_dbg(ac->dev, "%s: msg received, rc = %d\n", + __func__, rc); + } while ((rc = qmi_recv_msg(ac->qmi_hdl)) == 0); + + if (rc != -ENOMSG) + dev_err(ac->dev, "%s: qmi_recv_msg failed %d\n", + __func__, rc); +} + +static void wcd_spi_ac_clnt_notify(struct qmi_handle *hdl, + enum qmi_event_type event, void *priv_data) +{ + struct wcd_spi_ac_priv *ac; + + if (!priv_data) { + pr_err("%s: Invalid private data\n", __func__); + return; + } + + ac = (struct wcd_spi_ac_priv *) priv_data; + + switch (event) { + case QMI_RECV_MSG: + queue_work(ac->qmi_wq, &ac->recv_msg_work); + break; + default: + break; + } +} + +static void wcd_spi_ac_svc_arrive(struct work_struct *work) +{ + struct wcd_spi_ac_priv *ac; + int ret; + + ac = container_of(work, struct wcd_spi_ac_priv, + svc_arr_work); + if (!ac) { + pr_err("%s: Invalid private data\n", + __func__); + return; + } + + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock); + ac->qmi_hdl = qmi_handle_create(wcd_spi_ac_clnt_notify, + ac); + if (!ac->qmi_hdl) { + dev_err(ac->dev, "%s: qmi_handle_create failed\n", + __func__); + goto done; + } + + ret = qmi_connect_to_service(ac->qmi_hdl, + WCD_SPI_CTL_SERVICE_ID_V01, + WCD_SPI_CTL_SERVICE_VERS_V01, + WCD_SPI_CTL_INS_ID); + if (ret) { + dev_err(ac->dev, "%s, cant connect to service, error %d\n", + __func__, ret); + qmi_handle_destroy(ac->qmi_hdl); + ac->qmi_hdl = NULL; + goto done; + } + + /* Mark service as online */ + wcd_spi_ac_status_change(ac, 1); + + /* + * update the state and clear the WCD_SPI_AC_SVC_OFFLINE + * bit to indicate that the service is now online. + */ + ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_SVC_OFFLINE, true); + if (ret) + dev_err(ac->dev, "%s: clear_sync(SVC_OFFLINE) failed %d\n", + __func__, ret); +done: + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock); + +} + +static void wcd_spi_ac_svc_exit(struct work_struct *work) +{ + struct wcd_spi_ac_priv *ac; + int ret = 0; + + ac = container_of(work, struct wcd_spi_ac_priv, + svc_exit_work); + if (!ac) { + pr_err("%s: Invalid private data\n", + __func__); + return; + } + + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock); + ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_SVC_OFFLINE, true); + if (ret) + dev_err(ac->dev, "%s: set_sync(SVC_OFFLINE) failed %d\n", + __func__, ret); + qmi_handle_destroy(ac->qmi_hdl); + ac->qmi_hdl = NULL; + wcd_spi_ac_status_change(ac, 0); + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock); +} + +static int wcd_spi_ac_svc_event(struct notifier_block *this, + unsigned long event, + void *data) +{ + struct wcd_spi_ac_priv *ac; + + ac = container_of(this, struct wcd_spi_ac_priv, nb); + if (!ac) { + pr_err("%s: Invalid private data\n", __func__); + return -EINVAL; + } + + dev_dbg(ac->dev, "%s: event = 0x%lx", __func__, event); + + switch (event) { + case QMI_SERVER_ARRIVE: + schedule_work(&ac->svc_arr_work); + break; + case QMI_SERVER_EXIT: + schedule_work(&ac->svc_exit_work); + break; + default: + dev_err(ac->dev, "%s unhandled event %ld\n", + __func__, event); + break; + } + + return 0; +} + +static int wcd_spi_ac_probe(struct platform_device *pdev) +{ + struct wcd_spi_ac_priv *ac; + struct device *parent = pdev->dev.parent; + int ret = 0; + + ac = devm_kzalloc(&pdev->dev, sizeof(*ac), + GFP_KERNEL); + if (!ac) + return -ENOMEM; + + ac->dev = &pdev->dev; + ac->parent = parent; + + ret = wcd_spi_ac_reg_chardev(ac); + if (ret) + return ret; + + ret = wcd_spi_ac_procfs_init(ac); + if (ret) + goto unreg_chardev; + + mutex_init(&ac->status_lock); + mutex_init(&ac->state_lock); + mutex_init(&ac->svc_lock); + init_waitqueue_head(&ac->svc_poll_wait); + ac->svc_offline = 1; + ac->state = (WCD_SPI_AC_SVC_OFFLINE | + WCD_SPI_AC_UNINITIALIZED); + ac->current_access = WCD_SPI_AC_LOCAL_ACCESS; + + ac->nb.notifier_call = wcd_spi_ac_svc_event; + INIT_WORK(&ac->svc_arr_work, wcd_spi_ac_svc_arrive); + INIT_WORK(&ac->svc_exit_work, wcd_spi_ac_svc_exit); + INIT_WORK(&ac->recv_msg_work, wcd_spi_ac_recv_msg); + + ac->qmi_wq = create_singlethread_workqueue("qmi_wq"); + if (!ac->qmi_wq) { + dev_err(&pdev->dev, + "%s: create_singlethread_workqueue failed\n", + __func__); + goto deinit_procfs; + } + + dev_set_drvdata(&pdev->dev, ac); + + ret = qmi_svc_event_notifier_register( + WCD_SPI_CTL_SERVICE_ID_V01, + WCD_SPI_CTL_SERVICE_VERS_V01, + WCD_SPI_CTL_INS_ID, + &ac->nb); + if (ret) { + dev_err(&pdev->dev, + "%s: qmi_svc_event_notifier_register failed %d\n", + __func__, ret); + goto destroy_wq; + } + + + return 0; + +destroy_wq: + destroy_workqueue(ac->qmi_wq); + dev_set_drvdata(&pdev->dev, NULL); +deinit_procfs: + wcd_spi_ac_procfs_deinit(ac); + mutex_destroy(&ac->status_lock); + mutex_destroy(&ac->state_lock); + mutex_destroy(&ac->svc_lock); +unreg_chardev: + wcd_spi_ac_unreg_chardev(ac); + return ret; +} + +static int wcd_spi_ac_remove(struct platform_device *pdev) +{ + struct wcd_spi_ac_priv *ac; + + ac = dev_get_drvdata(&pdev->dev); + qmi_svc_event_notifier_unregister( + WCD_SPI_CTL_SERVICE_ID_V01, + WCD_SPI_CTL_SERVICE_VERS_V01, + WCD_SPI_CTL_INS_ID, + &ac->nb); + if (ac->qmi_wq) + destroy_workqueue(ac->qmi_wq); + wcd_spi_ac_unreg_chardev(ac); + wcd_spi_ac_procfs_deinit(ac); + mutex_destroy(&ac->status_lock); + mutex_destroy(&ac->state_lock); + mutex_destroy(&ac->svc_lock); + + return 0; +} + +static const struct of_device_id wcd_spi_ac_of_match[] = { + { .compatible = "qcom,wcd-spi-ac" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, wcd_spi_ac_of_match); + +static struct platform_driver wcd_spi_ac_driver = { + .driver = { + .name = "qcom,wcd-spi-ac", + .of_match_table = wcd_spi_ac_of_match, + }, + .probe = wcd_spi_ac_probe, + .remove = wcd_spi_ac_remove, +}; + +module_platform_driver(wcd_spi_ac_driver); + +MODULE_DESCRIPTION("WCD SPI access control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/soc/wcd_spi_ctl_v01.c b/soc/wcd_spi_ctl_v01.c new file mode 100644 index 000000000000..d5661e6dabe7 --- /dev/null +++ b/soc/wcd_spi_ctl_v01.c @@ -0,0 +1,138 @@ +/* Copyright (c) 2018, 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 "wcd_spi_ctl_v01.h" + +struct elem_info wcd_spi_req_access_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wcd_spi_req_access_msg_v01, + reason_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wcd_spi_req_access_msg_v01, + reason), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wcd_spi_req_access_resp_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wcd_spi_req_access_resp_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wcd_spi_rel_access_msg_v01_ei[] = { + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wcd_spi_rel_access_resp_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wcd_spi_rel_access_resp_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wcd_spi_buff_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = WCD_SPI_BUFF_CHANNELS_MAX_V01, + .elem_size = sizeof(u32), + .is_array = STATIC_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct wcd_spi_buff_msg_v01, + buff_addr_1), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wcd_spi_buff_msg_v01, + buff_addr_2_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = WCD_SPI_BUFF_CHANNELS_MAX_V01, + .elem_size = sizeof(u32), + .is_array = STATIC_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wcd_spi_buff_msg_v01, + buff_addr_2), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wcd_spi_buff_resp_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wcd_spi_buff_resp_v01, + resp), + .ei_array = get_qmi_response_type_v01_ei(), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; diff --git a/soc/wcd_spi_ctl_v01.h b/soc/wcd_spi_ctl_v01.h new file mode 100644 index 000000000000..ac411a34b413 --- /dev/null +++ b/soc/wcd_spi_ctl_v01.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2018, 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 WCD_SPI_CTL_V01_H +#define WCD_SPI_CTL_V01_H + +#define WCD_SPI_CTL_SERVICE_ID_V01 0x421 +#define WCD_SPI_CTL_SERVICE_VERS_V01 0x01 + +#define WCD_SPI_BUFF_RESP_V01 0x0022 +#define WCD_SPI_REL_ACCESS_RESP_V01 0x0021 +#define WCD_SPI_REQ_ACCESS_MSG_V01 0x0020 +#define WCD_SPI_BUFF_MSG_V01 0x0022 +#define WCD_SPI_REL_ACCESS_MSG_V01 0x0021 +#define WCD_SPI_REQ_ACCESS_RESP_V01 0x0020 + +#define WCD_SPI_BUFF_CHANNELS_MAX_V01 0x08 + +#define WCD_SPI_REQ_DATA_TRANSFER_V01 ((u64)0x01ULL) +#define WCD_SPI_REQ_CONCURRENCY_V01 ((u64)0x02ULL) +#define WCD_SPI_REQ_REMOTE_DOWN_V01 ((u64)0x04ULL) + +struct wcd_spi_req_access_msg_v01 { + u8 reason_valid; + u64 reason; +}; +#define WCD_SPI_REQ_ACCESS_MSG_V01_MAX_MSG_LEN 11 +extern struct elem_info wcd_spi_req_access_msg_v01_ei[]; + +struct wcd_spi_req_access_resp_v01 { + struct qmi_response_type_v01 resp; +}; +#define WCD_SPI_REQ_ACCESS_RESP_V01_MAX_MSG_LEN 7 +extern struct elem_info wcd_spi_req_access_resp_v01_ei[]; + +struct wcd_spi_rel_access_msg_v01 { + char placeholder; +}; +#define WCD_SPI_REL_ACCESS_MSG_V01_MAX_MSG_LEN 0 +extern struct elem_info wcd_spi_rel_access_msg_v01_ei[]; + +struct wcd_spi_rel_access_resp_v01 { + struct qmi_response_type_v01 resp; +}; +#define WCD_SPI_REL_ACCESS_RESP_V01_MAX_MSG_LEN 7 +extern struct elem_info wcd_spi_rel_access_resp_v01_ei[]; + +struct wcd_spi_buff_msg_v01 { + u32 buff_addr_1[WCD_SPI_BUFF_CHANNELS_MAX_V01]; + u8 buff_addr_2_valid; + u32 buff_addr_2[WCD_SPI_BUFF_CHANNELS_MAX_V01]; +}; +#define WCD_SPI_BUFF_MSG_V01_MAX_MSG_LEN 70 +extern struct elem_info wcd_spi_buff_msg_v01_ei[]; + +struct wcd_spi_buff_resp_v01 { + struct qmi_response_type_v01 resp; +}; +#define WCD_SPI_BUFF_RESP_V01_MAX_MSG_LEN 7 +extern struct elem_info wcd_spi_buff_resp_v01_ei[]; + +#endif From 85343227722aee5baef9094232879a2a86f1590f Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Tue, 6 Feb 2018 11:16:01 -0800 Subject: [PATCH 179/276] config: sdm845: enable CONFIG_WCD_SPI_AC WCD SPI Access Control driver is needed to arbitrate the WCD SPI bus access across different masters. Enable the same. Change-Id: I6c44d164f121ae6b3f550882f0c246ecf87be693 Signed-off-by: Bhalchandra Gajare --- config/sdm845auto.conf | 1 + config/sdm845autoconf.h | 1 + 2 files changed, 2 insertions(+) diff --git a/config/sdm845auto.conf b/config/sdm845auto.conf index 4c195b43ddc6..77444df5afa4 100644 --- a/config/sdm845auto.conf +++ b/config/sdm845auto.conf @@ -35,3 +35,4 @@ CONFIG_DTS_SRS_TM=y CONFIG_SND_SOC_MSM_STUB=y CONFIG_MSM_AVTIMER=y CONFIG_SND_SOC_MSM_HDMI_CODEC_RX=y +CONFIG_WCD_SPI_AC=y diff --git a/config/sdm845autoconf.h b/config/sdm845autoconf.h index 6ef465a61871..db6f9aacec26 100644 --- a/config/sdm845autoconf.h +++ b/config/sdm845autoconf.h @@ -48,3 +48,4 @@ #define CONFIG_WCD_DSP_GLINK 1 #define CONFIG_MSM_AVTIMER 1 #define CONFIG_SND_SOC_MSM_HDMI_CODEC_RX 1 +#define CONFIG_WCD_SPI_AC 1 From c3ca81569ba1d824e923e9c0ad08def04ed6c3c5 Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Mon, 19 Mar 2018 14:35:42 -0700 Subject: [PATCH 180/276] ASoC: wcd-spi: add SPI bus arbitration logic On some platforms, WCD SPI bus can be shared with other processors (ex: sensor processor). In such cases, there is need for software to arbitrate the bus control. Add functionality to perform SPI bus arbitration. Change-Id: I7df933f55ac5035a55173a04e74b74f7af1f7ece Signed-off-by: Bhalchandra Gajare --- asoc/codecs/wcd-spi.c | 93 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/asoc/codecs/wcd-spi.c b/asoc/codecs/wcd-spi.c index a25c9a6141ab..6b451ff6504e 100644 --- a/asoc/codecs/wcd-spi.c +++ b/asoc/codecs/wcd-spi.c @@ -21,8 +21,10 @@ #include #include #include +#include #include #include +#include #include "wcd-spi-registers.h" /* Byte manipulations */ @@ -160,6 +162,9 @@ struct wcd_spi_priv { /* Buffers to hold memory used for transfers */ void *tx_buf; void *rx_buf; + + /* Handle to child (qmi client) device */ + struct device *ac_dev; }; enum xfer_request { @@ -556,6 +561,19 @@ static int wcd_spi_clk_enable(struct spi_device *spi) int ret; u32 rd_status = 0; + /* Get the SPI access first */ + if (wcd_spi->ac_dev) { + ret = wcd_spi_access_ctl(wcd_spi->ac_dev, + WCD_SPI_ACCESS_REQUEST, + WCD_SPI_AC_DATA_TRANSFER); + if (ret) { + dev_err(&spi->dev, + "%s: Can't get spi access, err = %d\n", + __func__, ret); + return ret; + } + } + ret = wcd_spi_cmd_nop(spi); if (ret < 0) { dev_err(&spi->dev, "%s: NOP1 failed, err = %d\n", @@ -609,6 +627,17 @@ static int wcd_spi_clk_disable(struct spi_device *spi) */ clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask); + /* once the clock is released, SPI access can be released as well */ + if (wcd_spi->ac_dev) { + ret = wcd_spi_access_ctl(wcd_spi->ac_dev, + WCD_SPI_ACCESS_RELEASE, + WCD_SPI_AC_DATA_TRANSFER); + if (ret) + dev_err(&spi->dev, + "%s: SPI access release failed, err = %d\n", + __func__, ret); + } + return ret; } @@ -933,6 +962,18 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data, __func__, event); switch (event) { + case WDSP_EVENT_PRE_SHUTDOWN: + if (wcd_spi->ac_dev) { + ret = wcd_spi_access_ctl(wcd_spi->ac_dev, + WCD_SPI_ACCESS_REQUEST, + WCD_SPI_AC_REMOTE_DOWN); + if (ret) + dev_err(&spi->dev, + "%s: request access failed %d\n", + __func__, ret); + } + break; + case WDSP_EVENT_POST_SHUTDOWN: cancel_delayed_work_sync(&wcd_spi->clk_dwork); WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex); @@ -942,6 +983,18 @@ static int wdsp_spi_event_handler(struct device *dev, void *priv_data, WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex); break; + case WDSP_EVENT_POST_BOOTUP: + if (wcd_spi->ac_dev) { + ret = wcd_spi_access_ctl(wcd_spi->ac_dev, + WCD_SPI_ACCESS_RELEASE, + WCD_SPI_AC_REMOTE_DOWN); + if (ret) + dev_err(&spi->dev, + "%s: release access failed %d\n", + __func__, ret); + } + break; + case WDSP_EVENT_PRE_DLOAD_CODE: case WDSP_EVENT_PRE_DLOAD_DATA: ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE, @@ -1275,10 +1328,50 @@ static struct regmap_config wcd_spi_regmap_cfg = { .readable_reg = wcd_spi_is_readable_reg, }; +static int wcd_spi_add_ac_dev(struct device *dev, + struct device_node *node) +{ + struct spi_device *spi = to_spi_device(dev); + struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi); + struct platform_device *pdev; + int ret = 0; + + pdev = platform_device_alloc("wcd-spi-ac", -1); + if (IS_ERR_OR_NULL(pdev)) { + ret = PTR_ERR(pdev); + dev_err(dev, "%s: pdev alloc failed, ret = %d\n", + __func__, ret); + return ret; + } + + pdev->dev.parent = dev; + pdev->dev.of_node = node; + + ret = platform_device_add(pdev); + if (ret) { + dev_err(dev, "%s: pdev add failed, ret = %d\n", + __func__, ret); + goto dealloc_pdev; + } + + wcd_spi->ac_dev = &pdev->dev; + return 0; + +dealloc_pdev: + platform_device_put(pdev); + return ret; +} + static int wdsp_spi_init(struct device *dev, void *priv_data) { struct spi_device *spi = to_spi_device(dev); int ret; + struct device_node *node; + + for_each_child_of_node(dev->of_node, node) { + if (!strcmp(node->name, "wcd_spi_ac")) + wcd_spi_add_ac_dev(dev, node); + } ret = wcd_spi_init(spi); if (ret < 0) From 75d2072da86ed1278fe1dba9e816e06db051e953 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Tue, 20 Mar 2018 16:01:41 +0800 Subject: [PATCH 181/276] ASoC: codecs: wcd: avoid redundant component unbind Component unbind happens in component_bind_all, if component bind fails. There is no need to call extra component_unbind_all. Change-Id: I4ff2aececc5e10c5c4cfbe71778630c621768349 Signed-off-by: Xiaojun Sang --- asoc/codecs/wcd-dsp-mgr.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wcd-dsp-mgr.c b/asoc/codecs/wcd-dsp-mgr.c index 058a6cf0c23d..50716e4d57c6 100644 --- a/asoc/codecs/wcd-dsp-mgr.c +++ b/asoc/codecs/wcd-dsp-mgr.c @@ -1086,8 +1086,10 @@ static int wdsp_mgr_bind(struct device *dev) dev_info(dev, "%s: create_ramdump_device failed\n", __func__); ret = component_bind_all(dev, wdsp->ops); - if (ret < 0) + if (ret < 0) { WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret); + return ret; + } /* Make sure all components registered ops */ for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) { From 653168096e888dd0443a45ff2c40ee4e7727e713 Mon Sep 17 00:00:00 2001 From: Vaishnavi Kommaraju Date: Wed, 21 Feb 2018 14:58:47 +0530 Subject: [PATCH 182/276] ASoC: wcd934x-dsp-cntl: Limit array size of val Limit size of val to WCD_MISCDEV_CMD_MAX_LEN to avoid stack overflow. CRs-Fixed: 2177167 Change-Id: I5d2b91e92305d6a485b2e8f959036504f0f55b13 Signed-off-by: Vaishnavi Kommaraju --- asoc/codecs/wcd934x/wcd934x-dsp-cntl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c index 98223b3b9dd6..662f484028ee 100644 --- a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -1011,7 +1011,7 @@ static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, { struct wcd_dsp_cntl *cntl = container_of(filep->private_data, struct wcd_dsp_cntl, miscdev); - char val[count]; + char val[WCD_MISCDEV_CMD_MAX_LEN]; bool vote; int ret = 0; From 5c7b3ef4d4a676c287b64dafa0e10e86a6308bad Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Wed, 28 Feb 2018 17:05:09 +0530 Subject: [PATCH 183/276] dsp: fix MFC config param payload alignment issue MFC set param failed in DSP due to payload mismatch and stereo echo reference doesn't work. Channel type parameter in MFC config payload is an array of uint16_t, but it is assumed as an array of uint8_t while copying from device structure. Fix this by copying the channel type one by one instead of using memcpy. CRs-Fixed: 2197468 Change-Id: I4b6959e8db56743ac98da75ddc3aa8f56964b4ca Signed-off-by: Aditya Bavanari --- dsp/q6voice.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/dsp/q6voice.c b/dsp/q6voice.c index 241e3622085c..f50e448421a2 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -4063,6 +4063,7 @@ static int voice_send_cvp_mfc_config_v2(struct voice_data *v) struct cvp_set_mfc_config_cmd_v2 cvp_set_mfc_config_cmd; void *apr_cvp; u16 cvp_handle; + uint8_t ch_idx; struct vss_icommon_param_data_mfc_config_v2_t *cvp_config_param_data = &cvp_set_mfc_config_cmd.cvp_set_mfc_param_v2.param_data; struct vss_param_mfc_config_info_t *mfc_config_info = @@ -4111,9 +4112,15 @@ static int voice_send_cvp_mfc_config_v2(struct voice_data *v) mfc_config_info->num_channels = v->dev_rx.no_of_channels; mfc_config_info->bits_per_sample = 16; mfc_config_info->sample_rate = v->dev_rx.sample_rate; - memcpy(&mfc_config_info->channel_type, - v->dev_rx.channel_mapping, - VSS_NUM_CHANNELS_MAX * sizeof(uint8_t)); + + /* + * Do not use memcpy here as channel_type in mfc_config structure is a + * uint16_t array while channel_mapping array of device is of uint8_t + */ + for (ch_idx = 0; ch_idx < VSS_NUM_CHANNELS_MAX; ch_idx++) { + mfc_config_info->channel_type[ch_idx] = + v->dev_rx.channel_mapping[ch_idx]; + } v->cvp_state = CMD_STATUS_FAIL; v->async_err = 0; From 10d51a430975b4e7a6da9a2f4cc6eb3618749276 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Tue, 27 Mar 2018 17:42:32 +0530 Subject: [PATCH 184/276] dsp: compile avtimer for targets required AVTIMER compiles based on kernel config in Makefile. Kernel checker fails for 8953 which is on 4.9 kernel but respective conf files not present on audio-drivers 1.0. Compile avtimer driver based on ARCH config check. Change-Id: I2eccd47c0c526880081aed5f437d0f38f4637a41 Signed-off-by: Laxminath Kasam --- dsp/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dsp/Makefile b/dsp/Makefile index b609df228abe..d6ba8621d731 100644 --- a/dsp/Makefile +++ b/dsp/Makefile @@ -6,5 +6,8 @@ obj-$(CONFIG_MSM_QDSP6_SSR) += audio_ssr.o obj-$(CONFIG_MSM_QDSP6_PDR) += audio_pdr.o obj-$(CONFIG_MSM_QDSP6_NOTIFIER) += audio_notifier.o obj-$(CONFIG_MSM_ULTRASOUND) += usf.o usfcdev.o q6usm.o + +ifeq ($(CONFIG_ARCH_SDM845), y) obj-$(CONFIG_MSM_AVTIMER) += avtimer.o +endif obj-$(CONFIG_MSM_QDSP6V2_CODECS) += codecs/ From af2c052a20573de9642f33c1d42485897f65d6ce Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Tue, 27 Mar 2018 18:45:49 -0700 Subject: [PATCH 185/276] ASoC: wcd934x: enable MAD fs_cntr by default Currently, the fs_cntr for MAD is enabled whenever the MAD audio path is enabled. But, a new issue is reported where MAD fs_cntr is required even for some of the non-MAD paths. As per hardware requirements, enable the MAD fs_cntr always from the codec default registers. Change-Id: I6fe8f663b82e711b4194aa51a3e21150aa98cf02 Signed-off-by: Bhalchandra Gajare --- asoc/codecs/wcd934x/wcd934x.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 0662304e5e31..463f7f2b8ca4 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -3141,8 +3141,6 @@ static int __tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable) /* Undo reset for MAD */ snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, 0x02, 0x00); - snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, - 0x04, 0x04); } else { snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, 0x03, 0x00); @@ -3152,8 +3150,6 @@ static int __tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable) /* Turn off MAD clk */ snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL, 0x01, 0x00); - snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG, - 0x04, 0x00); } done: return rc; @@ -9374,6 +9370,7 @@ static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = { {WCD934X_HPH_R_TEST, 0x01, 0x01}, {WCD934X_CPE_FLL_CONFIG_CTL_2, 0xFF, 0x20}, {WCD934X_MBHC_NEW_CTL_2, 0x0C, 0x00}, + {WCD934X_CODEC_RPM_CLK_MCLK_CFG, 0x04, 0x04}, }; static const struct tavil_reg_mask_val tavil_codec_reg_init_1_1_val[] = { From 1e40a9c21167f88d0423d207d4e03fa1f2b9a412 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Fri, 23 Mar 2018 14:20:47 +0800 Subject: [PATCH 186/276] asoc: wcd-dsp-mgr: clear work queue for wcd dsp manager wdsp_mgr_bind would schedule work to load wdsp firmware. Before loading wdsp firmware, sound card registeration may fail. wcd_dsp_cntl_deinit would get called to free cntl which would be used in wdsp_load_fw_image which would cause kernel panic. Clear work queue in wdsp_mgr_unbind to avoid kernel panic. Change-Id: I6c052fa8ea1ff62a8b075ba191085612242a5005 Signed-off-by: Meng Wang --- asoc/codecs/wcd-dsp-mgr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/asoc/codecs/wcd-dsp-mgr.c b/asoc/codecs/wcd-dsp-mgr.c index 50716e4d57c6..7695b16a6dd5 100644 --- a/asoc/codecs/wcd-dsp-mgr.c +++ b/asoc/codecs/wcd-dsp-mgr.c @@ -1118,6 +1118,8 @@ static void wdsp_mgr_unbind(struct device *dev) struct wdsp_cmpnt *cmpnt; int idx; + cancel_work_sync(&wdsp->load_fw_work); + component_unbind_all(dev, wdsp->ops); wdsp_mgr_debugfs_remove(wdsp); From ff93907e14d0dff5b345a35edd849b79dafa5788 Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Wed, 21 Feb 2018 14:17:48 -0800 Subject: [PATCH 187/276] dsp: add support to send uevents Add a common function handle to send uevents from q6 layer. Add functions to initialize and cleanup the kernel object required to send uevents. CRs-Fixed: 2211324 Change-Id: I9dceb4eb16d6941600455602f78f4f1498171d19 Signed-off-by: Vignesh Kulothungan --- dsp/q6core.c | 104 +++++++++++++++++++++++++++++++++++++++++++ include/dsp/q6core.h | 11 ++++- 2 files changed, 114 insertions(+), 1 deletion(-) diff --git a/dsp/q6core.c b/dsp/q6core.c index 7bb690ec9f51..747154d0acc2 100644 --- a/dsp/q6core.c +++ b/dsp/q6core.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #include #include #include @@ -82,6 +84,106 @@ struct generic_get_data_ { }; static struct generic_get_data_ *generic_get_data; +static DEFINE_MUTEX(kset_lock); +static struct kset *audio_uevent_kset; + +static int q6core_init_uevent_kset(void) +{ + int ret = 0; + + mutex_lock(&kset_lock); + if (audio_uevent_kset) + goto done; + + /* Create a kset under /sys/kernel/ */ + audio_uevent_kset = kset_create_and_add("q6audio", NULL, kernel_kobj); + if (!audio_uevent_kset) { + pr_err("%s: error creating uevent kernel set", __func__); + ret = -EINVAL; + } +done: + mutex_unlock(&kset_lock); + return ret; +} + +static void q6core_destroy_uevent_kset(void) +{ + if (audio_uevent_kset) { + kset_unregister(audio_uevent_kset); + audio_uevent_kset = NULL; + } +} + +/** + * q6core_init_uevent_data - initialize kernel object required to send uevents. + * + * @uevent_data: uevent data (dynamically allocated memory). + * @name: name of the kernel object. + * + * Returns 0 on success or error otherwise. + */ +int q6core_init_uevent_data(struct audio_uevent_data *uevent_data, char *name) +{ + int ret = -EINVAL; + + if (!uevent_data || !name) + return ret; + + ret = q6core_init_uevent_kset(); + if (ret) + return ret; + + /* Set kset for kobject before initializing the kobject */ + uevent_data->kobj.kset = audio_uevent_kset; + + /* Initialize kobject and add it to kernel */ + ret = kobject_init_and_add(&uevent_data->kobj, &uevent_data->ktype, + NULL, "%s", name); + if (ret) { + pr_err("%s: error initializing uevent kernel object: %d", + __func__, ret); + kobject_put(&uevent_data->kobj); + return ret; + } + + /* Send kobject add event to the system */ + kobject_uevent(&uevent_data->kobj, KOBJ_ADD); + + return ret; +} +EXPORT_SYMBOL(q6core_init_uevent_data); + +/** + * q6core_destroy_uevent_data - destroy kernel object. + * + * @uevent_data: uevent data. + */ +void q6core_destroy_uevent_data(struct audio_uevent_data *uevent_data) +{ + if (uevent_data) + kobject_put(&uevent_data->kobj); +} +EXPORT_SYMBOL(q6core_destroy_uevent_data); + +/** + * q6core_send_uevent - send uevent to userspace. + * + * @uevent_data: uevent data. + * @event: event to send. + * + * Returns 0 on success or error otherwise. + */ +int q6core_send_uevent(struct audio_uevent_data *uevent_data, char *event) +{ + char *env[] = { event, NULL }; + + if (!event || !uevent_data) + return -EINVAL; + + return kobject_uevent_env(&uevent_data->kobj, KOBJ_CHANGE, env); +} +EXPORT_SYMBOL(q6core_send_uevent); + static int parse_fwk_version_info(uint32_t *payload) { size_t ver_size; @@ -1082,6 +1184,7 @@ static int __init core_init(void) mutex_init(&q6core_lcl.ver_lock); q6core_init_cal_data(); + q6core_init_uevent_kset(); return 0; } @@ -1092,6 +1195,7 @@ static void __exit core_exit(void) mutex_destroy(&q6core_lcl.cmd_lock); mutex_destroy(&q6core_lcl.ver_lock); q6core_delete_cal_data(); + q6core_destroy_uevent_kset(); } module_exit(core_exit); MODULE_DESCRIPTION("ADSP core driver"); diff --git a/include/dsp/q6core.h b/include/dsp/q6core.h index f6240a33e6fd..03a7b31af52d 100644 --- a/include/dsp/q6core.h +++ b/include/dsp/q6core.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -27,6 +27,15 @@ int q6core_get_service_version(uint32_t service_id, size_t size); size_t q6core_get_fwk_version_size(uint32_t service_id); +struct audio_uevent_data { + struct kobject kobj; + struct kobj_type ktype; +}; + +int q6core_init_uevent_data(struct audio_uevent_data *uevent_data, char *name); +void q6core_destroy_uevent_data(struct audio_uevent_data *uevent_data); +int q6core_send_uevent(struct audio_uevent_data *uevent_data, char *name); + #define ADSP_CMD_SET_DTS_EAGLE_DATA_ID 0x00012919 #define DTS_EAGLE_LICENSE_ID 0x00028346 struct adsp_dts_eagle { From c2d23e41ee56b10b66afe06c7014012831602e5b Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Tue, 13 Mar 2018 16:05:20 -0700 Subject: [PATCH 188/276] dsp: add support for mic break detection Add capability to receive notifications from voice service when there is a change in mic break status during a voice call. On receiving notification, send an uevent with mic status to userspace. CRs-Fixed: 2211324 Change-Id: Ie55bd1490fd8cead7261b70169ae36eba95e4011 Signed-off-by: Vignesh Kulothungan --- asoc/msm-pcm-voice-v2.c | 20 ++++- dsp/q6voice.c | 195 +++++++++++++++++++++++++++++++++++++++- include/dsp/q6voice.h | 50 ++++++++++- 3 files changed, 262 insertions(+), 3 deletions(-) diff --git a/asoc/msm-pcm-voice-v2.c b/asoc/msm-pcm-voice-v2.c index 9972132000e9..c0968a36d8e8 100644 --- a/asoc/msm-pcm-voice-v2.c +++ b/asoc/msm-pcm-voice-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -519,6 +519,22 @@ static int msm_voice_rx_device_mute_put(struct snd_kcontrol *kcontrol, return ret; } +static int msm_voice_mbd_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = voc_get_mbd_enable(); + return 0; +} + +static int msm_voice_mbd_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + bool enable = ucontrol->value.integer.value[0]; + + voc_set_mbd_enable(enable); + + return 0; +} static const char * const tty_mode[] = {"OFF", "HCO", "VCO", "FULL"}; @@ -661,6 +677,8 @@ static struct snd_kcontrol_new msm_voice_controls[] = { }, SOC_SINGLE_MULTI_EXT("Voice Sidetone Enable", SND_SOC_NOPM, 0, 1, 0, 1, msm_voice_sidetone_get, msm_voice_sidetone_put), + SOC_SINGLE_BOOL_EXT("Voice Mic Break Enable", 0, msm_voice_mbd_get, + msm_voice_mbd_put), }; static const struct snd_pcm_ops msm_pcm_ops = { diff --git a/dsp/q6voice.c b/dsp/q6voice.c index 241e3622085c..5185f663ca24 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -71,6 +71,9 @@ static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v, 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_mvm_event_class_cmd(struct voice_data *v, + uint32_t event_id, + uint32_t class_id); 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, @@ -748,6 +751,69 @@ static int voice_send_mvm_cvd_version_cmd(struct voice_data *v) return ret; } +static int voice_send_mvm_event_class_cmd(struct voice_data *v, + uint32_t event_id, + uint32_t class_id) +{ + struct vss_inotify_cmd_event_class_t mvm_event; + 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; + } + + memset(&mvm_event, 0, sizeof(mvm_event)); + mvm_handle = voice_get_mvm_handle(v); + mvm_event.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + mvm_event.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, + sizeof(mvm_event) - APR_HDR_SIZE); + mvm_event.hdr.src_port = + voice_get_idx_for_session(v->session_id); + mvm_event.hdr.dest_port = mvm_handle; + mvm_event.hdr.token = 0; + mvm_event.hdr.opcode = event_id; + mvm_event.class_id = class_id; + + v->mvm_state = CMD_STATUS_FAIL; + v->async_err = 0; + ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_event); + if (ret < 0) { + pr_err("%s: Error %d sending %x event\n", __func__, ret, + event_id); + 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_dual_control_cmd(struct voice_data *v) { int ret = 0; @@ -4195,6 +4261,23 @@ static int voice_get_avcs_version_per_service(uint32_t service_id) return ret; } +static void voice_mic_break_work_fn(struct work_struct *work) +{ + int ret = 0; + char event[25]; + struct voice_data *v = container_of(work, struct voice_data, + voice_mic_break_work); + + snprintf(event, sizeof(event), "MIC_BREAK_STATUS=%s", + v->mic_break_status ? "TRUE" : "FALSE"); + + mutex_lock(&common.common_lock); + ret = q6core_send_uevent(common.uevent_data, event); + if (ret) + pr_err("%s: Send UEvent %s failed :%d", __func__, event, ret); + mutex_unlock(&common.common_lock); +} + static int voice_setup_vocproc(struct voice_data *v) { int ret = 0; @@ -4290,6 +4373,11 @@ static int voice_setup_vocproc(struct voice_data *v) if (v->hd_enable) voice_send_hd_cmd(v, v->hd_enable); + if (common.mic_break_enable) + voice_send_mvm_event_class_cmd(v, + VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS, + VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE); + rtac_add_voice(voice_get_cvs_handle(v), voice_get_cvp_handle(v), v->dev_rx.port_id, v->dev_tx.port_id, @@ -5031,6 +5119,11 @@ static int voice_destroy_vocproc(struct voice_data *v) voice_send_cvp_deregister_dev_cfg_cmd(v); voice_send_cvs_deregister_cal_cmd(v); + if (common.mic_break_enable) + voice_send_mvm_event_class_cmd(v, + VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS, + VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE); + /* destrop cvp session */ cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), @@ -6453,6 +6546,53 @@ uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir) return ret; } +bool voc_get_mbd_enable(void) +{ + bool enable = false; + + mutex_lock(&common.common_lock); + enable = common.mic_break_enable; + mutex_unlock(&common.common_lock); + + return enable; +} + +uint8_t voc_set_mbd_enable(bool enable) +{ + struct voice_data *v = NULL; + struct voice_session_itr itr; + bool check_and_send_event = false; + uint32_t event_id = VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS; + uint32_t class_id = VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE; + + + mutex_lock(&common.common_lock); + if (common.mic_break_enable != enable) + check_and_send_event = true; + common.mic_break_enable = enable; + mutex_unlock(&common.common_lock); + + if (!check_and_send_event) + return 0; + + if (!enable) + event_id = VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS; + + 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)) { + voice_send_mvm_event_class_cmd(v, event_id, + class_id); + } + mutex_unlock(&v->lock); + } + } + + return 0; +} + int voc_end_voice_call(uint32_t session_id) { struct voice_data *v = voice_get_session(session_id); @@ -6889,6 +7029,7 @@ 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; + struct vss_evt_voice_activity *voice_act_update = NULL; int i = 0; struct vss_iversion_rsp_get_t *version_rsp = NULL; @@ -6997,6 +7138,8 @@ static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv) case VSS_IMVM_CMD_STANDBY_VOICE: case VSS_IHDVOICE_CMD_ENABLE: case VSS_IHDVOICE_CMD_DISABLE: + case VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS: + case VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS: pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]); v->mvm_state = CMD_STATUS_SUCCESS; v->async_err = ptr[1]; @@ -7101,7 +7244,33 @@ static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv) v->mvm_state = CMD_STATUS_SUCCESS; wake_up(&v->mvm_wait); } + } else if (data->opcode == VSS_ICOMMON_EVT_VOICE_ACTIVITY_UPDATE) { + if (data->payload_size == sizeof(struct vss_evt_voice_activity)) { + voice_act_update = + (struct vss_evt_voice_activity *) + data->payload; + + /* Drop notifications other than Mic Break */ + if ((voice_act_update->activity + != VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK) + && (voice_act_update->activity + != VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK)) + return 0; + + switch (voice_act_update->activity) { + case VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK: + v->mic_break_status = true; + break; + case VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK: + v->mic_break_status = false; + break; + } + + if (c->mic_break_enable) + schedule_work(&(v->voice_mic_break_work)); + } } + return 0; } @@ -9084,6 +9253,14 @@ int voc_get_source_tracking(struct source_tracking_param *sourceTrackingData) return ret; } +static void voc_release_uevent_data(struct kobject *kobj) +{ + struct audio_uevent_data *data = container_of(kobj, + struct audio_uevent_data, + kobj); + kfree(data); +} + int is_voc_initialized(void) { return module_initialized; @@ -9129,6 +9306,18 @@ static int __init voice_init(void) mutex_init(&common.common_lock); + common.uevent_data = kzalloc(sizeof(*(common.uevent_data)), GFP_KERNEL); + if (!common.uevent_data) + return -ENOMEM; + + /* + * Set release function to cleanup memory related to kobject + * before initializing the kobject. + */ + common.uevent_data->ktype.release = voc_release_uevent_data; + q6core_init_uevent_data(common.uevent_data, "q6voice_uevent"); + common.mic_break_enable = false; + /* Initialize session id with vsid */ init_session_id(); @@ -9169,6 +9358,9 @@ static int __init voice_init(void) common.voice[i].voc_state = VOC_INIT; + INIT_WORK(&common.voice[i].voice_mic_break_work, + voice_mic_break_work_fn); + init_waitqueue_head(&common.voice[i].mvm_wait); init_waitqueue_head(&common.voice[i].cvs_wait); init_waitqueue_head(&common.voice[i].cvp_wait); @@ -9190,6 +9382,7 @@ device_initcall(voice_init); static void __exit voice_exit(void) { + q6core_destroy_uevent_data(common.uevent_data); voice_delete_cal_data(); free_cal_map_table(); } diff --git a/include/dsp/q6voice.h b/include/dsp/q6voice.h index a41a2db8f868..86be4e14eb7a 100644 --- a/include/dsp/q6voice.h +++ b/include/dsp/q6voice.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -15,6 +15,7 @@ #include #include #include +#include #include #define MAX_VOC_PKT_SIZE 642 @@ -504,6 +505,19 @@ struct vss_icommon_cmd_set_param_v2_t { #define VSS_IHDVOICE_CMD_ENABLE 0x000130A2 #define VSS_IHDVOICE_CMD_DISABLE 0x000130A3 +/* To listen for events from MVM module */ +#define VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS 0x00012E56 +/* To cancel listening for events from MVM module */ +#define VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS 0x00012E57 +/* To receive ongoing voice activity notification */ +#define VSS_ICOMMON_EVENT_CLASS_VOICE_ACTIVITY_UPDATE 0x000131EF +/* Voice activity notification from MVM */ +#define VSS_ICOMMON_EVT_VOICE_ACTIVITY_UPDATE 0x000131F0 +/* Mic path is broken. */ +#define VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK 0x000131F3 +/* Mic path is restored. */ +#define VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK 0x000131F4 + enum msm_audio_voc_rate { VOC_0_RATE, /* Blank frame */ VOC_8_RATE, /* 1/8 rate */ @@ -729,6 +743,33 @@ struct vss_imemory_cmd_unmap_t { uint32_t mem_handle; } __packed; +/* + * Payload structure for VSS_INOTIFY_CMD_LISTEN_FOR_EVENT_CLASS and + * VSS_INOTIFY_CMD_CANCEL_EVENT_CLASS commands. + */ +struct vss_inotify_cmd_event_class_t { + struct apr_hdr hdr; + /* Event class ID to listen for. */ + uint32_t class_id; +} __packed; + +/* Payload structure for the VSS_ICOMMOM_EVT_VOICE_ACTIVITY_UPDATE event. */ +struct vss_evt_voice_activity { + uint32_t activity; + /* + * Specifies the voice acitivity. + * @values + * #VSS_ICOMMON_VOICE_ACTIVITY_VPTX_MUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_VPTX_UNMUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_MIC_BREAK + * #VSS_ICOMMON_VOICE_ACITIVTY_MIC_UNBREAK + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_STREAM_TX_MUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_STREAM_TX_UNMUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_VOCPROC_TX_MUTE + * #VSS_ICOMMON_VOICE_ACTIVITY_UI_VOCPROC_TX_UNMUTE + */ +} __packed; + /* TO CVS commands */ #define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION 0x00011140 /**< Wait for APRV2_IBASIC_RSP_RESULT response. */ @@ -1872,6 +1913,9 @@ struct voice_data { struct incall_music_info music_info; struct voice_rec_route_state rec_route_state; + + bool mic_break_status; + struct work_struct voice_mic_break_work; }; struct cal_mem { @@ -1934,6 +1978,8 @@ struct common_data { struct shared_mem_info source_tracking_sh_mem; struct vss_isourcetrack_activity_data_t sourceTrackingResponse; bool sidetone_enable; + bool mic_break_enable; + struct audio_uevent_data *uevent_data; }; struct voice_session_itr { @@ -2023,6 +2069,8 @@ int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute, 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); +bool voc_get_mbd_enable(void); +uint8_t voc_set_mbd_enable(bool enable); 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); From 69e63f479fd4abaadc5a69311439ff89766ebae1 Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Thu, 5 Apr 2018 16:37:59 -0700 Subject: [PATCH 189/276] soc: wcd-spi-ac: add logic to wait when service is offline If the service exits while holding wcd-spi access, it is required for the service to come up again and re-initialize the hardware. In the mean time, if there are any requests to access wcd-spi, they should be blocked until service is up. Change-Id: I8f19c381ce852917d371309fae9450dda4f3aff8 Signed-off-by: Bhalchandra Gajare --- soc/wcd-spi-ac.c | 144 ++++++++++++++++++++++++++++++----------------- 1 file changed, 93 insertions(+), 51 deletions(-) diff --git a/soc/wcd-spi-ac.c b/soc/wcd-spi-ac.c index 4eeeaedb5774..92eaa8937344 100644 --- a/soc/wcd-spi-ac.c +++ b/soc/wcd-spi-ac.c @@ -52,8 +52,9 @@ * to be released. */ #define WCD_SPI_AC_STATUS_RELEASE_ACCESS 0x00 -#define WCD_SPI_AC_LOCAL_ACCESS 0x00 -#define WCD_SPI_AC_REMOTE_ACCESS 0x01 +#define WCD_SPI_AC_LOCAL_ACCESS (0) +#define WCD_SPI_AC_REMOTE_ACCESS (0x01) +#define WCD_SPI_AC_NO_ACCESS (0x02) #define WCD_SPI_CTL_INS_ID 0 #define WCD_SPI_AC_QMI_TIMEOUT_MS 100 @@ -80,6 +81,7 @@ struct wcd_spi_ac_priv { u8 svc_offline_change; wait_queue_head_t svc_poll_wait; struct mutex status_lock; + struct completion online_compl; /* state maintenence related */ u32 state; @@ -91,7 +93,8 @@ struct wcd_spi_ac_priv { struct work_struct svc_arr_work; struct work_struct svc_exit_work; struct notifier_block nb; - struct mutex svc_lock; + struct mutex msg_lock; + struct mutex event_lock; struct workqueue_struct *qmi_wq; struct work_struct recv_msg_work; }; @@ -226,17 +229,13 @@ static void wcd_spi_ac_procfs_deinit(struct wcd_spi_ac_priv *ac) proc_remove(ac->pfs_root); } -static int wcd_spi_ac_request_access(struct wcd_spi_ac_priv *ac, - bool is_svc_locked) +static int wcd_spi_ac_request_access(struct wcd_spi_ac_priv *ac) { struct wcd_spi_req_access_msg_v01 req; struct wcd_spi_req_access_resp_v01 rsp; struct msg_desc req_desc, rsp_desc; int ret = 0; - dev_dbg(ac->dev, "%s: is_svc_locked = %s\n", - __func__, is_svc_locked ? "true" : "false"); - memset(&req, 0, sizeof(req)); memset(&rsp, 0, sizeof(rsp)); @@ -251,8 +250,7 @@ static int wcd_spi_ac_request_access(struct wcd_spi_ac_priv *ac, rsp_desc.msg_id = WCD_SPI_REQ_ACCESS_RESP_V01; rsp_desc.ei_array = wcd_spi_req_access_resp_v01_ei; - if (!is_svc_locked) - WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock); + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->msg_lock); ret = qmi_send_req_wait(ac->qmi_hdl, &req_desc, &req, sizeof(req), @@ -270,23 +268,19 @@ static int wcd_spi_ac_request_access(struct wcd_spi_ac_priv *ac, __func__, rsp.resp.result); } done: - if (!is_svc_locked) - WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock); + dev_dbg(ac->dev, "%s: status %d\n", __func__, ret); + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->msg_lock); return ret; } -static int wcd_spi_ac_release_access(struct wcd_spi_ac_priv *ac, - bool is_svc_locked) +static int wcd_spi_ac_release_access(struct wcd_spi_ac_priv *ac) { struct wcd_spi_rel_access_msg_v01 req; struct wcd_spi_rel_access_resp_v01 rsp; struct msg_desc req_desc, rsp_desc; int ret = 0; - dev_dbg(ac->dev, "%s: is_svc_locked = %s\n", - __func__, is_svc_locked ? "true" : "false"); - memset(&req, 0, sizeof(req)); memset(&rsp, 0, sizeof(rsp)); @@ -298,8 +292,7 @@ static int wcd_spi_ac_release_access(struct wcd_spi_ac_priv *ac, rsp_desc.msg_id = WCD_SPI_REL_ACCESS_RESP_V01; rsp_desc.ei_array = wcd_spi_rel_access_resp_v01_ei; - if (!is_svc_locked) - WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock); + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->msg_lock); ret = qmi_send_req_wait(ac->qmi_hdl, &req_desc, &req, sizeof(req), @@ -317,8 +310,9 @@ static int wcd_spi_ac_release_access(struct wcd_spi_ac_priv *ac, __func__, rsp.resp.result); } done: - if (!is_svc_locked) - WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock); + dev_dbg(ac->dev, "%s: status %d\n", __func__, ret); + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->msg_lock); + return ret; } @@ -354,7 +348,7 @@ static int wcd_spi_ac_buf_msg( rsp_desc.msg_id = WCD_SPI_BUFF_RESP_V01; rsp_desc.ei_array = wcd_spi_buff_resp_v01_ei; - WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock); + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->msg_lock); ret = qmi_send_req_wait(ac->qmi_hdl, &req_desc, &req, sizeof(req), &rsp_desc, &rsp, sizeof(rsp), @@ -372,7 +366,7 @@ static int wcd_spi_ac_buf_msg( __func__, rsp.resp.result); } done: - WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock); + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->msg_lock); return ret; } @@ -383,10 +377,9 @@ static int wcd_spi_ac_buf_msg( * already accesible. * @ac: pointer to the drivers private data * @value: value to be set in the status mask - * @is_svc_locked: flag to indicate if svc_lock is acquired by caller */ static int wcd_spi_ac_set_sync(struct wcd_spi_ac_priv *ac, - u32 value, bool is_svc_locked) + u32 value) { int ret = 0; @@ -394,18 +387,60 @@ static int wcd_spi_ac_set_sync(struct wcd_spi_ac_priv *ac, ac->state |= value; /* any non-zero state indicates us to request SPI access */ wmb(); - dev_dbg(ac->dev, "%s: current state = 0x%x, current access 0x%x\n", - __func__, ac->state, ac->current_access); + dev_dbg(ac->dev, + "%s: value 0x%x cur_state = 0x%x cur_access 0x%x\n", + __func__, value, ac->state, ac->current_access); if (ac->current_access == WCD_SPI_AC_REMOTE_ACCESS) { + if (value & WCD_SPI_AC_SVC_OFFLINE) { + /* + * service exited while holding access. + * SPI access is blocked until service + * arrives again. + */ + ac->current_access = WCD_SPI_AC_NO_ACCESS; + dev_err(ac->dev, + "%s: svc exit while holding access\n", + __func__); + goto done; + } + dev_dbg(ac->dev, "%s: requesting access, state = 0x%x\n", __func__, ac->state); - ret = wcd_spi_ac_request_access(ac, is_svc_locked); + ret = wcd_spi_ac_request_access(ac); if (!ret) ac->current_access = WCD_SPI_AC_LOCAL_ACCESS; + } else if (ac->current_access == WCD_SPI_AC_NO_ACCESS) { + /* + * Access is lost due to service offline, + * Wait here until service is back online + */ + ret = wait_for_completion_timeout(&ac->online_compl, + msecs_to_jiffies(3000)); + if (!ret) { + dev_err(ac->dev, + "%s: svc_online timedout\n", __func__); + ret = -ETIMEDOUT; + goto done; + } + + /* + * service is now online, current access is expected + * to be local at this time. check to make sure. + */ + if (ac->current_access != WCD_SPI_AC_LOCAL_ACCESS) { + dev_err(ac->dev, + "%s: wait complete, access 0x%x not local\n", + __func__, ac->current_access); + ret = -EIO; + goto done; + } + + /* Explicity assign return to 0, as access is local */ + ret = 0; } +done: WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->state_lock); - return ret; } @@ -414,10 +449,9 @@ static int wcd_spi_ac_set_sync(struct wcd_spi_ac_priv *ac, * bus and releases access if applicable * @ac: pointer to the drivers private data * @value: value to be cleared in the status mask - * @is_svc_locked: flag to indicate if svc_lock is acquired by caller */ static int wcd_spi_ac_clear_sync(struct wcd_spi_ac_priv *ac, - u32 value, bool is_svc_locked) + u32 value) { int ret = 0; @@ -433,7 +467,7 @@ static int wcd_spi_ac_clear_sync(struct wcd_spi_ac_priv *ac, dev_dbg(ac->dev, "%s: releasing access, state = 0x%x\n", __func__, ac->state); - ret = wcd_spi_ac_release_access(ac, is_svc_locked); + ret = wcd_spi_ac_release_access(ac); if (!ret) ac->current_access = WCD_SPI_AC_REMOTE_ACCESS; } @@ -484,13 +518,13 @@ int wcd_spi_access_ctl(struct device *dev, switch (request) { case WCD_SPI_ACCESS_REQUEST: - ret = wcd_spi_ac_set_sync(ac, reason, false); + ret = wcd_spi_ac_set_sync(ac, reason); if (ret) dev_err(dev, "%s: set_sync(0x%x) failed %d\n", __func__, reason, ret); break; case WCD_SPI_ACCESS_RELEASE: - ret = wcd_spi_ac_clear_sync(ac, reason, false); + ret = wcd_spi_ac_clear_sync(ac, reason); if (ret) dev_err(dev, "%s: clear_sync(0x%x) failed %d\n", __func__, reason, ret); @@ -571,7 +605,7 @@ static ssize_t wcd_spi_ac_cdev_write(struct file *file, switch (cmd_buf->cmd_type) { case WCD_SPI_AC_CMD_CONC_BEGIN: - ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_CONCURRENCY, false); + ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_CONCURRENCY); if (ret) { dev_err(ac->dev, "%s: set_sync(CONC) fail %d\n", __func__, ret); @@ -581,7 +615,7 @@ static ssize_t wcd_spi_ac_cdev_write(struct file *file, break; case WCD_SPI_AC_CMD_CONC_END: - ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_CONCURRENCY, false); + ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_CONCURRENCY); if (ret) { dev_err(ac->dev, "%s: clear_sync(CONC) fail %d\n", __func__, ret); @@ -626,8 +660,7 @@ static ssize_t wcd_spi_ac_cdev_write(struct file *file, goto free_cmd_buf; } - ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_UNINITIALIZED, - false); + ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_UNINITIALIZED); if (ret) { dev_err(ac->dev, "%s: clear_sync 0x%lx failed %d\n", __func__, WCD_SPI_AC_UNINITIALIZED, ret); @@ -662,7 +695,7 @@ static int wcd_spi_ac_cdev_release(struct inode *inode, return -EINVAL; } - ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_UNINITIALIZED, false); + ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_UNINITIALIZED); if (ret) dev_err(ac->dev, "%s: set_sync(UNINITIALIZED) failed %d\n", __func__, ret); @@ -792,7 +825,7 @@ static void wcd_spi_ac_svc_arrive(struct work_struct *work) return; } - WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock); + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->event_lock); ac->qmi_hdl = qmi_handle_create(wcd_spi_ac_clnt_notify, ac); if (!ac->qmi_hdl) { @@ -813,19 +846,22 @@ static void wcd_spi_ac_svc_arrive(struct work_struct *work) goto done; } - /* Mark service as online */ - wcd_spi_ac_status_change(ac, 1); - + /* Notify service availability */ + ac->current_access = WCD_SPI_AC_LOCAL_ACCESS; + complete(&ac->online_compl); /* * update the state and clear the WCD_SPI_AC_SVC_OFFLINE * bit to indicate that the service is now online. */ - ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_SVC_OFFLINE, true); + ret = wcd_spi_ac_clear_sync(ac, WCD_SPI_AC_SVC_OFFLINE); if (ret) dev_err(ac->dev, "%s: clear_sync(SVC_OFFLINE) failed %d\n", __func__, ret); + + /* Mark service as online */ + wcd_spi_ac_status_change(ac, 1); done: - WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock); + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->event_lock); } @@ -842,15 +878,17 @@ static void wcd_spi_ac_svc_exit(struct work_struct *work) return; } - WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->svc_lock); - ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_SVC_OFFLINE, true); + WCD_SPI_AC_MUTEX_LOCK(ac->dev, ac->event_lock); + reinit_completion(&ac->online_compl); + ret = wcd_spi_ac_set_sync(ac, WCD_SPI_AC_SVC_OFFLINE | + WCD_SPI_AC_UNINITIALIZED); if (ret) dev_err(ac->dev, "%s: set_sync(SVC_OFFLINE) failed %d\n", __func__, ret); qmi_handle_destroy(ac->qmi_hdl); ac->qmi_hdl = NULL; wcd_spi_ac_status_change(ac, 0); - WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->svc_lock); + WCD_SPI_AC_MUTEX_UNLOCK(ac->dev, ac->event_lock); } static int wcd_spi_ac_svc_event(struct notifier_block *this, @@ -907,7 +945,9 @@ static int wcd_spi_ac_probe(struct platform_device *pdev) mutex_init(&ac->status_lock); mutex_init(&ac->state_lock); - mutex_init(&ac->svc_lock); + mutex_init(&ac->msg_lock); + mutex_init(&ac->event_lock); + init_completion(&ac->online_compl); init_waitqueue_head(&ac->svc_poll_wait); ac->svc_offline = 1; ac->state = (WCD_SPI_AC_SVC_OFFLINE | @@ -951,7 +991,8 @@ static int wcd_spi_ac_probe(struct platform_device *pdev) wcd_spi_ac_procfs_deinit(ac); mutex_destroy(&ac->status_lock); mutex_destroy(&ac->state_lock); - mutex_destroy(&ac->svc_lock); + mutex_destroy(&ac->msg_lock); + mutex_destroy(&ac->event_lock); unreg_chardev: wcd_spi_ac_unreg_chardev(ac); return ret; @@ -973,7 +1014,8 @@ static int wcd_spi_ac_remove(struct platform_device *pdev) wcd_spi_ac_procfs_deinit(ac); mutex_destroy(&ac->status_lock); mutex_destroy(&ac->state_lock); - mutex_destroy(&ac->svc_lock); + mutex_destroy(&ac->msg_lock); + mutex_destroy(&ac->event_lock); return 0; } From 3829bab9c1cacffd20781b0466d43ab74600278a Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Tue, 10 Apr 2018 11:12:30 +0800 Subject: [PATCH 190/276] ASoC: codecs: update max value for HPH volume On tavil, the range for HPHL/HPHR volume is 0 to 24. Update max value for HPHL/HPHR Volume. Change-Id: I03f2eb3f833e5dc93a7cddaba09e765aa6077616 Signed-off-by: Meng Wang --- asoc/codecs/wcd934x/wcd934x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 463f7f2b8ca4..eccaa5ff13b7 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -6220,8 +6220,8 @@ static const struct snd_kcontrol_new tavil_snd_controls[] = { SOC_ENUM_EXT("SPKR Right Boost Max State", tavil_spkr_boost_stage_enum, tavil_spkr_right_boost_stage_get, tavil_spkr_right_boost_stage_put), - SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 20, 1, line_gain), - SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 20, 1, line_gain), + SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 24, 1, line_gain), + SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 24, 1, line_gain), SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER, 3, 16, 1, line_gain), SOC_SINGLE_TLV("LINEOUT2 Volume", WCD934X_DIFF_LO_LO2_COMPANDER, From 2fa1172971a623b34ec6b56cdcc7c013e76b459e Mon Sep 17 00:00:00 2001 From: Bhalchandra Gajare Date: Wed, 14 Feb 2018 18:12:43 -0800 Subject: [PATCH 191/276] ASoC: wcd934x: mark wdma3 output widget as ignore_suspend Paths using WDMA3_OUT widget are currently not marked to be ignored during suspend, which results in the path teardown during suspend. Due to this the audio use-case is broken. Fix this by marking WDMA3_OUT widget as ignore_suspend. CRs-fixed: 2176596 Change-Id: I72a2dda21aabfe9b13ea8660d4e3a51b3185d9ea Signed-off-by: Bhalchandra Gajare --- asoc/codecs/wcd934x/wcd934x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 463f7f2b8ca4..5cc716267ed0 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -10210,6 +10210,8 @@ static int tavil_soc_codec_probe(struct snd_soc_codec *codec) snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture"); snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback"); snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture"); + snd_soc_dapm_ignore_suspend(dapm, "WDMA3_OUT"); + if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) { snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback"); snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX"); From 169184531897c55666e8e977649f07de036a1ea0 Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Tue, 10 Apr 2018 08:34:48 -0700 Subject: [PATCH 192/276] dsp: fix encoder config check in AFE port start Correct configuration check for A2DP encoders in AFE port start. Change-Id: I48a5c0858fda0b73fe0bd9ad628f50afeb996e36 Signed-off-by: Aniket Kumar Lata --- dsp/q6afe.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 18de8f39fb96..6dc42047c146 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -3430,7 +3430,7 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config, if ((codec_format != ASM_MEDIA_FMT_NONE) && (cfg_type == AFE_PARAM_ID_SLIMBUS_CONFIG)) { - if (enc_cfg != NULL) + if (enc_cfg != NULL) { pr_debug("%s: Found AFE encoder support for SLIMBUS enc_format = %d\n", __func__, codec_format); ret = q6afe_send_enc_config(port_id, enc_cfg, @@ -3443,6 +3443,7 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config, __func__, port_id, ret); goto fail_cmd; } + } if (dec_cfg != NULL) { pr_debug("%s: Found AFE decoder support for SLIMBUS dec_format = %d\n", __func__, codec_format); From e8736e1c71ffbf05fe10e03468e4442a51d3c256 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 11 Apr 2018 17:33:55 +0800 Subject: [PATCH 193/276] ASoC: msm: remove pcm_i2S_sel_vt_address from machine driver lpaif_muxsel_virt_addr to select AUXPCM or I2S is not needed in machine driver. ADSP would select AUXPCM or I2S according to afe port. Remove related change in machine driver. Change-Id: I55520a56bab2134a08e86909b98eb15d588c53ba Signed-off-by: Meng Wang --- asoc/sdm845.c | 164 +------------------------------------------------- 1 file changed, 1 insertion(+), 163 deletions(-) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index a03b2ce7b670..9644aedad644 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -116,19 +116,6 @@ enum { 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; @@ -142,11 +129,6 @@ static u32 mi2s_ebit_clk[MI2S_MAX] = { Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT }; -struct auxpcm_conf { - struct mutex lock; - u32 ref_cnt; -}; - struct dev_config { u32 sample_rate; u32 bit_format; @@ -592,9 +574,7 @@ static struct afe_clk_set mi2s_clk[MI2S_MAX] = { } }; -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) { @@ -4254,83 +4234,6 @@ static int msm_wcn_hw_params(struct snd_pcm_substream *substream, 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; - - 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 err; - } - - 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 (ret < 0) - auxpcm_intf_conf[index].ref_cnt--; - - mutex_unlock(&auxpcm_intf_conf[index].lock); - -err: - 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__); - } - } - mutex_unlock(&auxpcm_intf_conf[index].lock); -} - static int msm_get_port_id(int be_id) { int afe_port_id; @@ -4855,18 +4758,7 @@ static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream) __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; - } + ret = snd_soc_dai_set_fmt(cpu_dai, fmt); if (ret < 0) { pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n", @@ -4932,11 +4824,6 @@ static struct snd_soc_ops msm_mi2s_be_ops = { .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_be_ops = { .hw_params = msm_snd_hw_params, }; @@ -6241,7 +6128,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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, @@ -6256,7 +6142,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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 */ { @@ -6272,7 +6157,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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, @@ -6287,7 +6171,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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 */ { @@ -6303,7 +6186,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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, @@ -6318,7 +6200,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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 */ { @@ -6334,7 +6215,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .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, @@ -6349,7 +6229,6 @@ static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = { .be_hw_params_fixup = msm_be_hw_params_fixup, .ignore_suspend = 1, .ignore_pmdown_time = 1, - .ops = &msm_aux_pcm_be_ops, }, }; @@ -6979,41 +6858,15 @@ static int msm_init_wsa_dev(struct platform_device *pdev, static void msm_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); @@ -7032,21 +6885,6 @@ static void msm_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); - mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL; - } - mutex_destroy(&mi2s_auxpcm_conf[count].lock); - } - - for (count = 0; count < AUX_PCM_MAX; count++) { - mutex_destroy(&auxpcm_intf_conf[count].lock); - auxpcm_intf_conf[count].ref_cnt = 0; - } - for (count = 0; count < MI2S_MAX; count++) { mutex_destroy(&mi2s_intf_conf[count].lock); mi2s_intf_conf[count].ref_cnt = 0; From bfe1f1d6437593697977d4c5c148a094566a6632 Mon Sep 17 00:00:00 2001 From: Haynes Mathew George Date: Mon, 29 Jan 2018 17:34:49 -0800 Subject: [PATCH 194/276] asoc: Fix check when opening ADM for listen Routing driver decides path type for capture sessions by checking the passthrough type field of a backend. This check was missing a check for passthrough type LISTEN. This leads to a listen session being incorrectly opened with path type COMPRESSED. Fix the check to consider LISTEN passthrough type. CRs-Fixed: 2196911 Change-Id: I3663b7ffbc0d9c649a060acfd9d65a3c22039dbc Signed-off-by: Haynes Mathew George --- asoc/msm-pcm-routing-v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 5fdb9563a457..fee15d413ba7 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -1632,7 +1632,7 @@ static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set) path_type = ADM_PATH_PLAYBACK; } else { session_type = SESSION_TYPE_TX; - if (passthr_mode != LEGACY_PCM) + if ((passthr_mode != LEGACY_PCM) && (passthr_mode != LISTEN)) path_type = ADM_PATH_COMPRESSED_TX; else path_type = ADM_PATH_LIVE_REC; From 64f13f6a69cdac4794f0318fd510e8df9c95972f Mon Sep 17 00:00:00 2001 From: Josh Kirsch Date: Tue, 10 Apr 2018 17:25:42 -0700 Subject: [PATCH 195/276] ASoC: msm: qdsp6v2: Add back dapm routes needed for SDA845 Add back dapm voice stub and voice stub 2 routes. Needed for SDA845. Change-Id: Iacf680461fe7e2364ec1333dcbb0a6c3b671c9aa Signed-off-by: Josh Kirsch --- asoc/msm-pcm-routing-v2.c | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 5fdb9563a457..cfb80e4a0a0e 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -8054,6 +8054,12 @@ static const struct snd_kcontrol_new sec_mi2s_rx_voice_mixer_controls[] = { 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("Voice Stub", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_mixer, + msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 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), @@ -8162,6 +8168,12 @@ static const struct snd_kcontrol_new mi2s_rx_voice_mixer_controls[] = { 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("Voice2 Stub", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer, + msm_routing_put_voice_stub_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), @@ -8180,6 +8192,12 @@ static const struct snd_kcontrol_new pri_mi2s_rx_voice_mixer_controls[] = { 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("DTMF", MSM_BACKEND_DAI_PRI_MI2S_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8234,6 +8252,12 @@ static const struct snd_kcontrol_new tert_mi2s_rx_voice_mixer_controls[] = { 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("DTMF", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8252,6 +8276,12 @@ static const struct snd_kcontrol_new quat_mi2s_rx_voice_mixer_controls[] = { 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("DTMF", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8270,6 +8300,12 @@ static const struct snd_kcontrol_new quin_mi2s_rx_voice_mixer_controls[] = { 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("DTMF", MSM_BACKEND_DAI_QUINARY_MI2S_RX, MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), @@ -8735,6 +8771,9 @@ static const struct snd_kcontrol_new tx_voice2_stub_mixer_controls[] = { 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("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_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), @@ -14957,6 +14996,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"}, {"SEC_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"SEC_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"SEC_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_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"}, @@ -15049,6 +15090,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"HDMI", NULL, "HDMI_DL_HL"}, {"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"}, + {"MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"}, + {"MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"}, {"MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"}, {"MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, {"MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"}, @@ -15056,6 +15099,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"}, {"PRI_MI2S_RX_Voice Mixer", "Voip", "VOIP_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"}, @@ -15077,6 +15122,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_Voice Mixer"}, {"TERT_MI2S_RX_Voice Mixer", "Voip", "VOIP_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"}, @@ -15084,12 +15131,16 @@ static const struct snd_soc_dapm_route intercon[] = { {"TERT_MI2S_RX", NULL, "TERT_MI2S_RX_Voice Mixer"}, {"QUAT_MI2S_RX_Voice Mixer", "Voip", "VOIP_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", "Voip", "VOIP_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"}, @@ -16019,6 +16070,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Tx Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"VOLTE_STUB_UL", NULL, "VoLTE Stub Tx Mixer"}, {"Voice2 Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"}, @@ -16032,6 +16084,7 @@ static const struct snd_soc_dapm_route intercon[] = { {"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 Tx Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"VOICE2_STUB_UL", NULL, "Voice2 Stub Tx Mixer"}, {"STUB_RX Mixer", "VoiceMMode1", "VOICEMMODE1_DL"}, From 088434cdf2a922d18e34679c3f9366870d153c9a Mon Sep 17 00:00:00 2001 From: Josh Kirsch Date: Thu, 22 Mar 2018 10:00:26 -0700 Subject: [PATCH 196/276] soc: msm_audio_ion: Add audio memory protection API call for MDM Add audio memory protection scm API call in msm_audio_ion_probe to protect audio ion memory region. Fetch the base address and size of audio-mem using CMA APIs and give them as arguments to scm call2. SCM driver internally calls TZ APIs. Change-Id: I84ef72cfed515ba451554458ce30cb7a51662534 Signed-off-by: Josh Kirsch --- dsp/msm_audio_ion.c | 97 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/dsp/msm_audio_ion.c b/dsp/msm_audio_ion.c index e1e769e2d7d3..1a6604cffd73 100644 --- a/dsp/msm_audio_ion.c +++ b/dsp/msm_audio_ion.c @@ -20,14 +20,18 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include +#include +#include #define MSM_AUDIO_ION_PROBED (1 << 0) @@ -39,6 +43,22 @@ #define MSM_AUDIO_SMMU_SID_OFFSET 32 +/* mem protection defines */ +#define TZBSP_MEM_PROTECT_AUDIO_CMD_ID 0x00000016 +#define MEM_PROTECT_VMID_HLOS 0x3 +#define MEM_PROTECT_VMID_MSS_MSA 0xF +#define MEM_PROTECT_PERM_READ 0x4 +#define MEM_PROTECT_PERM_WRITE 0x2 +#define MEM_PROTECT_MAX_DEST 2 + +#ifdef CONFIG_ARM64 + +/* functions not defined on arm64 */ +#define outer_flush_range(x, y) +#define __cpuc_flush_dcache_area __flush_dcache_area + +#endif + struct msm_audio_ion_private { bool smmu_enabled; struct device *cb_dev; @@ -60,6 +80,25 @@ struct msm_audio_alloc_data { struct list_head list; }; +struct msm_audio_mem_prot_info { + u64 addr; + u64 size; +}; + +struct msm_audio_dest_vm_and_perm_info { + u32 vm; + u32 perm; + u64 ctx; + u32 ctx_size; +}; + +struct msm_audio_protect_mem { + struct msm_audio_mem_prot_info mem_info; + u32 source_vm; + struct msm_audio_dest_vm_and_perm_info dest_info[MEM_PROTECT_MAX_DEST]; +}; + + static struct msm_audio_ion_private msm_audio_ion_data = {0,}; static int msm_audio_ion_get_phys(struct ion_client *client, @@ -714,13 +753,65 @@ u32 msm_audio_populate_upper_32_bits(ion_phys_addr_t pa) return upper_32_bits(pa); } +static void msm_audio_protect_memory_region(struct device *dev) +{ + int ret = 0; + struct scm_desc desc = {0}; + struct msm_audio_protect_mem scm_buffer; + + scm_buffer.mem_info.addr = cma_get_base(dev_get_cma_area(dev)); + scm_buffer.mem_info.size = cma_get_size(dev_get_cma_area(dev)); + + pr_debug("%s: cma_audio_mem_addr %pK with size %llu\n", + __func__, &(scm_buffer.mem_info.addr), + scm_buffer.mem_info.size); + + scm_buffer.dest_info[0].vm = MEM_PROTECT_VMID_MSS_MSA; + scm_buffer.dest_info[0].perm = + MEM_PROTECT_PERM_READ|MEM_PROTECT_PERM_WRITE; + scm_buffer.dest_info[0].ctx = 0; + scm_buffer.dest_info[0].ctx_size = 0; + scm_buffer.dest_info[1].vm = MEM_PROTECT_VMID_HLOS; + scm_buffer.dest_info[1].perm = + MEM_PROTECT_PERM_READ|MEM_PROTECT_PERM_WRITE; + scm_buffer.dest_info[1].ctx = 0; + scm_buffer.dest_info[1].ctx_size = 0; + + scm_buffer.source_vm = MEM_PROTECT_VMID_HLOS; + + /* flush cache required by scm_call2 */ + __cpuc_flush_dcache_area(&scm_buffer, sizeof(scm_buffer)); + outer_flush_range(virt_to_phys(&scm_buffer), + virt_to_phys(&scm_buffer) + sizeof(scm_buffer)); + + desc.args[0] = virt_to_phys(&(scm_buffer.mem_info)); + desc.args[1] = sizeof(scm_buffer.mem_info); /*array size of args[0] */ + desc.args[2] = virt_to_phys(&(scm_buffer.source_vm)); /*source*/ + desc.args[3] = sizeof(scm_buffer.source_vm); /*array size of args[2] */ + desc.args[4] = virt_to_phys(&(scm_buffer.dest_info)); /*destination */ + desc.args[5] = sizeof(scm_buffer.dest_info); /*array size of args[4] */ + desc.args[6] = 0; /*reserved*/ + + desc.arginfo = SCM_ARGS(7, SCM_RO, SCM_VAL, SCM_RO, SCM_VAL, SCM_RO, + SCM_VAL, SCM_VAL); + + ret = scm_call2(SCM_SIP_FNID(SCM_SVC_MP, + TZBSP_MEM_PROTECT_AUDIO_CMD_ID), &desc); + + if (ret < 0) + pr_err("%s: SCM call2 failed, ret %d scm_resp %llu\n", + __func__, ret, desc.ret[0]); +} + 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"; + const char *mdm_audio_ion_scm = "qcom,scm-mp-enabled"; const char *msm_audio_ion_smmu_sid_mask = "qcom,smmu-sid-mask"; bool smmu_enabled; + bool scm_mp_enabled; enum apr_subsys_state q6_state; struct device *dev = &pdev->dev; @@ -732,6 +823,9 @@ static int msm_audio_ion_probe(struct platform_device *pdev) return 0; } + scm_mp_enabled = of_property_read_bool(dev->of_node, + mdm_audio_ion_scm); + smmu_enabled = of_property_read_bool(dev->of_node, msm_audio_ion_dt); msm_audio_ion_data.smmu_enabled = smmu_enabled; @@ -761,6 +855,9 @@ static int msm_audio_ion_probe(struct platform_device *pdev) dev_dbg(dev, "%s: SMMU is %s\n", __func__, (smmu_enabled) ? "Enabled" : "Disabled"); + if (scm_mp_enabled) + msm_audio_protect_memory_region(dev); + if (smmu_enabled) { u64 smmu_sid = 0; u64 smmu_sid_mask = 0; From f6c5fd4bd8645fdd7e46123e424a1ed810d8987c Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Tue, 3 Apr 2018 15:11:36 +0800 Subject: [PATCH 197/276] ipc: make APR driver a platform driver Change APR driver to platform driver and add any child device only after ADSP up notification is received. The idea is to have machine driver as a child device under APR, and add the platform device for machine driver only after ADSP is up. This will help invoke audio drivers waiting with deferred probe and eventually should help sound card registers successfully. Change-Id: Ib0c0f7ec1d7dd93a1b54a9a66260861223d55c67 Signed-off-by: Laxminath Kasam Signed-off-by: Banajit Goswami Signed-off-by: Meng Wang --- ipc/apr.c | 186 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 160 insertions(+), 26 deletions(-) diff --git a/ipc/apr.c b/ipc/apr.c index c7eab98fb69b..f9caca741a8d 100644 --- a/ipc/apr.c +++ b/ipc/apr.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -42,7 +43,6 @@ 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); @@ -52,6 +52,21 @@ struct apr_reset_work { struct work_struct work; }; +struct apr_chld_device { + struct platform_device *pdev; + struct list_head node; +}; + +struct apr_private { + struct device *dev; + spinlock_t apr_lock; + bool is_initial_boot; + struct work_struct add_chld_dev_work; + spinlock_t apr_chld_lock; + struct list_head apr_chlds; +}; + +static struct apr_private *apr_priv; static bool apr_cf_debug; #ifdef CONFIG_DEBUG_FS @@ -261,15 +276,65 @@ enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev, static void apr_adsp_down(unsigned long opcode) { + pr_debug("%s: Q6 is Down\n", __func__); apr_set_q6_state(APR_SUBSYS_DOWN); dispatch_event(opcode, APR_DEST_QDSP6); } +static void apr_add_child_devices(struct work_struct *work) +{ + int ret; + struct device_node *node; + struct platform_device *pdev; + struct apr_chld_device *apr_chld_dev; + + for_each_child_of_node(apr_priv->dev->of_node, node) { + apr_chld_dev = kzalloc(sizeof(*apr_chld_dev), GFP_KERNEL); + if (!apr_chld_dev) + continue; + pdev = platform_device_alloc(node->name, -1); + if (!pdev) { + dev_err(apr_priv->dev, + "%s: pdev memory alloc failed for %s\n", + __func__, node->name); + kfree(apr_chld_dev); + continue; + } + pdev->dev.parent = apr_priv->dev; + pdev->dev.of_node = node; + + ret = platform_device_add(pdev); + if (ret) { + dev_err(apr_priv->dev, + "%s: Cannot add platform device %s\n", + __func__, node->name); + platform_device_put(pdev); + kfree(apr_chld_dev); + continue; + } + + apr_chld_dev->pdev = pdev; + + spin_lock(&apr_priv->apr_chld_lock); + list_add_tail(&apr_chld_dev->node, &apr_priv->apr_chlds); + spin_unlock(&apr_priv->apr_chld_lock); + + dev_dbg(apr_priv->dev, "%s: Added APR child dev: %s\n", + __func__, dev_name(&pdev->dev)); + } +} + static void apr_adsp_up(void) { + pr_debug("%s: Q6 is Up\n", __func__); if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_LOADED) == APR_SUBSYS_DOWN) wake_up(&dsp_wait); + + spin_lock(&apr_priv->apr_lock); + if (apr_priv->is_initial_boot) + schedule_work(&apr_priv->add_chld_dev_work); + spin_unlock(&apr_priv->apr_lock); } int apr_wait_for_device_up(int dest_id) @@ -970,21 +1035,25 @@ static int apr_notifier_service_cb(struct notifier_block *this, * recovery notifications during initial boot * up since everything is expected to be down. */ - if (is_initial_boot) { - is_initial_boot = false; + spin_lock(&apr_priv->apr_lock); + if (apr_priv->is_initial_boot) { + spin_unlock(&apr_priv->apr_lock); break; } + spin_unlock(&apr_priv->apr_lock); 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(); + spin_lock(&apr_priv->apr_lock); + apr_priv->is_initial_boot = false; + spin_unlock(&apr_priv->apr_lock); break; default: break; @@ -1003,10 +1072,57 @@ static struct notifier_block modem_service_nb = { .priority = 0, }; -static int __init apr_init(void) +#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; +} +#else +static int __init apr_debug_init(void) +( + return 0; +) +#endif + +static void apr_cleanup(void) +{ + int i, j, k; + + if (apr_reset_workqueue) { + flush_workqueue(apr_reset_workqueue); + destroy_workqueue(apr_reset_workqueue); + } + mutex_destroy(&q6.lock); + for (i = 0; i < APR_DEST_MAX; i++) { + for (j = 0; j < APR_CLIENT_MAX; j++) { + mutex_destroy(&client[i][j].m_lock); + for (k = 0; k < APR_SVC_MAX; k++) + mutex_destroy(&client[i][j].svc[k].m_lock); + } + } + debugfs_remove(debugfs_apr_debug); +} + +static int apr_probe(struct platform_device *pdev) { int i, j, k; + init_waitqueue_head(&dsp_wait); + init_waitqueue_head(&modem_wait); + + apr_priv = devm_kzalloc(&pdev->dev, sizeof(*apr_priv), GFP_KERNEL); + if (!apr_priv) + return -ENOMEM; + + apr_priv->dev = &pdev->dev; + spin_lock_init(&apr_priv->apr_lock); + spin_lock_init(&apr_priv->apr_chld_lock); + INIT_LIST_HEAD(&apr_priv->apr_chlds); + INIT_WORK(&apr_priv->add_chld_dev_work, apr_add_child_devices); + for (i = 0; i < APR_DEST_MAX; i++) for (j = 0; j < APR_CLIENT_MAX; j++) { mutex_init(&client[i][j].m_lock); @@ -1018,42 +1134,60 @@ static int __init apr_init(void) apr_set_subsys_state(); mutex_init(&q6.lock); apr_reset_workqueue = create_singlethread_workqueue("apr_driver"); - if (!apr_reset_workqueue) + if (!apr_reset_workqueue) { + apr_priv = NULL; 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; + spin_lock(&apr_priv->apr_lock); + apr_priv->is_initial_boot = true; + spin_unlock(&apr_priv->apr_lock); subsys_notif_register("apr_adsp", AUDIO_NOTIFIER_ADSP_DOMAIN, &adsp_service_nb); subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN, &modem_service_nb); - return 0; + return apr_debug_init(); } -device_initcall(apr_init); -static int __init apr_late_init(void) +static int apr_remove(struct platform_device *pdev) { - int ret = 0; + struct apr_chld_device *chld, *tmp; + + apr_cleanup(); + spin_lock(&apr_priv->apr_chld_lock); + list_for_each_entry_safe(chld, tmp, &apr_priv->apr_chlds, node) { + platform_device_unregister(chld->pdev); + list_del(&chld->node); + kfree(chld); + } + spin_unlock(&apr_priv->apr_chld_lock); + apr_priv = NULL; + return 0; +} - init_waitqueue_head(&dsp_wait); - init_waitqueue_head(&modem_wait); +static const struct of_device_id apr_machine_of_match[] = { + { .compatible = "qcom,msm-audio-apr", }, + {}, +}; - return ret; -} -late_initcall(apr_late_init); +static struct platform_driver apr_driver = { + .probe = apr_probe, + .remove = apr_remove, + .driver = { + .name = "audio_apr", + .owner = THIS_MODULE, + .of_match_table = apr_machine_of_match, + } +}; -#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 +module_platform_driver(apr_driver); + +MODULE_DESCRIPTION("APR DRIVER"); +MODULE_LICENSE("GPL v2"); +MODULE_DEVICE_TABLE(of, apr_machine_of_match); From 2f99d27c97aff9b0769dce3d92264ead4624861d Mon Sep 17 00:00:00 2001 From: Sudheer Papothi Date: Fri, 6 Apr 2018 00:51:48 +0530 Subject: [PATCH 198/276] ASoC: wsa881x: Avoid query temp during suspend Runtime suspend in slimbus driver makes QMI call which takes wakelock and result in first system suspend to fail. As a result of first suspend fail, POST_PM_SUSPEND event is dispatched to thermal core which registered to pm notifier. WSA being one of registered thermal zone gets query for temperature, and makes slimbus reads/writes which will result in runtime resume of slimbus driver to happen. System suspend fails again continuously in this endless loop as slimbus runtime suspend will make QMI call again. Update wsa temp sensor to handle suspend event by registering to pm notifier and ignore the temperature request from thermal core at resume. This will avoid slimbus reads/writes during suspend in progress and allow XO shutdown to happen. Change-Id: Id13a9701cffb1231ef7d563cbc30756fd71d5868 Signed-off-by: Laxminath Kasam Signed-off-by: Sudheer Papothi --- asoc/codecs/wsa881x-temp-sensor.c | 47 ++++++++++++++++++++++++++++++- asoc/codecs/wsa881x-temp-sensor.h | 6 +++- asoc/codecs/wsa881x.c | 30 +++++++++++++++++++- 3 files changed, 80 insertions(+), 3 deletions(-) diff --git a/asoc/codecs/wsa881x-temp-sensor.c b/asoc/codecs/wsa881x-temp-sensor.c index 5ab0ecfdc022..b2ed963b7e22 100644 --- a/asoc/codecs/wsa881x-temp-sensor.c +++ b/asoc/codecs/wsa881x-temp-sensor.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2015, 2017-2018 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 @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -61,6 +62,20 @@ int wsa881x_get_temp(struct thermal_zone_device *thermal, pr_err("%s: pdata is NULL\n", __func__); return -EINVAL; } + if (atomic_cmpxchg(&pdata->is_suspend_spk, 1, 0)) { + /* + * get_temp query happens as part of POST_PM_SUSPEND + * from thermal core. To avoid calls to slimbus + * as part of this thermal query, return default temp + * and reset the suspend flag. + */ + if (!pdata->t0_init) { + if (temp) + *temp = pdata->curr_temp; + return 0; + } + } + temp_retry: if (pdata->wsa_temp_reg_read) { ret = pdata->wsa_temp_reg_read(codec, ®); @@ -108,6 +123,8 @@ int wsa881x_get_temp(struct thermal_zone_device *thermal, goto temp_retry; } } + pdata->curr_temp = temp_val; + if (temp) *temp = temp_val; pr_debug("%s: t0 measured: %d dmeas = %d, d1 = %d, d2 = %d\n", @@ -120,6 +137,23 @@ static struct thermal_zone_device_ops wsa881x_thermal_ops = { .get_temp = wsa881x_get_temp, }; + +static int wsa881x_pm_notify(struct notifier_block *nb, + unsigned long mode, void *_unused) +{ + struct wsa881x_tz_priv *pdata = + container_of(nb, struct wsa881x_tz_priv, pm_nb); + + switch (mode) { + case PM_SUSPEND_PREPARE: + atomic_set(&pdata->is_suspend_spk, 1); + break; + default: + break; + } + return 0; +} + int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata) { struct thermal_zone_device *tz_dev; @@ -137,12 +171,23 @@ int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata) return -EINVAL; } tz_pdata->tz_dev = tz_dev; + tz_pdata->pm_nb.notifier_call = wsa881x_pm_notify; + register_pm_notifier(&tz_pdata->pm_nb); + atomic_set(&tz_pdata->is_suspend_spk, 0); + return 0; } EXPORT_SYMBOL(wsa881x_init_thermal); void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev) { + struct wsa881x_tz_priv *pdata; + + if (tz_dev && tz_dev->devdata) { + pdata = tz_dev->devdata; + if (pdata) + unregister_pm_notifier(&pdata->pm_nb); + } if (tz_dev) thermal_zone_device_unregister(tz_dev); } diff --git a/asoc/codecs/wsa881x-temp-sensor.h b/asoc/codecs/wsa881x-temp-sensor.h index d6c1eb75e940..26828b716d07 100644 --- a/asoc/codecs/wsa881x-temp-sensor.h +++ b/asoc/codecs/wsa881x-temp-sensor.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015, 2018 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 @@ -31,6 +31,10 @@ struct wsa881x_tz_priv { struct wsa_temp_register *wsa_temp_reg; char name[80]; wsa_temp_register_read wsa_temp_reg_read; + struct notifier_block pm_nb; + atomic_t is_suspend_spk; + int t0_init; + int curr_temp; }; int wsa881x_get_temp(struct thermal_zone_device *tz_dev, int *temp); diff --git a/asoc/codecs/wsa881x.c b/asoc/codecs/wsa881x.c index 0755cdcab8b6..41a186f1936d 100644 --- a/asoc/codecs/wsa881x.c +++ b/asoc/codecs/wsa881x.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, 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 @@ -200,12 +200,40 @@ static int wsa881x_set_mute(struct snd_kcontrol *kcontrol, return 0; } +static int wsa881x_get_t0_init(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + struct wsa881x_tz_priv *pdata = &wsa881x->tz_pdata; + + ucontrol->value.integer.value[0] = pdata->t0_init; + dev_dbg(codec->dev, "%s: t0 init %d\n", __func__, pdata->t0_init); + + return 0; +} + +static int wsa881x_set_t0_init(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec); + struct wsa881x_tz_priv *pdata = &wsa881x->tz_pdata; + + pdata->t0_init = ucontrol->value.integer.value[0]; + dev_dbg(codec->dev, "%s: t0 init %d\n", __func__, pdata->t0_init); + + return 0; +} static const struct snd_kcontrol_new wsa_snd_controls[] = { SOC_ENUM_EXT("WSA PA Gain", wsa_pa_gain_enum, wsa_pa_gain_get, wsa_pa_gain_put), SOC_SINGLE_EXT("WSA PA Mute", SND_SOC_NOPM, 0, 1, 0, wsa881x_get_mute, wsa881x_set_mute), + SOC_SINGLE_EXT("WSA T0 Init", SND_SOC_NOPM, 0, 1, 0, + wsa881x_get_t0_init, wsa881x_set_t0_init), }; static int codec_debug_open(struct inode *inode, struct file *file) From 7e3e04f9f81d2c92a5e4a292dcf6523c0f16b1c4 Mon Sep 17 00:00:00 2001 From: Sachin Mohan Gadag Date: Wed, 2 May 2018 18:24:52 +0530 Subject: [PATCH 199/276] rtac: Reduce RTAC buffer size Currently RTAC buffer size is 163k which is uint32_t max, but dsp expects buffer size of max of uint16_t for get_param_data_V5 api. Hence reduce size to 57344 which is highest unsigned 2byte (0xE000) integer and multiple of 8192. Change-Id: I7e866a537fd39ffd43eb4e4e6b82e3d4dffad74d Signed-off-by: Sachin Mohan Gadag --- dsp/rtac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsp/rtac.c b/dsp/rtac.c index 84ef8355a1c3..774cfb5b2320 100644 --- a/dsp/rtac.c +++ b/dsp/rtac.c @@ -35,7 +35,7 @@ #define MAX_PAYLOAD_SIZE 4076 #define RTAC_MAX_ACTIVE_VOICE_COMBOS 2 #define RTAC_MAX_ACTIVE_POPP 8 -#define RTAC_BUF_SIZE 163840 +#define RTAC_BUF_SIZE 57344 #define TIMEOUT_MS 1000 From 4aed07da6eafca8dac0f3b19aea0922e5d3440fb Mon Sep 17 00:00:00 2001 From: Arun Mirpuri Date: Mon, 30 Apr 2018 11:47:18 -0700 Subject: [PATCH 200/276] dsp: add incall stereo capture Add support for incall stereo capture for voice uplink and downlink capture Change-Id: Id23448990170b8215f547073608cd6a998d479ae Signed-off-by: Arun Mirpuri --- asoc/msm-pcm-voice-v2.c | 44 +++++++++++++++++++++++++++++- dsp/q6voice.c | 59 +++++++++++++++++++++++++++++++++++++---- include/dsp/q6voice.h | 4 +++ 3 files changed, 101 insertions(+), 6 deletions(-) diff --git a/asoc/msm-pcm-voice-v2.c b/asoc/msm-pcm-voice-v2.c index c0968a36d8e8..50f055f7efde 100644 --- a/asoc/msm-pcm-voice-v2.c +++ b/asoc/msm-pcm-voice-v2.c @@ -30,6 +30,9 @@ #include "msm-pcm-voice-v2.h" +#define NUM_CHANNELS_MONO 1 +#define NUM_CHANNELS_STEREO 2 + static struct msm_voice voice_info[VOICE_SESSION_INDEX_MAX]; static struct snd_pcm_hardware msm_pcm_hardware = { @@ -619,6 +622,38 @@ static int msm_voice_topology_disable_put(struct snd_kcontrol *kcontrol, return ret; } +static int msm_voice_rec_config_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = 0; + int voc_rec_config_channels = ucontrol->value.integer.value[0]; + + if (voc_rec_config_channels < NUM_CHANNELS_MONO || + voc_rec_config_channels > NUM_CHANNELS_STEREO) { + pr_err("%s: Invalid channel config (%d)\n", __func__, + voc_rec_config_channels); + ret = -EINVAL; + goto done; + } + voc_set_incall_capture_channel_config(voc_rec_config_channels); + +done: + pr_debug("%s: voc_rec_config_channels = %d, ret = %d\n", __func__, + voc_rec_config_channels, ret); + return ret; +} + +static int msm_voice_rec_config_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + ucontrol->value.integer.value[0] = + voc_get_incall_capture_channel_config(); + pr_debug("%s: rec_config_channels = %ld\n", __func__, + ucontrol->value.integer.value[0]); + return 0; +} + static int msm_voice_cvd_version_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { @@ -681,6 +716,12 @@ static struct snd_kcontrol_new msm_voice_controls[] = { msm_voice_mbd_put), }; +static struct snd_kcontrol_new msm_voice_rec_config_controls[] = { + SOC_SINGLE_MULTI_EXT("Voc Rec Config", SND_SOC_NOPM, 0, + 2, 0, 1, msm_voice_rec_config_get, + msm_voice_rec_config_put), +}; + static const struct snd_pcm_ops msm_pcm_ops = { .open = msm_pcm_open, .hw_params = msm_pcm_hw_params, @@ -706,7 +747,8 @@ 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)); - + snd_soc_add_platform_controls(platform, msm_voice_rec_config_controls, + ARRAY_SIZE(msm_voice_rec_config_controls)); return 0; } diff --git a/dsp/q6voice.c b/dsp/q6voice.c index f05d880b427f..cac734005aec 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -101,7 +101,8 @@ static int voice_get_avcs_version_per_service(uint32_t service_id); 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_start_record(struct voice_data *v, uint32_t rec_mode, + uint32_t port_id); static int voice_cvs_stop_record(struct voice_data *v); static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv); @@ -4372,7 +4373,8 @@ static int voice_setup_vocproc(struct voice_data *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); + voice_cvs_start_record(v, v->rec_info.rec_mode, + v->rec_info.port_id); if (v->dtmf_rx_detect_en) voice_send_dtmf_rx_detection_cmd(v, v->dtmf_rx_detect_en); @@ -5068,7 +5070,8 @@ static int voice_destroy_vocproc(struct voice_data *v) if (v->rec_info.rec_enable) { voice_cvs_start_record( &common.voice[VOC_PATH_PASSIVE], - v->rec_info.rec_mode); + v->rec_info.rec_mode, + v->rec_info.port_id); common.srvcc_rec_flag = true; pr_debug("%s: switch recording, srvcc_rec_flag %d\n", @@ -5574,7 +5577,8 @@ static int voice_send_vol_step_cmd(struct voice_data *v) return 0; } -static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode) +static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode, + uint32_t port_id) { int ret = 0; void *apr_cvs; @@ -5625,6 +5629,18 @@ static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode) VSS_IRECORD_TAP_POINT_STREAM_END; cvs_start_record.rec_mode.tx_tap_point = VSS_IRECORD_TAP_POINT_STREAM_END; + if (common.rec_channel_count == + NUM_CHANNELS_STEREO) { + /* + * if channel count is not stereo, + * then default port_id and mode + * (mono) will be used + */ + cvs_start_record.rec_mode.mode = + VSS_IRECORD_MODE_TX_RX_STEREO; + cvs_start_record.rec_mode.port_id = + port_id; + } } else { pr_err("%s: Invalid in-call rec_mode %d\n", __func__, rec_mode); @@ -5773,6 +5789,7 @@ int voc_start_record(uint32_t port_id, uint32_t set, uint32_t session_id) mutex_lock(&v->lock); rec_mode = v->rec_info.rec_mode; + v->rec_info.port_id = port_id; rec_set = set; if (set) { if ((v->rec_route_state.ul_flag != 0) && @@ -5849,7 +5866,8 @@ int voc_start_record(uint32_t port_id, uint32_t set, uint32_t session_id) if (cvs_handle != 0) { if (rec_set) - ret = voice_cvs_start_record(v, rec_mode); + ret = voice_cvs_start_record(v, rec_mode, + port_id); else ret = voice_cvs_stop_record(v); } @@ -6126,6 +6144,31 @@ int voc_disable_topology(uint32_t session_id, uint32_t disable) return ret; } +/** + * voc_set_incall_capture_channel_config - + * command to set channel count for record + * + * @channel_count: number of channels + * + */ +void voc_set_incall_capture_channel_config(int channel_count) +{ + common.rec_channel_count = channel_count; +} +EXPORT_SYMBOL(voc_set_incall_capture_channel_config); + +/** + * voc_get_incall_capture_channel_config - + * command to get channel count for record + * + * Returns number of channels configured for record + */ +int voc_get_incall_capture_channel_config(void) +{ + return common.rec_channel_count; +} +EXPORT_SYMBOL(voc_get_incall_capture_channel_config); + static int voice_set_packet_exchange_mode_and_config(uint32_t session_id, uint32_t mode) { @@ -9311,6 +9354,12 @@ static int __init voice_init(void) /* Initialize Per-Vocoder Calibration flag */ common.is_per_vocoder_cal_enabled = false; + /* + * Initialize in call record channel config + * to mono + */ + common.rec_channel_count = NUM_CHANNELS_MONO; + mutex_init(&common.common_lock); common.uevent_data = kzalloc(sizeof(*(common.uevent_data)), GFP_KERNEL); diff --git a/include/dsp/q6voice.h b/include/dsp/q6voice.h index 86be4e14eb7a..7515e0f37aa8 100644 --- a/include/dsp/q6voice.h +++ b/include/dsp/q6voice.h @@ -1793,6 +1793,7 @@ struct incall_rec_info { uint32_t rec_enable; uint32_t rec_mode; uint32_t recording; + uint32_t port_id; }; struct incall_music_info { @@ -1980,6 +1981,7 @@ struct common_data { bool sidetone_enable; bool mic_break_enable; struct audio_uevent_data *uevent_data; + int32_t rec_channel_count; }; struct voice_session_itr { @@ -2111,6 +2113,8 @@ int voc_disable_topology(uint32_t session_id, uint32_t disable); int voc_set_device_config(uint32_t session_id, uint8_t path_dir, struct media_format_info *finfo); uint32_t voice_get_topology(uint32_t topology_idx); +void voc_set_incall_capture_channel_config(int channel_count); +int voc_get_incall_capture_channel_config(void); int voice_set_topology_specific_info(struct voice_data *v, uint32_t topology_idx); int voc_set_sound_focus(struct sound_focus_param sound_focus_param); From 55e7ac4c5a76db3bc266a65ed179591061c33ac7 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Fri, 23 Mar 2018 15:07:57 -0700 Subject: [PATCH 201/276] asoc: remove tdm optional properties boot logs Move the tdm device property logs from probe function to prepare function to print them only when tdm usecase is started and not during bootup. Change-Id: I3844709d501bf510620d6f20f270e30c68e78b78 Signed-off-by: Karthikeyan Mani --- asoc/msm-dai-q6-v2.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index d16cad04fb1c..3e344dec619a 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -5357,9 +5357,6 @@ static int msm_dai_tdm_q6_probe(struct platform_device *pdev) 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_tdm_group_ports); @@ -7025,6 +7022,13 @@ static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream, int group_idx = 0; atomic_t *group_ref = NULL; + dev_dbg(dai->dev, "%s: dev_name: %s dev_id: 0x%x group_id: 0x%x\n", + __func__, dev_name(dai->dev), dai->dev->id, group_id); + + if (dai_data->port_cfg.custom_tdm_header.minor_version == 0) + dev_dbg(dai->dev, + "%s: Custom tdm header not supported\n", __func__); + 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", @@ -8793,9 +8797,6 @@ static int msm_dai_q6_tdm_dev_probe(struct platform_device *pdev) } 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) { @@ -8936,8 +8937,6 @@ static int msm_dai_q6_tdm_dev_probe(struct platform_device *pdev) 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; From c9d8cbc17efb0297344c0ef209ae4b57a1e24852 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Thu, 17 May 2018 11:23:27 +0800 Subject: [PATCH 202/276] ASoC: wcd934x: Reset power_active_ref to 0 when it is negative Reset power_active_ref counter to 0 when it is negative to take digital core out of reset during next session. Change-Id: I724ffe6e3547655424238ca02a9bf8ac24b101fe Signed-off-by: Meng Wang --- asoc/codecs/wcd934x/wcd934x.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 988312afdbab..5869ad2ffdab 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -8977,8 +8977,10 @@ static int tavil_dig_core_power_collapse(struct tavil_priv *tavil, goto unlock_mutex; if (tavil->power_active_ref < 0) { - dev_dbg(tavil->dev, "%s: power_active_ref is negative\n", + dev_dbg(tavil->dev, + "%s: power_active_ref is negative, reset it\n", __func__); + tavil->power_active_ref = 0; goto unlock_mutex; } From d969f7e321584033906073d291886411d1f329e5 Mon Sep 17 00:00:00 2001 From: Weiyin Jiang Date: Tue, 1 May 2018 18:23:56 +0800 Subject: [PATCH 203/276] ASoC: don't override channel allocation if it's already set Don't derive channel allocation from channel count if it's already set explicitly from AHAL. In this way, precise CA is reserved. CRs-Fixed: 2246345 Change-Id: I47ab4eb52b78cd90f325b752441d1c53e5cde606 Signed-off-by: Weiyin Jiang --- asoc/msm-dai-q6-hdmi-v2.c | 9 ++++-- include/dsp/apr_audio-v2.h | 60 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/asoc/msm-dai-q6-hdmi-v2.c b/asoc/msm-dai-q6-hdmi-v2.c index 212c4e3274fb..8ca8e08660ec 100644 --- a/asoc/msm-dai-q6-hdmi-v2.c +++ b/asoc/msm-dai-q6-hdmi-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -243,7 +243,12 @@ static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream, dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x12; break; case 8: - dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x13; + if (dai_data->ca.set_ca == false) { + dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x13; + } else { + dai_data->port_config.hdmi_multi_ch.channel_allocation = + dai_data->ca.ca; + } break; default: dev_err(dai->dev, "invalid Channels = %u\n", diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index 9b67afd16674..e1efcf0df312 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -4281,6 +4281,66 @@ struct asm_softvolume_params { /* Rear right of center. */ #define PCM_CHANNEL_RRC 16 +/* Secondary low frequency effect channel. */ +#define PCM_CHANNEL_LFE2 17 + +/* Side left channel. */ +#define PCM_CHANNEL_SL 18 + +/* Side right channel. */ +#define PCM_CHANNEL_SR 19 + +/* Top front left channel. */ +#define PCM_CHANNEL_TFL 20 + +/* Left vertical height channel. */ +#define PCM_CHANNEL_LVH PCM_CHANNEL_TFL + +/* Top front right channel. */ +#define PCM_CHANNEL_TFR 21 + +/* Right vertical height channel. */ +#define PCM_CHANNEL_RVH PCM_CHANNEL_TFR + +/* Top center channel. */ +#define PCM_CHANNEL_TC 22 + +/* Top back left channel. */ +#define PCM_CHANNEL_TBL 23 + +/* Top back right channel. */ +#define PCM_CHANNEL_TBR 24 + +/* Top side left channel. */ +#define PCM_CHANNEL_TSL 25 + +/* Top side right channel. */ +#define PCM_CHANNEL_TSR 26 + +/* Top back center channel. */ +#define PCM_CHANNEL_TBC 27 + +/* Bottom front center channel. */ +#define PCM_CHANNEL_BFC 28 + +/* Bottom front left channel. */ +#define PCM_CHANNEL_BFL 29 + +/* Bottom front right channel. */ +#define PCM_CHANNEL_BFR 30 + +/* Left wide channel. */ +#define PCM_CHANNEL_LW 31 + +/* Right wide channel. */ +#define PCM_CHANNEL_RW 32 + +/* Left side direct channel. */ +#define PCM_CHANNEL_LSD 33 + +/* Right side direct channel. */ +#define PCM_CHANNEL_RSD 34 + #define PCM_FORMAT_MAX_NUM_CHANNEL 8 #define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5 From 9890fdd78fa4aaa3b58cdaac6191afbf5b4f83d6 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Thu, 10 May 2018 14:20:18 +0530 Subject: [PATCH 204/276] ASoC: wcd: Remove non-essential current source votings from adc Zdet current source is enabled for 4 pole aux cable and kept even after plug removal. This increases voltage for both hph left and right which causes error in cross-connection detection for subsequent plug detections. Remove current source enablement from unrequired places. CRs-Fixed: 2234559 Change-Id: Icffbca88f7a6873c509c91e31b494dd6d5d21cb9 Signed-off-by: Vatsal Bucha --- asoc/codecs/wcd-mbhc-v2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wcd-mbhc-v2.c b/asoc/codecs/wcd-mbhc-v2.c index 3ba89503b60f..806abd46e39a 100644 --- a/asoc/codecs/wcd-mbhc-v2.c +++ b/asoc/codecs/wcd-mbhc-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -939,6 +939,8 @@ static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc) jack_type = SND_JACK_HEADSET; break; case MBHC_PLUG_TYPE_HIGH_HPH: + if (mbhc->mbhc_detection_logic == WCD_DETECTION_ADC) + WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_ISRC_EN, 0); mbhc->is_extn_cable = false; jack_type = SND_JACK_LINEOUT; break; From 46f961cee40f097c9240cedaea6a2a83e7f1056e Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Tue, 29 May 2018 17:03:55 -0700 Subject: [PATCH 205/276] asoc: wcd-spi: use CMA memory for TX and RX buffer instead of kzalloc In wcd-spi driver, the kzalloc is used to allocate memory for TX and RX buffer. Those allocations are order4 or order5, which could fail on some kernels. To avoid potential memory allocation failure, the CMA region is used for those allocations by dma_alloc_coherent API. Change-Id: I0a4d6c6b5e0e47f96187563272e63c3a006e469f Signed-off-by: Xiaoyu Ye --- asoc/codecs/wcd-spi.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/asoc/codecs/wcd-spi.c b/asoc/codecs/wcd-spi.c index 6b451ff6504e..20b358e0fd30 100644 --- a/asoc/codecs/wcd-spi.c +++ b/asoc/codecs/wcd-spi.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -165,6 +166,10 @@ struct wcd_spi_priv { /* Handle to child (qmi client) device */ struct device *ac_dev; + + /* DMA handles for transfer buffers */ + dma_addr_t tx_dma; + dma_addr_t rx_dma; }; enum xfer_request { @@ -1445,17 +1450,20 @@ static int wcd_spi_component_bind(struct device *dev, spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2); /* Pre-allocate the buffers */ - wcd_spi->tx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE, - GFP_KERNEL | GFP_DMA); + wcd_spi->tx_buf = dma_zalloc_coherent(&spi->dev, + WCD_SPI_RW_MAX_BUF_SIZE, + &wcd_spi->tx_dma, GFP_KERNEL); if (!wcd_spi->tx_buf) { ret = -ENOMEM; goto done; } - wcd_spi->rx_buf = kzalloc(WCD_SPI_RW_MAX_BUF_SIZE, - GFP_KERNEL | GFP_DMA); + wcd_spi->rx_buf = dma_zalloc_coherent(&spi->dev, + WCD_SPI_RW_MAX_BUF_SIZE, + &wcd_spi->rx_dma, GFP_KERNEL); if (!wcd_spi->rx_buf) { - kfree(wcd_spi->tx_buf); + dma_free_coherent(&spi->dev, WCD_SPI_RW_MAX_BUF_SIZE, + wcd_spi->tx_buf, wcd_spi->tx_dma); wcd_spi->tx_buf = NULL; ret = -ENOMEM; goto done; @@ -1482,8 +1490,10 @@ static void wcd_spi_component_unbind(struct device *dev, spi_transfer_del(&wcd_spi->xfer2[0]); spi_transfer_del(&wcd_spi->xfer2[1]); - kfree(wcd_spi->tx_buf); - kfree(wcd_spi->rx_buf); + dma_free_coherent(&spi->dev, WCD_SPI_RW_MAX_BUF_SIZE, + wcd_spi->tx_buf, wcd_spi->tx_dma); + dma_free_coherent(&spi->dev, WCD_SPI_RW_MAX_BUF_SIZE, + wcd_spi->rx_buf, wcd_spi->rx_dma); wcd_spi->tx_buf = NULL; wcd_spi->rx_buf = NULL; } @@ -1519,6 +1529,7 @@ static int wcd_spi_probe(struct spi_device *spi) mutex_init(&wcd_spi->xfer_mutex); INIT_DELAYED_WORK(&wcd_spi->clk_dwork, wcd_spi_clk_work); init_completion(&wcd_spi->resume_comp); + arch_setup_dma_ops(&spi->dev, 0, 0, NULL, true); wcd_spi->spi = spi; spi_set_drvdata(spi, wcd_spi); From 4f6d3a17121d2c9b1b339423846e1cfa92892619 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Fri, 23 Mar 2018 08:57:33 +0800 Subject: [PATCH 206/276] ASoC: msm: check payload size before memory allocation Buffer from mixer ctl or ADSP is composed of payload size and actual payload. On a 32 bit platform, we could have an overflow if payload size is below UINT_MAX while payload size + sizeof(struct) is over UINT_MAX. Allocated memory size would be less than expected. Check payload size against limit before memory allocation. Change-Id: I0bf19ca7b8c93083177a21ad726122dc20f45551 Signed-off-by: Xiaojun Sang --- asoc/msm-compress-q6-v2.c | 4 ++-- asoc/msm-pcm-q6-v2.c | 4 ++-- asoc/msm-qti-pp-config.c | 5 +++-- asoc/msm-transcode-loopback-q6-v2.c | 4 ++-- dsp/q6asm.c | 7 +++++++ 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index d36b50af62a9..72c02bc4c456 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -3688,8 +3688,8 @@ static int msm_compr_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, goto done; } - if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= - sizeof(ucontrol->value.bytes.data)) { + if (event_data->payload_len > sizeof(ucontrol->value.bytes.data) + - sizeof(struct msm_adsp_event_data)) { pr_err("%s param length=%d exceeds limit", __func__, event_data->payload_len); ret = -EINVAL; diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 59258c2e010b..e112dc82464b 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -1152,8 +1152,8 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, goto done; } - if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= - sizeof(ucontrol->value.bytes.data)) { + if (event_data->payload_len > sizeof(ucontrol->value.bytes.data) + - sizeof(struct msm_adsp_event_data)) { pr_err("%s param length=%d exceeds limit", __func__, event_data->payload_len); ret = -EINVAL; diff --git a/asoc/msm-qti-pp-config.c b/asoc/msm-qti-pp-config.c index 00a29d71a55b..f5f9ee1ae952 100644 --- a/asoc/msm-qti-pp-config.c +++ b/asoc/msm-qti-pp-config.c @@ -999,8 +999,9 @@ int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, event_data = (struct msm_adsp_event_data *)payload; kctl->info(kctl, &kctl_info); - if (sizeof(struct msm_adsp_event_data) - + event_data->payload_len > kctl_info.count) { + + if (event_data->payload_len > + kctl_info.count - sizeof(struct msm_adsp_event_data)) { pr_err("%s: payload length exceeds limit of %u bytes.\n", __func__, kctl_info.count); ret = -EINVAL; diff --git a/asoc/msm-transcode-loopback-q6-v2.c b/asoc/msm-transcode-loopback-q6-v2.c index 3932a18434d2..0d65596c4cac 100644 --- a/asoc/msm-transcode-loopback-q6-v2.c +++ b/asoc/msm-transcode-loopback-q6-v2.c @@ -549,8 +549,8 @@ static int msm_transcode_stream_cmd_put(struct snd_kcontrol *kcontrol, goto done; } - if ((sizeof(struct msm_adsp_event_data) + event_data->payload_len) >= - sizeof(ucontrol->value.bytes.data)) { + if (event_data->payload_len > sizeof(ucontrol->value.bytes.data) + - sizeof(struct msm_adsp_event_data)) { pr_err("%s param length=%d exceeds limit", __func__, event_data->payload_len); ret = -EINVAL; diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 24246ec3d966..3b7809c714e7 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -2181,6 +2181,13 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) * package is composed of event type + size + actual payload */ payload_size = data->payload_size; + if (payload_size > UINT_MAX - sizeof(struct msm_adsp_event_data)) { + pr_err("%s: payload size = %d exceeds limit.\n", + __func__, payload_size); + spin_unlock(&(session[session_id].session_lock)); + return -EINVAL; + } + pp_event_package = kzalloc(payload_size + sizeof(struct msm_adsp_event_data), GFP_ATOMIC); From 4aca34eb59de5f834c1ee71725fe0868198f1b6a Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Wed, 6 Jun 2018 11:29:56 +0800 Subject: [PATCH 207/276] ipc: Implement FIFO queue to fix sequence inconsistency The SVA history buffer is out of order if there are more than 2 continuous RX buffer done from GLINK. Implement FIFO to ensure sequence consistency. Change-Id: If70e2d0160e8f3140d621298b0db03bd89ba88ba Signed-off-by: Xiaojun Sang --- ipc/wcd-dsp-glink.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/ipc/wcd-dsp-glink.c b/ipc/wcd-dsp-glink.c index cab7c3b7c4a2..adcab5bd1596 100644 --- a/ipc/wcd-dsp-glink.c +++ b/ipc/wcd-dsp-glink.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, 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 @@ -106,6 +106,8 @@ struct wdsp_glink_priv { /* Respone buffer related */ u8 rsp_cnt; struct wdsp_glink_rsp_que rsp[RESP_QUEUE_SIZE]; + u8 write_idx; + u8 read_idx; struct completion rsp_complete; struct mutex rsp_mutex; @@ -199,13 +201,19 @@ static void wdsp_glink_notify_rx(void *handle, const void *priv, mutex_lock(&wpriv->rsp_mutex); rsp_cnt = wpriv->rsp_cnt; if (rsp_cnt >= RESP_QUEUE_SIZE) { - dev_err(wpriv->dev, "%s: Resp Queue is Full\n", __func__); - rsp_cnt = 0; + dev_err(wpriv->dev, "%s: Resp Queue is Full. Ignore latest and keep oldest.\n", + __func__); + mutex_unlock(&wpriv->rsp_mutex); + glink_rx_done(handle, ptr, true); + return; } - dev_dbg(wpriv->dev, "%s: copy into buffer %d\n", __func__, rsp_cnt); + dev_dbg(wpriv->dev, "%s: rsp_cnt = %d copy into buffer %d\n", + __func__, rsp_cnt, wpriv->write_idx); + + memcpy(wpriv->rsp[wpriv->write_idx].buf, rx_buf, size); + wpriv->rsp[wpriv->write_idx].buf_size = size; - memcpy(wpriv->rsp[rsp_cnt].buf, rx_buf, size); - wpriv->rsp[rsp_cnt].buf_size = size; + wpriv->write_idx = (wpriv->write_idx + 1) % RESP_QUEUE_SIZE; wpriv->rsp_cnt = ++rsp_cnt; mutex_unlock(&wpriv->rsp_mutex); @@ -777,10 +785,11 @@ static ssize_t wdsp_glink_read(struct file *file, char __user *buf, mutex_lock(&wpriv->rsp_mutex); if (wpriv->rsp_cnt) { wpriv->rsp_cnt--; - dev_dbg(wpriv->dev, "%s: read from buffer %d\n", - __func__, wpriv->rsp_cnt); + dev_dbg(wpriv->dev, "%s: rsp_cnt=%d read from buffer %d\n", + __func__, wpriv->rsp_cnt, wpriv->read_idx); - rsp = &wpriv->rsp[wpriv->rsp_cnt]; + rsp = &wpriv->rsp[wpriv->read_idx]; + wpriv->read_idx = (wpriv->read_idx + 1) % RESP_QUEUE_SIZE; if (count < rsp->buf_size) { ret1 = copy_to_user(buf, &rsp->buf, count); /* Return the number of bytes copied */ From 68628dbd46466b19fd6c3ffa43e37dca79fe2605 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Tue, 24 Apr 2018 15:21:49 +0800 Subject: [PATCH 208/276] ipc: apr: use of_platform_populate to add child node of apr Use of_platform_populate to add child node of apr. Change-Id: I9f0ded5e39deaf37c79ecda4d960d64c9c14eec9 Signed-off-by: Meng Wang --- ipc/apr.c | 57 +++++++------------------------------------------------ 1 file changed, 7 insertions(+), 50 deletions(-) diff --git a/ipc/apr.c b/ipc/apr.c index f9caca741a8d..a15f937dc6d2 100644 --- a/ipc/apr.c +++ b/ipc/apr.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -62,8 +63,6 @@ struct apr_private { spinlock_t apr_lock; bool is_initial_boot; struct work_struct add_chld_dev_work; - spinlock_t apr_chld_lock; - struct list_head apr_chlds; }; static struct apr_private *apr_priv; @@ -284,44 +283,12 @@ static void apr_adsp_down(unsigned long opcode) static void apr_add_child_devices(struct work_struct *work) { int ret; - struct device_node *node; - struct platform_device *pdev; - struct apr_chld_device *apr_chld_dev; - - for_each_child_of_node(apr_priv->dev->of_node, node) { - apr_chld_dev = kzalloc(sizeof(*apr_chld_dev), GFP_KERNEL); - if (!apr_chld_dev) - continue; - pdev = platform_device_alloc(node->name, -1); - if (!pdev) { - dev_err(apr_priv->dev, - "%s: pdev memory alloc failed for %s\n", - __func__, node->name); - kfree(apr_chld_dev); - continue; - } - pdev->dev.parent = apr_priv->dev; - pdev->dev.of_node = node; - - ret = platform_device_add(pdev); - if (ret) { - dev_err(apr_priv->dev, - "%s: Cannot add platform device %s\n", - __func__, node->name); - platform_device_put(pdev); - kfree(apr_chld_dev); - continue; - } - - apr_chld_dev->pdev = pdev; - spin_lock(&apr_priv->apr_chld_lock); - list_add_tail(&apr_chld_dev->node, &apr_priv->apr_chlds); - spin_unlock(&apr_priv->apr_chld_lock); - - dev_dbg(apr_priv->dev, "%s: Added APR child dev: %s\n", - __func__, dev_name(&pdev->dev)); - } + ret = of_platform_populate(apr_priv->dev->of_node, + NULL, NULL, apr_priv->dev); + if (ret) + dev_dbg(apr_priv->dev, "%s: failed to add child nodes, ret=%d\n", + __func__, ret); } static void apr_adsp_up(void) @@ -1091,6 +1058,7 @@ static void apr_cleanup(void) { int i, j, k; + of_platform_depopulate(apr_priv->dev); if (apr_reset_workqueue) { flush_workqueue(apr_reset_workqueue); destroy_workqueue(apr_reset_workqueue); @@ -1119,8 +1087,6 @@ static int apr_probe(struct platform_device *pdev) apr_priv->dev = &pdev->dev; spin_lock_init(&apr_priv->apr_lock); - spin_lock_init(&apr_priv->apr_chld_lock); - INIT_LIST_HEAD(&apr_priv->apr_chlds); INIT_WORK(&apr_priv->add_chld_dev_work, apr_add_child_devices); for (i = 0; i < APR_DEST_MAX; i++) @@ -1157,16 +1123,7 @@ static int apr_probe(struct platform_device *pdev) static int apr_remove(struct platform_device *pdev) { - struct apr_chld_device *chld, *tmp; - apr_cleanup(); - spin_lock(&apr_priv->apr_chld_lock); - list_for_each_entry_safe(chld, tmp, &apr_priv->apr_chlds, node) { - platform_device_unregister(chld->pdev); - list_del(&chld->node); - kfree(chld); - } - spin_unlock(&apr_priv->apr_chld_lock); apr_priv = NULL; return 0; } From 2fe48bfe195246fa51597ebd775b091c91fc1c37 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Wed, 23 May 2018 17:14:31 +0800 Subject: [PATCH 209/276] ASoC: msm: remove unnecessary error log in audio kernel PP event queue is not used by capture path. Initialize PP event queue for playback path only to remove unnecessary log. Change log level from error to debug when no matching afe cal block is found. Change-Id: Ibc323449f2fdc74cccd350ae30ac76d937898ffa Signed-off-by: Xiaojun Sang --- asoc/msm-pcm-q6-v2.c | 5 ++++- dsp/q6afe.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index e112dc82464b..0b6f2d46f11f 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -704,7 +704,10 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) prtd->set_channel_map = false; prtd->reset_event = false; runtime->private_data = prtd; - msm_adsp_init_mixer_ctl_pp_event_queue(soc_prtd); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + msm_adsp_init_mixer_ctl_pp_event_queue(soc_prtd); + /* Vote to update the Rx thread priority to RT Thread for playback */ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) && (pdata->perf_mode == LOW_LATENCY_PCM_MODE)) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 6dc42047c146..46664c88a7e2 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -1558,7 +1558,7 @@ static struct cal_block_data *afe_find_cal(int cal_index, int port_id) goto exit; } } - pr_err("%s: no matching cal_block found\n", __func__); + pr_debug("%s: no matching cal_block found\n", __func__); cal_block = NULL; exit: From f9f9d207f8746a896e545e6c90ceb2df6007d88a Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Mon, 12 Feb 2018 19:03:02 +0800 Subject: [PATCH 210/276] asoc: codecs: set boost state to MAX_STATE_2 for WSA8815 WSA8815 requires default boost state of MAX_STATE_2, when speaker protection feature is disabled. Set boost state to MAX_STATE_2 for WSA8815 in all scenarios. Change-Id: Ic700d9a206e94a4cdd1d99f8547bed9df1e759e8 Signed-off-by: Xiaojun Sang --- asoc/codecs/msm_sdw/msm_sdw_cdc.c | 10 +++++----- asoc/codecs/wcd9335.c | 10 +++++----- asoc/codecs/wcd934x/wcd934x.c | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/asoc/codecs/msm_sdw/msm_sdw_cdc.c b/asoc/codecs/msm_sdw/msm_sdw_cdc.c index 7a5ba8f0c7c4..46223b1a3686 100644 --- a/asoc/codecs/msm_sdw/msm_sdw_cdc.c +++ b/asoc/codecs/msm_sdw/msm_sdw_cdc.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, 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 @@ -82,8 +82,8 @@ static const struct msm_sdw_reg_mask_val msm_sdw_spkr_default[] = { {MSM_SDW_COMPANDER8_CTL3, 0x80, 0x80}, {MSM_SDW_COMPANDER7_CTL7, 0x01, 0x01}, {MSM_SDW_COMPANDER8_CTL7, 0x01, 0x01}, - {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x50}, - {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x50}, + {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x58}, }; static const struct msm_sdw_reg_mask_val msm_sdw_spkr_mode1[] = { @@ -1699,8 +1699,8 @@ static const struct msm_sdw_reg_mask_val msm_sdw_reg_init[] = { {MSM_SDW_BOOST1_BOOST_CFG1, 0x3F, 0x12}, {MSM_SDW_BOOST1_BOOST_CFG2, 0x1C, 0x08}, {MSM_SDW_COMPANDER8_CTL7, 0x1E, 0x18}, - {MSM_SDW_BOOST0_BOOST_CTL, 0x70, 0x50}, - {MSM_SDW_BOOST1_BOOST_CTL, 0x70, 0x50}, + {MSM_SDW_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {MSM_SDW_BOOST1_BOOST_CTL, 0x7C, 0x58}, {MSM_SDW_RX7_RX_PATH_CFG1, 0x08, 0x08}, {MSM_SDW_RX8_RX_PATH_CFG1, 0x08, 0x08}, {MSM_SDW_TOP_TOP_CFG1, 0x02, 0x02}, diff --git a/asoc/codecs/wcd9335.c b/asoc/codecs/wcd9335.c index 7bc2272164a7..03958f39c3c7 100644 --- a/asoc/codecs/wcd9335.c +++ b/asoc/codecs/wcd9335.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, 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 @@ -845,8 +845,8 @@ static const struct tasha_reg_mask_val tasha_spkr_default[] = { {WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80}, {WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01}, {WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01}, - {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50}, - {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x58}, }; static const struct tasha_reg_mask_val tasha_spkr_mode1[] = { @@ -12405,8 +12405,8 @@ static const struct tasha_reg_mask_val tasha_codec_reg_init_common_val[] = { {WCD9335_CDC_CLSH_K2_MSB, 0x0F, 0x00}, {WCD9335_CDC_CLSH_K2_LSB, 0xFF, 0x60}, {WCD9335_CPE_SS_DMIC_CFG, 0x80, 0x00}, - {WCD9335_CDC_BOOST0_BOOST_CTL, 0x70, 0x50}, - {WCD9335_CDC_BOOST1_BOOST_CTL, 0x70, 0x50}, + {WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x58}, {WCD9335_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08}, {WCD9335_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08}, {WCD9335_ANA_LO_1_2, 0x3C, 0X3C}, diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 5869ad2ffdab..0ae228701466 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -643,8 +643,8 @@ static const struct tavil_reg_mask_val tavil_spkr_default[] = { {WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80}, {WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01}, {WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01}, - {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50}, - {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50}, + {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x58}, }; static const struct tavil_reg_mask_val tavil_spkr_mode1[] = { @@ -9407,8 +9407,8 @@ static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = { {WCD934X_CDC_CLSH_K2_MSB, 0x0F, 0x00}, {WCD934X_CDC_CLSH_K2_LSB, 0xFF, 0x60}, {WCD934X_CPE_SS_DMIC_CFG, 0x80, 0x00}, - {WCD934X_CDC_BOOST0_BOOST_CTL, 0x70, 0x50}, - {WCD934X_CDC_BOOST1_BOOST_CTL, 0x70, 0x50}, + {WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x58}, + {WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x58}, {WCD934X_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08}, {WCD934X_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08}, {WCD934X_CDC_TOP_TOP_CFG1, 0x02, 0x02}, From 2b2d9acc25d0011b1f6bee9306486b139dca281b Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Fri, 27 Apr 2018 14:57:33 +0800 Subject: [PATCH 211/276] ASoC: q6asm: use session lock to avoid use-after-free It is possible that audio session free and param set happens simultaneously. Audio client might be freed while param set is doing wait_event_timeout. Use session lock to make sure client_free happens after param set. Change-Id: I0947c309c8e445fa2e220680e329d88bd5fe2418 Signed-off-by: Xiaojun Sang --- dsp/q6asm.c | 236 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 196 insertions(+), 40 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 3b7809c714e7..5029e7c98c8f 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -95,6 +95,7 @@ static struct asm_mmap this_mmap; struct audio_session { struct audio_client *ac; spinlock_t session_lock; + struct mutex mutex_lock_per_session; }; /* session id: 0 reserved */ static struct audio_session session[ASM_ACTIVE_STREAMS_ALLOWED + 1]; @@ -561,6 +562,8 @@ static int q6asm_get_session_id_from_audio_client(struct audio_client *ac) if (session[n].ac == ac) return n; } + pr_err("%s: cannot find matching audio client. ac = %pK\n", + __func__, ac); return 0; } @@ -576,6 +579,7 @@ static void q6asm_session_free(struct audio_client *ac) pr_debug("%s: sessionid[%d]\n", __func__, ac->session); session_id = ac->session; + mutex_lock(&session[session_id].mutex_lock_per_session); rtac_remove_popp_from_adm_devices(ac->session); spin_lock_irqsave(&(session[session_id].session_lock), flags); session[ac->session].ac = NULL; @@ -587,6 +591,7 @@ static void q6asm_session_free(struct audio_client *ac) kfree(ac); ac = NULL; spin_unlock_irqrestore(&(session[session_id].session_lock), flags); + mutex_unlock(&session[session_id].mutex_lock_per_session); } static uint32_t q6asm_get_next_buf(struct audio_client *ac, @@ -1155,7 +1160,7 @@ int q6asm_send_stream_cmd(struct audio_client *ac, { char *asm_params = NULL; struct apr_hdr hdr; - int rc; + int rc, session_id = 0; uint32_t sz = 0; uint64_t actual_sz = 0; @@ -1166,6 +1171,12 @@ int q6asm_send_stream_cmd(struct audio_client *ac, goto done; } + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) { + rc = -EINVAL; + goto done; + } + if (data->event_type >= ARRAY_SIZE(adsp_reg_event_opcode)) { pr_err("%s: event %u out of boundary of array size of (%lu)\n", __func__, data->event_type, @@ -1189,6 +1200,12 @@ int q6asm_send_stream_cmd(struct audio_client *ac, goto done; } + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { + rc = -EINVAL; + goto fail_send_param; + } + q6asm_add_hdr_async(ac, &hdr, sz, TRUE); atomic_set(&ac->cmd_state_pp, -1); hdr.opcode = adsp_reg_event_opcode[data->event_type]; @@ -1221,6 +1238,7 @@ int q6asm_send_stream_cmd(struct audio_client *ac, rc = 0; fail_send_param: + mutex_unlock(&session[session_id].mutex_lock_per_session); kfree(asm_params); done: return rc; @@ -6845,12 +6863,26 @@ 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; + int session_id = 0; if (ac == NULL) { pr_err("%s: APR handle NULL\n", __func__); + rc = -EINVAL; + goto done; + } + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) { + rc = -EINVAL; + goto done; + } + + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { rc = -EINVAL; goto fail_cmd; } + if (ac->apr == NULL) { pr_err("%s: AC APR handle NULL\n", __func__); rc = -EINVAL; @@ -6904,6 +6936,8 @@ int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain) } rc = 0; fail_cmd: + mutex_unlock(&session[session_id].mutex_lock_per_session); +done: return rc; } @@ -6921,7 +6955,7 @@ int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels, struct asm_volume_ctrl_multichannel_gain multich_gain; int sz = 0; int rc = 0; - int i; + int i, session_id = 0; u8 default_chmap[VOLUME_CONTROL_MAX_CHANNELS]; if (ac == NULL) { @@ -6929,30 +6963,44 @@ int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels, rc = -EINVAL; goto done; } + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) { + rc = -EINVAL; + goto done; + } + + memset(&multich_gain, 0, sizeof(multich_gain)); + sz = sizeof(struct asm_volume_ctrl_multichannel_gain); + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { + rc = -EINVAL; + goto fail_cmd; + } + if (ac->apr == NULL) { dev_err(ac->dev, "%s: AC APR handle NULL\n", __func__); rc = -EINVAL; - goto done; + goto fail_cmd; } + if (gains == NULL) { dev_err(ac->dev, "%s: gain_list is NULL\n", __func__); rc = -EINVAL; - goto done; + goto fail_cmd; } if (channels > VOLUME_CONTROL_MAX_CHANNELS) { dev_err(ac->dev, "%s: Invalid channel count %d\n", __func__, channels); rc = -EINVAL; - goto done; + goto fail_cmd; } if (!use_default && ch_map == NULL) { dev_err(ac->dev, "%s: NULL channel map\n", __func__); rc = -EINVAL; - goto done; + goto fail_cmd; } - 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_pp, -1); multich_gain.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; @@ -6970,7 +7018,7 @@ int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels, if (use_default) { rc = q6asm_map_channels(default_chmap, channels, false); if (rc < 0) - goto done; + goto fail_cmd; for (i = 0; i < channels; i++) { multich_gain.gain_data[i].channeltype = default_chmap[i]; @@ -6988,7 +7036,7 @@ int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels, 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; + goto fail_cmd; } rc = wait_event_timeout(ac->cmd_wait, @@ -6997,16 +7045,18 @@ int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels, pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__, multich_gain.data.param_id); rc = -EINVAL; - goto done; + goto fail_cmd; } if (atomic_read(&ac->cmd_state_pp) > 0) { pr_err("%s: DSP returned error[%d] , set-params paramid[0x%x]\n", __func__, atomic_read(&ac->cmd_state_pp), multich_gain.data.param_id); rc = -EINVAL; - goto done; + goto fail_cmd; } rc = 0; +fail_cmd: + mutex_unlock(&session[session_id].mutex_lock_per_session); done: return rc; } @@ -7016,12 +7066,26 @@ int q6asm_set_mute(struct audio_client *ac, int muteflag) struct asm_volume_ctrl_mute_config mute; int sz = 0; int rc = 0; + int session_id = 0; if (ac == NULL) { pr_err("%s: APR handle NULL\n", __func__); + rc = -EINVAL; + goto done; + } + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) { + rc = -EINVAL; + goto done; + } + + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { rc = -EINVAL; goto fail_cmd; } + if (ac->apr == NULL) { pr_err("%s: AC APR handle NULL\n", __func__); rc = -EINVAL; @@ -7070,6 +7134,8 @@ int q6asm_set_mute(struct audio_client *ac, int muteflag) } rc = 0; fail_cmd: + mutex_unlock(&session[session_id].mutex_lock_per_session); +done: return rc; } @@ -7078,17 +7144,18 @@ 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; + int module_id, session_id = 0; if (ac == NULL) { pr_err("%s: APR handle NULL\n", __func__); rc = -EINVAL; - goto fail_cmd; + goto done; } - if (ac->apr == NULL) { - pr_err("%s: AC APR handle NULL\n", __func__); + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) { rc = -EINVAL; - goto fail_cmd; + goto done; } switch (instance) { @@ -7102,6 +7169,18 @@ static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance) } sz = sizeof(struct asm_volume_ctrl_master_gain); + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { + rc = -EINVAL; + goto fail_cmd; + } + + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + q6asm_add_hdr_async(ac, &vol.hdr, sz, TRUE); atomic_set(&ac->cmd_state_pp, -1); vol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; @@ -7144,6 +7223,8 @@ static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance) rc = 0; fail_cmd: + mutex_unlock(&session[session_id].mutex_lock_per_session); +done: return rc; } @@ -7376,12 +7457,26 @@ int q6asm_set_softpause(struct audio_client *ac, struct asm_soft_pause_params softpause; int sz = 0; int rc = 0; + int session_id = 0; if (ac == NULL) { pr_err("%s: APR handle NULL\n", __func__); + rc = -EINVAL; + goto done; + } + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) { + rc = -EINVAL; + goto done; + } + + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { rc = -EINVAL; goto fail_cmd; } + if (ac->apr == NULL) { pr_err("%s: AC APR handle NULL\n", __func__); rc = -EINVAL; @@ -7435,6 +7530,8 @@ int q6asm_set_softpause(struct audio_client *ac, } rc = 0; fail_cmd: + mutex_unlock(&session[session_id].mutex_lock_per_session); +done: return rc; } @@ -7445,17 +7542,18 @@ static int __q6asm_set_softvolume(struct audio_client *ac, struct asm_soft_step_volume_params softvol; int sz = 0; int rc = 0; - int module_id; + int module_id, session_id; if (ac == NULL) { pr_err("%s: APR handle NULL\n", __func__); rc = -EINVAL; - goto fail_cmd; + goto done; } - if (ac->apr == NULL) { - pr_err("%s: AC APR handle NULL\n", __func__); + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) { rc = -EINVAL; - goto fail_cmd; + goto done; } switch (instance) { @@ -7469,6 +7567,18 @@ static int __q6asm_set_softvolume(struct audio_client *ac, } sz = sizeof(struct asm_soft_step_volume_params); + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { + rc = -EINVAL; + goto fail_cmd; + } + + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_cmd; + } + q6asm_add_hdr_async(ac, &softvol.hdr, sz, TRUE); atomic_set(&ac->cmd_state_pp, -1); softvol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2; @@ -7513,6 +7623,8 @@ static int __q6asm_set_softvolume(struct audio_client *ac, } rc = 0; fail_cmd: + mutex_unlock(&session[session_id].mutex_lock_per_session); +done: return rc; } @@ -7537,12 +7649,26 @@ int q6asm_equalizer(struct audio_client *ac, void *eq_p) int i = 0; int sz = 0; int rc = 0; + int session_id = 0; if (ac == NULL) { pr_err("%s: APR handle NULL\n", __func__); + rc = -EINVAL; + goto done; + } + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) { + rc = -EINVAL; + goto done; + } + + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { rc = -EINVAL; goto fail_cmd; } + if (ac->apr == NULL) { pr_err("%s: AC APR handle NULL\n", __func__); rc = -EINVAL; @@ -7620,6 +7746,8 @@ int q6asm_equalizer(struct audio_client *ac, void *eq_p) } rc = 0; fail_cmd: + mutex_unlock(&session[session_id].mutex_lock_per_session); +done: return rc; } @@ -8210,17 +8338,18 @@ int q6asm_send_audio_effects_params(struct audio_client *ac, char *params, char *asm_params = NULL; struct apr_hdr hdr; struct asm_stream_cmd_set_pp_params_v2 payload_params; - int sz, rc; + int sz, rc, session_id = 0; 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__); + + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) return -EINVAL; - } + if (params == NULL) { pr_err("%s: params NULL\n", __func__); return -EINVAL; @@ -8233,6 +8362,18 @@ int q6asm_send_audio_effects_params(struct audio_client *ac, char *params, pr_err("%s, asm params memory alloc failed", __func__); return -ENOMEM; } + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { + rc = -EINVAL; + goto fail_send_param; + } + + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + rc = -EINVAL; + goto fail_send_param; + } + q6asm_add_hdr_async(ac, &hdr, (sizeof(struct apr_hdr) + sizeof(struct asm_stream_cmd_set_pp_params_v2) + params_length), TRUE); @@ -8272,6 +8413,7 @@ int q6asm_send_audio_effects_params(struct audio_client *ac, char *params, rc = 0; fail_send_param: + mutex_unlock(&session[session_id].mutex_lock_per_session); kfree(asm_params); return rc; } @@ -9183,7 +9325,7 @@ int q6asm_send_cal(struct audio_client *ac) struct apr_hdr hdr; char *asm_params = NULL; struct asm_stream_cmd_set_pp_params_v2 payload_params; - int sz, rc = -EINVAL; + int sz, rc = -EINVAL, session_id = 0; pr_debug("%s:\n", __func__); @@ -9191,22 +9333,13 @@ int q6asm_send_cal(struct audio_client *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) + session_id = q6asm_get_session_id_from_audio_client(ac); + if (!session_id) goto done; - if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) { - rc = 0; /* no cal is required, not error case */ + if (cal_data[ASM_AUDSTRM_CAL] == NULL) goto done; - } mutex_lock(&cal_data[ASM_AUDSTRM_CAL]->lock); cal_block = cal_utils_get_only_cal_block(cal_data[ASM_AUDSTRM_CAL]); @@ -9240,6 +9373,26 @@ int q6asm_send_cal(struct audio_client *ac) goto unlock; } + mutex_lock(&session[session_id].mutex_lock_per_session); + if (!q6asm_is_valid_audio_client(ac)) { + rc = -EINVAL; + goto free; + } + + if (ac->apr == NULL) { + pr_err("%s: AC APR handle NULL\n", __func__); + goto free; + } + if (ac->io_mode & NT_MODE) { + pr_debug("%s: called for NT MODE, exiting\n", __func__); + goto free; + } + + if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) { + rc = 0; /* no cal is required, not error case */ + goto free; + } + /* 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); @@ -9288,6 +9441,7 @@ int q6asm_send_cal(struct audio_client *ac) rc = 0; free: + mutex_unlock(&session[session_id].mutex_lock_per_session); kfree(asm_params); unlock: mutex_unlock(&cal_data[ASM_AUDSTRM_CAL]->lock); @@ -9476,8 +9630,10 @@ static int __init q6asm_init(void) memset(session, 0, sizeof(struct audio_session) * (ASM_ACTIVE_STREAMS_ALLOWED + 1)); - for (lcnt = 0; lcnt <= ASM_ACTIVE_STREAMS_ALLOWED; lcnt++) + for (lcnt = 0; lcnt <= ASM_ACTIVE_STREAMS_ALLOWED; lcnt++) { spin_lock_init(&(session[lcnt].session_lock)); + mutex_init(&(session[lcnt].mutex_lock_per_session)); + } set_custom_topology = 1; /*setup common client used for cal mem map */ From 69f16a559069257b613d5f5f95f7dbaf73ba2ec2 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Fri, 1 Jun 2018 12:05:25 +0530 Subject: [PATCH 212/276] ASoC: Fix null pointer dereference for prtd pointer Private data pointer may not be initialized before access it in adsp_stream_cmd_put. NULL check for prtd avoids the issue. CRs-Fixed: 2250112 Change-Id: I0a033d7e867b183c329941b57b49232729191f1b Signed-off-by: Vatsal Bucha --- asoc/msm-pcm-q6-v2.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 0b6f2d46f11f..5684a4e05701 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -638,6 +638,7 @@ static int msm_pcm_open(struct snd_pcm_substream *substream) if (!prtd->audio_client) { pr_info("%s: Could not allocate memory\n", __func__); kfree(prtd); + prtd = NULL; return -ENOMEM; } @@ -1131,6 +1132,12 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, } prtd = substream->runtime->private_data; + if (prtd == NULL) { + pr_err("%s prtd is null.\n", __func__); + ret = -EINVAL; + goto done; + } + if (prtd->audio_client == NULL) { pr_err("%s prtd is null.\n", __func__); ret = -EINVAL; From 7dc77a0bcd1186eae02e5c7256214d69676fdd2a Mon Sep 17 00:00:00 2001 From: Ramprasad Katkam Date: Tue, 19 Jun 2018 17:07:57 +0530 Subject: [PATCH 213/276] rtac: Add mutex protection for rtac cal apis Add mutex lock protection to synchronize rtac calibration set and get api calls. Change-Id: Ieb2d01642ecefff6405bb59554157c304b4b651d Signed-off-by: Ramprasad Katkam --- dsp/rtac.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/dsp/rtac.c b/dsp/rtac.c index 774cfb5b2320..976a67e3d039 100644 --- a/dsp/rtac.c +++ b/dsp/rtac.c @@ -125,6 +125,10 @@ struct mutex rtac_asm_apr_mutex; struct mutex rtac_voice_mutex; struct mutex rtac_voice_apr_mutex; struct mutex rtac_afe_apr_mutex; +struct mutex rtac_asm_cal_mutex; +struct mutex rtac_adm_cal_mutex; +struct mutex rtac_afe_cal_mutex; +struct mutex rtac_voice_cal_mutex; int rtac_clear_mapping(uint32_t cal_type) { @@ -1723,42 +1727,62 @@ static long rtac_ioctl_shared(struct file *f, } case AUDIO_GET_RTAC_ADM_CAL: + mutex_lock(&rtac_adm_cal_mutex); result = send_adm_apr((void *)arg, ADM_CMD_GET_PP_PARAMS_V5); + mutex_unlock(&rtac_adm_cal_mutex); break; case AUDIO_SET_RTAC_ADM_CAL: + mutex_lock(&rtac_adm_cal_mutex); result = send_adm_apr((void *)arg, ADM_CMD_SET_PP_PARAMS_V5); + mutex_unlock(&rtac_adm_cal_mutex); break; case AUDIO_GET_RTAC_ASM_CAL: + mutex_lock(&rtac_asm_cal_mutex); result = send_rtac_asm_apr((void *)arg, ASM_STREAM_CMD_GET_PP_PARAMS_V2); + mutex_unlock(&rtac_asm_cal_mutex); break; case AUDIO_SET_RTAC_ASM_CAL: + mutex_lock(&rtac_asm_cal_mutex); result = send_rtac_asm_apr((void *)arg, ASM_STREAM_CMD_SET_PP_PARAMS_V2); + mutex_unlock(&rtac_asm_cal_mutex); break; case AUDIO_GET_RTAC_CVS_CAL: + mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVS, (void *) arg, VSS_ICOMMON_CMD_GET_PARAM_V2); + mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_SET_RTAC_CVS_CAL: + mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVS, (void *) arg, VSS_ICOMMON_CMD_SET_PARAM_V2); + mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_GET_RTAC_CVP_CAL: + mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVP, (void *) arg, VSS_ICOMMON_CMD_GET_PARAM_V2); + mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_SET_RTAC_CVP_CAL: + mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVP, (void *) arg, VSS_ICOMMON_CMD_SET_PARAM_V2); + mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_GET_RTAC_AFE_CAL: + mutex_lock(&rtac_afe_cal_mutex); result = send_rtac_afe_apr((void *)arg, AFE_PORT_CMD_GET_PARAM_V2); + mutex_unlock(&rtac_afe_cal_mutex); break; case AUDIO_SET_RTAC_AFE_CAL: + mutex_lock(&rtac_afe_cal_mutex); result = send_rtac_afe_apr((void *)arg, AFE_PORT_CMD_SET_PARAM_V2); + mutex_unlock(&rtac_afe_cal_mutex); break; default: pr_err("%s: Invalid IOCTL, command = %d!\n", @@ -1890,6 +1914,7 @@ static int __init rtac_init(void) init_waitqueue_head(&rtac_adm_apr_data.cmd_wait); mutex_init(&rtac_adm_mutex); mutex_init(&rtac_adm_apr_mutex); + mutex_init(&rtac_adm_cal_mutex); rtac_adm_buffer = kzalloc( rtac_cal[ADM_RTAC_CAL].map_data.map_size, GFP_KERNEL); @@ -1903,6 +1928,7 @@ static int __init rtac_init(void) init_waitqueue_head(&rtac_asm_apr_data[i].cmd_wait); } mutex_init(&rtac_asm_apr_mutex); + mutex_init(&rtac_asm_cal_mutex); rtac_asm_buffer = kzalloc( rtac_cal[ASM_RTAC_CAL].map_data.map_size, GFP_KERNEL); @@ -1916,6 +1942,7 @@ static int __init rtac_init(void) atomic_set(&rtac_afe_apr_data.cmd_state, 0); init_waitqueue_head(&rtac_afe_apr_data.cmd_wait); mutex_init(&rtac_afe_apr_mutex); + mutex_init(&rtac_afe_cal_mutex); rtac_afe_buffer = kzalloc( rtac_cal[AFE_RTAC_CAL].map_data.map_size, GFP_KERNEL); @@ -1934,6 +1961,7 @@ static int __init rtac_init(void) } mutex_init(&rtac_voice_mutex); mutex_init(&rtac_voice_apr_mutex); + mutex_init(&rtac_voice_cal_mutex); rtac_voice_buffer = kzalloc( rtac_cal[VOICE_RTAC_CAL].map_data.map_size, GFP_KERNEL); From 2887d7e9526d1b3da71eb57a822c830385868705 Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Fri, 29 Jun 2018 09:21:32 +0800 Subject: [PATCH 214/276] ASoC: wcd934x: update threshold when micbias voltage is changed When wcd_correct_swch_plug and wcd_mbhc_adc_hs_rem_irq are running concurrently, adc_threshold should get called when micbias is changed and it would not consider the removal as fake removal. Move adc_threshold to the while loop for fake removal check to fix it. Change-Id: I49934adbe045c8bf087cccadb04410e9c2e0bee0 Signed-off-by: Meng Wang --- asoc/codecs/wcd-mbhc-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/codecs/wcd-mbhc-adc.c b/asoc/codecs/wcd-mbhc-adc.c index c9304109179b..0d16b1f0ed3d 100644 --- a/asoc/codecs/wcd-mbhc-adc.c +++ b/asoc/codecs/wcd-mbhc-adc.c @@ -945,7 +945,6 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) timeout = jiffies + msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS); - adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); do { retry++; @@ -954,6 +953,7 @@ static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data) * any change in IN2_P */ usleep_range(10000, 10100); + adc_threshold = wcd_mbhc_adc_get_hs_thres(mbhc); output_mv = wcd_measure_adc_once(mbhc, MUX_CTL_IN2P); pr_debug("%s: Check for fake removal: output_mv %d\n", From 1950206970771563d5808d450839b76e110a10fc Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 6 Jun 2018 09:46:44 +0800 Subject: [PATCH 215/276] ipc: apr: add qcom,subsys-name for apr Add qcom,subsys-name for apr driver. This property enables apr driver to receive subsystem up/down notification from modem/adsp. Change-Id: Id492c8b24d1a4cf0bcab37700a1e9c61c9067041 Signed-off-by: Meng Wang --- ipc/apr.c | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/ipc/apr.c b/ipc/apr.c index a15f937dc6d2..ff574be20b5f 100644 --- a/ipc/apr.c +++ b/ipc/apr.c @@ -44,6 +44,7 @@ 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 char *subsys_name = NULL; /* Subsystem restart: QDSP6 data, functions */ static struct workqueue_struct *apr_reset_workqueue; static void apr_reset_deregister(struct work_struct *work); @@ -1076,7 +1077,7 @@ static void apr_cleanup(void) static int apr_probe(struct platform_device *pdev) { - int i, j, k; + int i, j, k, ret = 0; init_waitqueue_head(&dsp_wait); init_waitqueue_head(&modem_wait); @@ -1113,10 +1114,26 @@ static int apr_probe(struct platform_device *pdev) spin_lock(&apr_priv->apr_lock); apr_priv->is_initial_boot = true; spin_unlock(&apr_priv->apr_lock); - subsys_notif_register("apr_adsp", AUDIO_NOTIFIER_ADSP_DOMAIN, - &adsp_service_nb); - subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN, - &modem_service_nb); + ret = of_property_read_string(pdev->dev.of_node, + "qcom,subsys-name", + (const char **)(&subsys_name)); + if (ret) { + pr_err("%s: missing subsys-name entry in dt node\n", __func__); + return -EINVAL; + } + + if (!strcmp(subsys_name, "apr_adsp")) { + subsys_notif_register("apr_adsp", + AUDIO_NOTIFIER_ADSP_DOMAIN, + &adsp_service_nb); + } else if (!strcmp(subsys_name, "apr_modem")) { + subsys_notif_register("apr_modem", + AUDIO_NOTIFIER_MODEM_DOMAIN, + &modem_service_nb); + } else { + pr_err("%s: invalid subsys-name %s\n", __func__, subsys_name); + return -EINVAL; + } return apr_debug_init(); } From a3b101ba1ea9af5a4b4cf5a7648881e147053652 Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Mon, 18 Jun 2018 11:59:09 +0530 Subject: [PATCH 216/276] dsp: Fix rtac memory unmap issue in ASM driver During unmap of rtac block in ASM, mem_map_handle address is set to zero instead of the value. Set the map handle value to zero to avoid issue in freeing the ion memory. CRs-Fixed: 2254339 Change-Id: I6584be029d4c8dde235e722149c758df0db9916e Signed-off-by: Aditya Bavanari --- dsp/q6asm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 5029e7c98c8f..e0bbd1575239 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -955,7 +955,7 @@ int q6asm_unmap_rtac_block(uint32_t *mem_map_handle) __func__, result2); result = result2; } else { - mem_map_handle = 0; + *mem_map_handle = 0; } result2 = q6asm_mmap_apr_dereg(); From ba5150f1aff0b766cdb83a3d4a5695eaa211eb23 Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Wed, 20 Jun 2018 14:57:05 -0700 Subject: [PATCH 217/276] dsp: Add frame size control parameter for AAC encoder BT sink devices have a limitation on the packet sizes they can receive and handle from BT sources. AAC encoder can produce output frames which can vary in size based on content being encoded. If frame size exceeds MTU size of BT sink device, it will lead to incorrect decoding of frames in BT sink. Add a frame configuration parameter (MTU/Peak bitrate) for AAC encoder to solve this problem. Change-Id: Ia7795049e982400b0b3657b0b3a8990fc6920e38 Signed-off-by: Aniket Kumar Lata --- asoc/msm-dai-q6-v2.c | 4 ++-- dsp/q6afe.c | 35 +++++++++++++++++++++++++++++++++++ include/dsp/apr_audio-v2.h | 26 +++++++++++++++++++++++++- 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 3e344dec619a..e3c5022a5d33 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -2273,7 +2273,7 @@ static int msm_dai_q6_afe_enc_cfg_get(struct snd_kcontrol *kcontrol, 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)); + sizeof(struct asm_aac_enc_cfg_t)); break; case ENC_FMT_APTX: memcpy(ucontrol->value.bytes.data + format_size, @@ -2331,7 +2331,7 @@ static int msm_dai_q6_afe_enc_cfg_put(struct snd_kcontrol *kcontrol, 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)); + sizeof(struct asm_aac_enc_cfg_t)); break; case ENC_FMT_APTX: memcpy(&dai_data->enc_config.data, diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 46664c88a7e2..d73f2177ebc2 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -3028,6 +3028,15 @@ static int q6afe_send_enc_config(u16 port_id, config.port.enc_blk_param.enc_cfg_blk_size = sizeof(config.port.enc_blk_param.enc_blk_config) - sizeof(struct afe_abr_enc_cfg_t); + } else if (format == ASM_MEDIA_FMT_AAC_V2) { + config.param.payload_size = payload_size + + sizeof(config.port.enc_blk_param) + - sizeof(struct asm_aac_frame_size_control_t); + config.pdata.param_size = sizeof(config.port.enc_blk_param) + - sizeof(struct asm_aac_frame_size_control_t); + config.port.enc_blk_param.enc_cfg_blk_size = + sizeof(config.port.enc_blk_param.enc_blk_config) + - sizeof(struct asm_aac_frame_size_control_t); } else { config.param.payload_size = payload_size + sizeof(config.port.enc_blk_param); @@ -3046,6 +3055,32 @@ static int q6afe_send_enc_config(u16 port_id, goto exit; } + if (format == ASM_MEDIA_FMT_AAC_V2) { + uint32_t frame_size_ctl_value = config.port.enc_blk_param. + enc_blk_config.aac_config.frame_ctl.ctl_value; + if (frame_size_ctl_value > 0) { + config.param.payload_size = payload_size + + sizeof(config.port.frame_ctl_param); + config.pdata.param_id = + AFE_PARAM_ID_AAC_FRM_SIZE_CONTROL; + config.pdata.param_size = + sizeof(config.port.frame_ctl_param); + config.port.frame_ctl_param.ctl_type = + config.port.enc_blk_param.enc_blk_config. + aac_config.frame_ctl.ctl_type; + config.port.frame_ctl_param.ctl_value = + frame_size_ctl_value; + pr_debug("%s: send AFE_PARAM_ID_AAC_FRM_SIZE_CONTROL\n", + __func__); + ret = afe_apr_send_pkt(&config, &this_afe.wait[index]); + if (ret) { + pr_err("%s: AAC_FRM_SIZE_CONTROL failed %d\n", + __func__, ret); + goto exit; + } + } + } + if (format == ASM_MEDIA_FMT_APTX) { config.param.payload_size = payload_size + sizeof(config.port.sync_mode_param); diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index e1efcf0df312..e1ce488ad3a9 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -3229,6 +3229,12 @@ struct afe_param_id_aptx_sync_mode { */ #define AFE_SB_DATA_FORMAT_GENERIC_COMPRESSED 0x3 +/* + * Parameter to send frame control size + * to DSP for AAC encoder in AFE. + */ +#define AFE_PARAM_ID_AAC_FRM_SIZE_CONTROL 0x000132EA + /* * ID for AFE port module. This will be used to define port properties. * This module supports following parameter IDs: @@ -3399,6 +3405,23 @@ struct asm_aac_enc_cfg_v2_t { uint32_t sample_rate; } __packed; +/* Structure to control frame size of AAC encoded frames. */ +struct asm_aac_frame_size_control_t { + /* Type of frame size control: MTU_SIZE / PEAK_BIT_RATE*/ + uint32_t ctl_type; + /* + * Control value + * MTU_SIZE: MTU size in bytes + * PEAK_BIT_RATE: Peak bitrate in bits per second. + */ + uint32_t ctl_value; +} __packed; + +struct asm_aac_enc_cfg_t { + struct asm_aac_enc_cfg_v2_t aac_cfg; + struct asm_aac_frame_size_control_t frame_ctl; +} __packed; + /* FMT ID for apt-X Classic */ #define ASM_MEDIA_FMT_APTX 0x000131ff @@ -3593,7 +3616,7 @@ struct afe_port_media_type_t { union afe_enc_config_data { struct asm_sbc_enc_cfg_t sbc_config; - struct asm_aac_enc_cfg_v2_t aac_config; + struct asm_aac_enc_cfg_t aac_config; struct asm_custom_enc_cfg_t custom_config; struct asm_celt_enc_cfg_t celt_config; struct asm_aptx_enc_cfg_t aptx_config; @@ -3690,6 +3713,7 @@ union afe_port_config { struct afe_param_id_tdm_cfg tdm; struct afe_param_id_usb_audio_cfg usb_audio; struct afe_param_id_aptx_sync_mode sync_mode_param; + struct asm_aac_frame_size_control_t frame_ctl_param; 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; From 8bea406fceeab2111557526278c8a556be9648a7 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Fri, 29 Jun 2018 15:14:37 +0800 Subject: [PATCH 218/276] ASoC: hide bind/unbind in sysfs Exposure of driver bind/unbind to userspace via sysfs may lead to unexpected behavior. Hide bind and unbind by driver attribute. Change-Id: I20d6ee653bcc16af15d6368664aaf240c6645cd0 Signed-off-by: Xiaojun Sang --- asoc/codecs/audio-ext-clk-up.c | 3 ++- asoc/codecs/audio-ext-clk.c | 3 ++- asoc/codecs/msm-cdc-pinctrl.c | 3 ++- asoc/codecs/msm_hdmi_codec_rx.c | 3 ++- asoc/codecs/msm_sdw/msm_sdw_cdc.c | 1 + asoc/codecs/msm_stub.c | 3 ++- asoc/codecs/wcd-dsp-mgr.c | 1 + asoc/codecs/wcd9335.c | 1 + asoc/codecs/wcd934x/wcd934x.c | 1 + asoc/codecs/wcd9xxx-irq.c | 3 ++- asoc/msm-compress-q6-v2.c | 1 + asoc/msm-cpe-lsm.c | 3 ++- asoc/msm-dai-fe.c | 3 ++- asoc/msm-dai-q6-hdmi-v2.c | 1 + asoc/msm-dai-q6-v2.c | 8 ++++++++ asoc/msm-dai-stub-v2.c | 4 +++- asoc/msm-lsm-client.c | 3 ++- asoc/msm-pcm-afe-v2.c | 3 ++- asoc/msm-pcm-dtmf-v2.c | 3 ++- asoc/msm-pcm-host-voice-v2.c | 3 ++- asoc/msm-pcm-hostless.c | 3 ++- asoc/msm-pcm-loopback-v2.c | 3 ++- asoc/msm-pcm-q6-noirq.c | 3 ++- asoc/msm-pcm-q6-v2.c | 1 + asoc/msm-pcm-routing-v2.c | 1 + asoc/msm-pcm-voice-v2.c | 1 + asoc/msm-pcm-voip-v2.c | 3 ++- asoc/msm-transcode-loopback-q6-v2.c | 1 + asoc/sdm660-common.c | 1 + asoc/sdm845.c | 1 + dsp/adsp-loader.c | 3 ++- dsp/avtimer.c | 3 ++- dsp/msm_audio_ion.c | 1 + ipc/apr.c | 1 + ipc/wcd-dsp-glink.c | 1 + soc/pinctrl-lpi.c | 3 ++- soc/pinctrl-wcd.c | 3 ++- soc/swr-wcd-ctrl.c | 1 + soc/wcd-spi-ac.c | 1 + 39 files changed, 68 insertions(+), 21 deletions(-) diff --git a/asoc/codecs/audio-ext-clk-up.c b/asoc/codecs/audio-ext-clk-up.c index 9caf13a59cfb..e12581c3179f 100644 --- a/asoc/codecs/audio-ext-clk-up.c +++ b/asoc/codecs/audio-ext-clk-up.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -607,6 +607,7 @@ static struct platform_driver audio_ref_clk_driver = { .name = "audio-ref-clk", .owner = THIS_MODULE, .of_match_table = audio_ref_clk_match, + .suppress_bind_attrs = true, }, .probe = audio_ref_clk_probe, .remove = audio_ref_clk_remove, diff --git a/asoc/codecs/audio-ext-clk.c b/asoc/codecs/audio-ext-clk.c index 72f16f5848dd..3abd36fde586 100644 --- a/asoc/codecs/audio-ext-clk.c +++ b/asoc/codecs/audio-ext-clk.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -329,6 +329,7 @@ static struct platform_driver audio_ref_clk_driver = { .name = "audio-ref-clk", .owner = THIS_MODULE, .of_match_table = audio_ref_clk_match, + .suppress_bind_attrs = true, }, .probe = audio_ref_clk_probe, .remove = audio_ref_clk_remove, diff --git a/asoc/codecs/msm-cdc-pinctrl.c b/asoc/codecs/msm-cdc-pinctrl.c index 79e322f1a277..005557670a89 100644 --- a/asoc/codecs/msm-cdc-pinctrl.c +++ b/asoc/codecs/msm-cdc-pinctrl.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, 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 @@ -241,6 +241,7 @@ static struct platform_driver msm_cdc_pinctrl_driver = { .name = "msm-cdc-pinctrl", .owner = THIS_MODULE, .of_match_table = msm_cdc_pinctrl_match, + .suppress_bind_attrs = true, }, .probe = msm_cdc_pinctrl_probe, .remove = msm_cdc_pinctrl_remove, diff --git a/asoc/codecs/msm_hdmi_codec_rx.c b/asoc/codecs/msm_hdmi_codec_rx.c index 3a48f530d1ef..a4990c0e6b73 100644 --- a/asoc/codecs/msm_hdmi_codec_rx.c +++ b/asoc/codecs/msm_hdmi_codec_rx.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -534,6 +534,7 @@ static struct platform_driver msm_ext_disp_audio_codec_rx_driver = { .name = "msm-ext-disp-audio-codec-rx", .owner = THIS_MODULE, .of_match_table = msm_ext_disp_audio_codec_rx_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_ext_disp_audio_codec_rx_plat_probe, .remove = msm_ext_disp_audio_codec_rx_plat_remove, diff --git a/asoc/codecs/msm_sdw/msm_sdw_cdc.c b/asoc/codecs/msm_sdw/msm_sdw_cdc.c index 46223b1a3686..498dfbfe9ed4 100644 --- a/asoc/codecs/msm_sdw/msm_sdw_cdc.c +++ b/asoc/codecs/msm_sdw/msm_sdw_cdc.c @@ -2076,6 +2076,7 @@ static struct platform_driver msm_sdw_codec_driver = { .name = "msm_sdw_codec", .owner = THIS_MODULE, .of_match_table = msm_sdw_codec_dt_match, + .suppress_bind_attrs = true, }, }; module_platform_driver(msm_sdw_codec_driver); diff --git a/asoc/codecs/msm_stub.c b/asoc/codecs/msm_stub.c index 68e55ae1da16..2e04d23cb3dc 100644 --- a/asoc/codecs/msm_stub.c +++ b/asoc/codecs/msm_stub.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2014, 2017-2018 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 @@ -67,6 +67,7 @@ static struct platform_driver msm_stub_driver = { .name = "msm-stub-codec", .owner = THIS_MODULE, .of_match_table = msm_stub_codec_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_stub_dev_probe, .remove = msm_stub_dev_remove, diff --git a/asoc/codecs/wcd-dsp-mgr.c b/asoc/codecs/wcd-dsp-mgr.c index 7695b16a6dd5..e24878c2d94a 100644 --- a/asoc/codecs/wcd-dsp-mgr.c +++ b/asoc/codecs/wcd-dsp-mgr.c @@ -1313,6 +1313,7 @@ static struct platform_driver wdsp_mgr_driver = { .name = "wcd-dsp-mgr", .owner = THIS_MODULE, .of_match_table = of_match_ptr(wdsp_mgr_dt_match), + .suppress_bind_attrs = true, }, .probe = wdsp_mgr_probe, .remove = wdsp_mgr_remove, diff --git a/asoc/codecs/wcd9335.c b/asoc/codecs/wcd9335.c index 03958f39c3c7..bebbf9486c21 100644 --- a/asoc/codecs/wcd9335.c +++ b/asoc/codecs/wcd9335.c @@ -14317,6 +14317,7 @@ static struct platform_driver tasha_codec_driver = { #ifdef CONFIG_PM .pm = &tasha_pm_ops, #endif + .suppress_bind_attrs = true, }, }; diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 0ae228701466..ba9bcbade212 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -11040,6 +11040,7 @@ static struct platform_driver tavil_codec_driver = { #ifdef CONFIG_PM .pm = &tavil_pm_ops, #endif + .suppress_bind_attrs = true, }, }; diff --git a/asoc/codecs/wcd9xxx-irq.c b/asoc/codecs/wcd9xxx-irq.c index b192e992d1fa..5ec5944ff603 100644 --- a/asoc/codecs/wcd9xxx-irq.c +++ b/asoc/codecs/wcd9xxx-irq.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2018, 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 @@ -863,6 +863,7 @@ static struct platform_driver wcd9xxx_irq_driver = { .name = "wcd9xxx_intc", .owner = THIS_MODULE, .of_match_table = of_match_ptr(of_match), + .suppress_bind_attrs = true, }, }; diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 72c02bc4c456..497566b27f82 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -4559,6 +4559,7 @@ static struct platform_driver msm_compr_driver = { .name = "msm-compress-dsp", .owner = THIS_MODULE, .of_match_table = msm_compr_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_compr_dev_probe, .remove = msm_compr_remove, diff --git a/asoc/msm-cpe-lsm.c b/asoc/msm-cpe-lsm.c index 87297ec05104..99507d1167f4 100644 --- a/asoc/msm-cpe-lsm.c +++ b/asoc/msm-cpe-lsm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2018, 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 @@ -3331,6 +3331,7 @@ static struct platform_driver msm_cpe_lsm_driver = { .name = "msm-cpe-lsm", .owner = THIS_MODULE, .of_match_table = of_match_ptr(msm_cpe_lsm_dt_match), + .suppress_bind_attrs = true, }, .probe = msm_cpe_lsm_probe, .remove = msm_cpe_lsm_remove, diff --git a/asoc/msm-dai-fe.c b/asoc/msm-dai-fe.c index 172936a8eac6..e57c3e3a2605 100644 --- a/asoc/msm-dai-fe.c +++ b/asoc/msm-dai-fe.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -2598,6 +2598,7 @@ static struct platform_driver msm_fe_dai_driver = { .name = "msm-dai-fe", .owner = THIS_MODULE, .of_match_table = msm_dai_fe_dt_match, + .suppress_bind_attrs = true, }, }; diff --git a/asoc/msm-dai-q6-hdmi-v2.c b/asoc/msm-dai-q6-hdmi-v2.c index 8ca8e08660ec..6b2d2e799a7c 100644 --- a/asoc/msm-dai-q6-hdmi-v2.c +++ b/asoc/msm-dai-q6-hdmi-v2.c @@ -540,6 +540,7 @@ static struct platform_driver msm_dai_q6_hdmi_driver = { .name = "msm-dai-q6-hdmi", .owner = THIS_MODULE, .of_match_table = msm_dai_q6_hdmi_dt_match, + .suppress_bind_attrs = true, }, }; diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 3e344dec619a..e1adcce64371 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -3386,6 +3386,7 @@ static struct platform_driver msm_auxpcm_dev_driver = { .name = "msm-auxpcm-dev", .owner = THIS_MODULE, .of_match_table = msm_auxpcm_dev_dt_match, + .suppress_bind_attrs = true, }, }; @@ -5163,6 +5164,7 @@ static struct platform_driver msm_dai_q6_dev = { .name = "msm-dai-q6-dev", .owner = THIS_MODULE, .of_match_table = msm_dai_q6_dev_dt_match, + .suppress_bind_attrs = true, }, }; @@ -5199,6 +5201,7 @@ static struct platform_driver msm_dai_q6 = { .name = "msm-dai-q6", .owner = THIS_MODULE, .of_match_table = msm_dai_q6_dt_match, + .suppress_bind_attrs = true, }, }; @@ -5234,6 +5237,7 @@ static struct platform_driver msm_dai_mi2s_q6 = { .name = "msm-dai-mi2s", .owner = THIS_MODULE, .of_match_table = msm_dai_mi2s_dt_match, + .suppress_bind_attrs = true, }, }; @@ -5251,6 +5255,7 @@ static struct platform_driver msm_dai_q6_mi2s_driver = { .name = "msm-dai-q6-mi2s", .owner = THIS_MODULE, .of_match_table = msm_dai_q6_mi2s_dev_dt_match, + .suppress_bind_attrs = true, }, }; @@ -5288,6 +5293,7 @@ static struct platform_driver msm_dai_q6_spdif_driver = { .name = "msm-dai-q6-spdif", .owner = THIS_MODULE, .of_match_table = msm_dai_q6_spdif_dt_match, + .suppress_bind_attrs = true, }, }; @@ -5494,6 +5500,7 @@ static struct platform_driver msm_dai_tdm_q6 = { .name = "msm-dai-tdm", .owner = THIS_MODULE, .of_match_table = msm_dai_tdm_dt_match, + .suppress_bind_attrs = true, }, }; @@ -9006,6 +9013,7 @@ static struct platform_driver msm_dai_q6_tdm_driver = { .name = "msm-dai-q6-tdm", .owner = THIS_MODULE, .of_match_table = msm_dai_q6_tdm_dev_dt_match, + .suppress_bind_attrs = true, }, }; diff --git a/asoc/msm-dai-stub-v2.c b/asoc/msm-dai-stub-v2.c index 3a2c3d3dbbcf..9724abe3c567 100644 --- a/asoc/msm-dai-stub-v2.c +++ b/asoc/msm-dai-stub-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2017-2018 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 @@ -310,6 +310,7 @@ static struct platform_driver msm_dai_stub_dev = { .name = "msm-dai-stub-dev", .owner = THIS_MODULE, .of_match_table = msm_dai_stub_dev_dt_match, + .suppress_bind_attrs = true, }, }; @@ -351,6 +352,7 @@ static struct platform_driver msm_dai_stub_driver = { .name = "msm-dai-stub", .owner = THIS_MODULE, .of_match_table = msm_dai_stub_dt_match, + .suppress_bind_attrs = true, }, }; diff --git a/asoc/msm-lsm-client.c b/asoc/msm-lsm-client.c index 339125e2d775..afbe9c2bb56d 100644 --- a/asoc/msm-lsm-client.c +++ b/asoc/msm-lsm-client.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2018, 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 @@ -2396,6 +2396,7 @@ static struct platform_driver msm_lsm_driver = { .name = "msm-lsm-client", .owner = THIS_MODULE, .of_match_table = of_match_ptr(msm_lsm_client_dt_match), + .suppress_bind_attrs = true, }, .probe = msm_lsm_probe, .remove = msm_lsm_remove, diff --git a/asoc/msm-pcm-afe-v2.c b/asoc/msm-pcm-afe-v2.c index 102fad8b3278..19bb07ce28bb 100644 --- a/asoc/msm-pcm-afe-v2.c +++ b/asoc/msm-pcm-afe-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -899,6 +899,7 @@ static struct platform_driver msm_afe_driver = { .name = "msm-pcm-afe", .owner = THIS_MODULE, .of_match_table = msm_pcm_afe_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_afe_probe, .remove = msm_afe_remove, diff --git a/asoc/msm-pcm-dtmf-v2.c b/asoc/msm-pcm-dtmf-v2.c index c387c7d0aab4..b617df3b610c 100644 --- a/asoc/msm-pcm-dtmf-v2.c +++ b/asoc/msm-pcm-dtmf-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2014, 2017-2018 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 @@ -575,6 +575,7 @@ static struct platform_driver msm_pcm_driver = { .name = "msm-pcm-dtmf", .owner = THIS_MODULE, .of_match_table = msm_pcm_dtmf_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_pcm_probe, .remove = msm_pcm_remove, diff --git a/asoc/msm-pcm-host-voice-v2.c b/asoc/msm-pcm-host-voice-v2.c index 55b07bc75823..1642ca5d3897 100644 --- a/asoc/msm-pcm-host-voice-v2.c +++ b/asoc/msm-pcm-host-voice-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2018, 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 @@ -1499,6 +1499,7 @@ static struct platform_driver msm_pcm_driver = { .name = "msm-voice-host-pcm", .owner = THIS_MODULE, .of_match_table = msm_voice_host_pcm_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_pcm_probe, .remove = msm_pcm_remove, diff --git a/asoc/msm-pcm-hostless.c b/asoc/msm-pcm-hostless.c index 35766b44981c..cf510eb7a389 100644 --- a/asoc/msm-pcm-hostless.c +++ b/asoc/msm-pcm-hostless.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2014, 2017-2018 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 @@ -61,6 +61,7 @@ static struct platform_driver msm_pcm_hostless_driver = { .name = "msm-pcm-hostless", .owner = THIS_MODULE, .of_match_table = msm_pcm_hostless_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_pcm_hostless_probe, .remove = msm_pcm_hostless_remove, diff --git a/asoc/msm-pcm-loopback-v2.c b/asoc/msm-pcm-loopback-v2.c index a6ac8ca006fa..9b42d62aef93 100644 --- a/asoc/msm-pcm-loopback-v2.c +++ b/asoc/msm-pcm-loopback-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2018, 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 @@ -780,6 +780,7 @@ static struct platform_driver msm_pcm_driver = { .name = "msm-pcm-loopback", .owner = THIS_MODULE, .of_match_table = msm_pcm_loopback_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_pcm_probe, .remove = msm_pcm_remove, diff --git a/asoc/msm-pcm-q6-noirq.c b/asoc/msm-pcm-q6-noirq.c index 3e03437abcf6..693014dfbeb5 100644 --- a/asoc/msm-pcm-q6-noirq.c +++ b/asoc/msm-pcm-q6-noirq.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2018, 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 @@ -1263,6 +1263,7 @@ static struct platform_driver msm_pcm_driver_noirq = { .name = "msm-pcm-dsp-noirq", .owner = THIS_MODULE, .of_match_table = msm_pcm_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_pcm_probe, .remove = msm_pcm_remove, diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 5684a4e05701..f690ee369341 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -1905,6 +1905,7 @@ static struct platform_driver msm_pcm_driver = { .name = "msm-pcm-dsp", .owner = THIS_MODULE, .of_match_table = msm_pcm_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_pcm_probe, .remove = msm_pcm_remove, diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 7490f6908e63..fe4b74cc4226 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -17053,6 +17053,7 @@ static struct platform_driver msm_routing_pcm_driver = { .name = "msm-pcm-routing", .owner = THIS_MODULE, .of_match_table = msm_pcm_routing_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_routing_pcm_probe, .remove = msm_routing_pcm_remove, diff --git a/asoc/msm-pcm-voice-v2.c b/asoc/msm-pcm-voice-v2.c index 50f055f7efde..febd02f38197 100644 --- a/asoc/msm-pcm-voice-v2.c +++ b/asoc/msm-pcm-voice-v2.c @@ -813,6 +813,7 @@ static struct platform_driver msm_pcm_driver = { .name = "msm-pcm-voice", .owner = THIS_MODULE, .of_match_table = msm_voice_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_pcm_probe, .remove = msm_pcm_remove, diff --git a/asoc/msm-pcm-voip-v2.c b/asoc/msm-pcm-voip-v2.c index 86dcacacbaa3..46eef78a1364 100644 --- a/asoc/msm-pcm-voip-v2.c +++ b/asoc/msm-pcm-voip-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -1679,6 +1679,7 @@ static struct platform_driver msm_pcm_driver = { .name = "msm-voip-dsp", .owner = THIS_MODULE, .of_match_table = msm_voip_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_pcm_probe, .remove = msm_pcm_remove, diff --git a/asoc/msm-transcode-loopback-q6-v2.c b/asoc/msm-transcode-loopback-q6-v2.c index 0d65596c4cac..871f83c63360 100644 --- a/asoc/msm-transcode-loopback-q6-v2.c +++ b/asoc/msm-transcode-loopback-q6-v2.c @@ -956,6 +956,7 @@ static struct platform_driver msm_transcode_loopback_driver = { .name = "msm-transcode-loopback", .owner = THIS_MODULE, .of_match_table = msm_transcode_loopback_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_transcode_dev_probe, .remove = msm_transcode_remove, diff --git a/asoc/sdm660-common.c b/asoc/sdm660-common.c index ccfe528487dd..a1093cb79f35 100644 --- a/asoc/sdm660-common.c +++ b/asoc/sdm660-common.c @@ -3439,6 +3439,7 @@ static struct platform_driver sdm660_asoc_machine_driver = { .owner = THIS_MODULE, .pm = &snd_soc_pm_ops, .of_match_table = sdm660_asoc_machine_of_match, + .suppress_bind_attrs = true, }, .probe = msm_asoc_machine_probe, .remove = msm_asoc_machine_remove, diff --git a/asoc/sdm845.c b/asoc/sdm845.c index 9644aedad644..52a985da4951 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -7107,6 +7107,7 @@ static struct platform_driver sdm845_asoc_machine_driver = { .owner = THIS_MODULE, .pm = &snd_soc_pm_ops, .of_match_table = sdm845_asoc_machine_of_match, + .suppress_bind_attrs = true, }, .probe = msm_asoc_machine_probe, .remove = msm_asoc_machine_remove, diff --git a/dsp/adsp-loader.c b/dsp/adsp-loader.c index 07eeea4e4c8b..ac35cf9b83cb 100644 --- a/dsp/adsp-loader.c +++ b/dsp/adsp-loader.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2014, 2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2014, 2017-2018, 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 @@ -295,6 +295,7 @@ static struct platform_driver adsp_loader_driver = { .name = "adsp-loader", .owner = THIS_MODULE, .of_match_table = adsp_loader_dt_match, + .suppress_bind_attrs = true, }, .probe = adsp_loader_probe, .remove = adsp_loader_remove, diff --git a/dsp/avtimer.c b/dsp/avtimer.c index 63044db6e4b9..10a2964c3609 100644 --- a/dsp/avtimer.c +++ b/dsp/avtimer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2015, 2017-2018 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 @@ -514,6 +514,7 @@ static struct platform_driver dev_avtimer_driver = { .driver = { .name = "dev_avtimer", .of_match_table = avtimer_machine_of_match, + .suppress_bind_attrs = true, }, }; diff --git a/dsp/msm_audio_ion.c b/dsp/msm_audio_ion.c index 1a6604cffd73..bb3096c57176 100644 --- a/dsp/msm_audio_ion.c +++ b/dsp/msm_audio_ion.c @@ -925,6 +925,7 @@ static struct platform_driver msm_audio_ion_driver = { .name = "msm-audio-ion", .owner = THIS_MODULE, .of_match_table = msm_audio_ion_dt_match, + .suppress_bind_attrs = true, }, .probe = msm_audio_ion_probe, .remove = msm_audio_ion_remove, diff --git a/ipc/apr.c b/ipc/apr.c index a15f937dc6d2..43145a461876 100644 --- a/ipc/apr.c +++ b/ipc/apr.c @@ -1140,6 +1140,7 @@ static struct platform_driver apr_driver = { .name = "audio_apr", .owner = THIS_MODULE, .of_match_table = apr_machine_of_match, + .suppress_bind_attrs = true, } }; diff --git a/ipc/wcd-dsp-glink.c b/ipc/wcd-dsp-glink.c index adcab5bd1596..67a06ecefc4c 100644 --- a/ipc/wcd-dsp-glink.c +++ b/ipc/wcd-dsp-glink.c @@ -1218,6 +1218,7 @@ static struct platform_driver wdsp_glink_driver = { .name = WDSP_GLINK_DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = wdsp_glink_of_match, + .suppress_bind_attrs = true, }, }; diff --git a/soc/pinctrl-lpi.c b/soc/pinctrl-lpi.c index 07a582e2e4de..b7e5b40cfb42 100644 --- a/soc/pinctrl-lpi.c +++ b/soc/pinctrl-lpi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, 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 @@ -638,6 +638,7 @@ static struct platform_driver lpi_pinctrl_driver = { .driver = { .name = "qcom-lpi-pinctrl", .of_match_table = lpi_pinctrl_of_match, + .suppress_bind_attrs = true, }, .probe = lpi_pinctrl_probe, .remove = lpi_pinctrl_remove, diff --git a/soc/pinctrl-wcd.c b/soc/pinctrl-wcd.c index d7695dbe77b8..7515f98bb205 100644 --- a/soc/pinctrl-wcd.c +++ b/soc/pinctrl-wcd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2018, 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 @@ -424,6 +424,7 @@ static struct platform_driver wcd_pinctrl_driver = { .driver = { .name = "qcom-wcd-pinctrl", .of_match_table = wcd_pinctrl_of_match, + .suppress_bind_attrs = true, }, .probe = wcd_pinctrl_probe, .remove = wcd_pinctrl_remove, diff --git a/soc/swr-wcd-ctrl.c b/soc/swr-wcd-ctrl.c index 722b19a7193a..4eeaea5ccbf7 100644 --- a/soc/swr-wcd-ctrl.c +++ b/soc/swr-wcd-ctrl.c @@ -1883,6 +1883,7 @@ static struct platform_driver swr_mstr_driver = { .owner = THIS_MODULE, .pm = &swrm_dev_pm_ops, .of_match_table = swrm_dt_match, + .suppress_bind_attrs = true, }, }; diff --git a/soc/wcd-spi-ac.c b/soc/wcd-spi-ac.c index 92eaa8937344..b1bbb084da16 100644 --- a/soc/wcd-spi-ac.c +++ b/soc/wcd-spi-ac.c @@ -1031,6 +1031,7 @@ static struct platform_driver wcd_spi_ac_driver = { .driver = { .name = "qcom,wcd-spi-ac", .of_match_table = wcd_spi_ac_of_match, + .suppress_bind_attrs = true, }, .probe = wcd_spi_ac_probe, .remove = wcd_spi_ac_remove, From 9718f39318c203ef93a4ef227f8e7c40e3bdcda8 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Tue, 3 Jul 2018 14:05:53 +0530 Subject: [PATCH 219/276] wcd934x: Fix NULL pointer dereference for wcd9xxx pointer During bind/unbind of ngd_msm_ctrl power_gate_digital_core is called. Simultaneously tavil_remove is also called making tavil pointer as NULL. Check for tavil as not NULL at the start of function to avoid NULL pointer dereference. CRs-Fixed: 2267796 Change-Id: I6fc476cc8b706b556836f30838983de0f34d4fc1 Signed-off-by: Vatsal Bucha --- asoc/codecs/wcd934x/wcd934x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 7a1f23fb0331..95170e379327 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -8900,6 +8900,8 @@ static struct snd_soc_dai_driver tavil_i2s_dai[] = { static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil) { + if (!tavil) + return; mutex_lock(&tavil->power_lock); dev_dbg(tavil->dev, "%s: Entering power gating function, %d\n", __func__, tavil->power_active_ref); From ddbfad82276fbb68a9ce81729187dc902b96c5d6 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Tue, 31 Jul 2018 11:53:36 -0700 Subject: [PATCH 220/276] ASoC: wcd934x-dsp-cntl: initialize local char array val Due to the local char array that stores the codec dsp control command is not initialized, an invalid command could cause the stack content to be printed out in kernel dmesg. Initialize the array with memset. Change-Id: I739eacb5058275c91bef67505531097cb364b93e Signed-off-by: Xiaoyu Ye --- asoc/codecs/wcd934x/wcd934x-dsp-cntl.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c index 662f484028ee..de4f3997d476 100644 --- a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -1011,10 +1011,12 @@ static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, { struct wcd_dsp_cntl *cntl = container_of(filep->private_data, struct wcd_dsp_cntl, miscdev); - char val[WCD_MISCDEV_CMD_MAX_LEN]; + char val[WCD_MISCDEV_CMD_MAX_LEN + 1]; bool vote; int ret = 0; + memset(val, 0, WCD_MISCDEV_CMD_MAX_LEN + 1); + if (count == 0 || count > WCD_MISCDEV_CMD_MAX_LEN) { pr_err("%s: Invalid count = %zd\n", __func__, count); ret = -EINVAL; From d0a871609dbbe87c31eb716bc3b767418421170c Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Tue, 7 Aug 2018 11:20:55 +0800 Subject: [PATCH 221/276] ipc: glink: set handle NULL after glink close in all cases When glink_close runs, refcount for ctx object goes down to zero regardless of any failure. As a result, ctx object gets freed. However, handle was not set NULL in the caller. It results in second glink_close on released ctx object. Set handle NULL to avoid further glink_close. Change-Id: Ie311046e01a70555eaa230b0a3a096648524032d Signed-off-by: Xiaojun Sang --- ipc/wcd-dsp-glink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ipc/wcd-dsp-glink.c b/ipc/wcd-dsp-glink.c index 67a06ecefc4c..a2dbe3661f6c 100644 --- a/ipc/wcd-dsp-glink.c +++ b/ipc/wcd-dsp-glink.c @@ -411,10 +411,10 @@ static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch) dev_err(wpriv->dev, "%s: glink_close is failed, ret = %d\n", __func__, ret); } else { - ch->handle = NULL; dev_dbg(wpriv->dev, "%s: ch %s is closed\n", __func__, ch->ch_cfg.name); } + ch->handle = NULL; } else { dev_dbg(wpriv->dev, "%s: ch %s is already closed\n", __func__, ch->ch_cfg.name); From efe2419349f54870c321337d5017cb7926cbed3f Mon Sep 17 00:00:00 2001 From: Meng Wang Date: Wed, 25 Jul 2018 16:31:40 +0800 Subject: [PATCH 222/276] asoc: wcd-dsp-mgr: reinit ssr ready flag for adsp/wdsp ssr When adsp/wdsp SSR happens, WDSP_SSR_TYPE_WDSP_DOWN/WDSP_SSR_TYPE_CDC_DOWN would get received by wcd-dsp-mgr. wcd-dsp-mgr would clear current work and init wdsp when adsp is up and ready_compl flag is set to ready. Sometimes ready_compl flag is not cleared and wdsp initialization would happen when adsp/wdsp is down which would cause kernel panic. Reinit ready_compl flag when adsp/wdsp SSR happens. Change-Id: I45186abba7992ee6912c5b0da171ef37b04e2e71 Signed-off-by: Meng Wang --- asoc/codecs/wcd-dsp-mgr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/asoc/codecs/wcd-dsp-mgr.c b/asoc/codecs/wcd-dsp-mgr.c index e24878c2d94a..2bcc314dacf0 100644 --- a/asoc/codecs/wcd-dsp-mgr.c +++ b/asoc/codecs/wcd-dsp-mgr.c @@ -802,6 +802,7 @@ static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg, __wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY); wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, NULL); + reinit_completion(&wdsp->ready_compl); schedule_work(&wdsp->ssr_work); break; @@ -818,7 +819,7 @@ static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg, WDSP_EVENT_PRE_SHUTDOWN, NULL); } - + reinit_completion(&wdsp->ready_compl); schedule_work(&wdsp->ssr_work); break; From 7bdc2757ceb04ad8dc4eaac0a0b50ba2856b2987 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Tue, 31 Jul 2018 19:27:44 +0530 Subject: [PATCH 223/276] soc: swr-wcd-ctrl: Fix wsa mute issue for stereo playback When temperature on a single wsa881x device is being read, then soundwire master wakes up both wsa881x devices but regcache_sync is happening only for one wsa881x device on which the temperature is being read. This results in audio playback mute after temperature read. Fix the regcache sync during temperature read and playback usecase. Change-Id: I856b96517c629ac685bbc25caabee841037106c8 Signed-off-by: Laxminath Kasam --- soc/swr-wcd-ctrl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/soc/swr-wcd-ctrl.c b/soc/swr-wcd-ctrl.c index 4eeaea5ccbf7..f4c63792342a 100644 --- a/soc/swr-wcd-ctrl.c +++ b/soc/swr-wcd-ctrl.c @@ -1738,6 +1738,8 @@ int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data) (swrm->state == SWR_MSTR_UP)) { dev_dbg(swrm->dev, "%s: SWR master is already UP: %d\n", __func__, swrm->state); + list_for_each_entry(swr_dev, &mstr->devices, dev_list) + swr_reset_device(swr_dev); } else { pm_runtime_mark_last_busy(&pdev->dev); mutex_unlock(&swrm->reslock); From 0106d6a744a9a99716e5000f68bb32f231e182ce Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Thu, 21 Jun 2018 11:13:29 -0700 Subject: [PATCH 224/276] dsp: asm: initialize variables before use Initialize flag variables before use in spinlocks. CRs-Fixed: 2257317 Change-Id: I105b96208a62cc4b50e726af9dac4b728c9a1c98 Signed-off-by: Vignesh Kulothungan --- dsp/q6asm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index e0bbd1575239..fce9f4d099d4 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -575,7 +575,7 @@ static bool q6asm_is_valid_audio_client(struct audio_client *ac) static void q6asm_session_free(struct audio_client *ac) { int session_id; - unsigned long flags; + unsigned long flags = 0; pr_debug("%s: sessionid[%d]\n", __func__, ac->session); session_id = ac->session; @@ -1557,7 +1557,7 @@ 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; + unsigned long dsp_flags = 0; unsigned long flags = 0; struct asm_buffer_node *buf_node = NULL; struct list_head *ptr, *next; @@ -1774,7 +1774,7 @@ 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; + unsigned long dsp_flags = 0; uint32_t *payload; uint32_t wakeup_flag = 1; int32_t ret = 0; @@ -1782,7 +1782,7 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) uint8_t buf_index; struct msm_adsp_event_data *pp_event_package = NULL; uint32_t payload_size = 0; - unsigned long flags; + unsigned long flags = 0; int session_id; if (ac == NULL) { @@ -2430,7 +2430,7 @@ int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac) 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) { - unsigned long flags; + unsigned long flags = 0; 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); From ae8797589cb30b00b719bfabe908c9a39e2a3447 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Tue, 31 Jul 2018 19:30:32 +0530 Subject: [PATCH 225/276] soc: soundwire: Fix wsa mute issue for stereo playback When stereo playback start and temperature read initiated at same time, one of speaker PA bits are not set in particular race scenario. Handle the broadcast check in soundwire framework to ensure broadcast is disabled only when respective gr_sid slave calls swr_remove_from_group. Change-Id: I6f82a8c3c0f39dc20c1def09c1728ce4c26c2f5d Signed-off-by: Laxminath Kasam --- soc/soundwire.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soc/soundwire.c b/soc/soundwire.c index 6095c0a82e94..ad2487e23043 100644 --- a/soc/soundwire.c +++ b/soc/soundwire.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -245,7 +245,7 @@ int swr_remove_from_group(struct swr_device *dev, u8 dev_num) if (!dev->group_id) return 0; - if (master->gr_sid == dev_num) + if (master->gr_sid != dev_num) return 0; if (master->remove_from_group && master->remove_from_group(master)) From ef9a362667da5eedd04dd31dfee44cae7b82723d Mon Sep 17 00:00:00 2001 From: Aniket Kumar Lata Date: Mon, 6 Aug 2018 15:36:20 -0700 Subject: [PATCH 226/276] dsp: Do not send ABR parameters if bitrate is fixed ABR encoder config parameters should be sent to DSP only when ABR is enabled. Do not send these parameters for fixed bitrate. Change-Id: Id234d6b75c375aa9ec20e828cd8b847b91ffff9a Signed-off-by: Aniket Kumar Lata --- dsp/q6afe.c | 3 ++- include/dsp/apr_audio-v2.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index d73f2177ebc2..3aecde0b50b4 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -3128,7 +3128,8 @@ static int q6afe_send_enc_config(u16 port_id, goto exit; } - if (format == ASM_MEDIA_FMT_LDAC) { + if (format == ASM_MEDIA_FMT_LDAC && + cfg->ldac_config.abr_config.is_abr_enabled) { config.param.payload_size = payload_size + sizeof(config.port.map_param); pr_debug("%s:sending AFE_ENCODER_PARAM_ID_BIT_RATE_LEVEL_MAP to DSP payload = %d\n", diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h index e1ce488ad3a9..becc21ad0f84 100644 --- a/include/dsp/apr_audio-v2.h +++ b/include/dsp/apr_audio-v2.h @@ -3138,6 +3138,10 @@ struct afe_abr_enc_cfg_t { * Information to set up IMC between decoder and encoder. */ struct afe_imc_dec_enc_info imc_info; + /* + * Flag to indicate whether ABR is enabled. + */ + bool is_abr_enabled; } __packed; #define AFE_PARAM_ID_APTX_SYNC_MODE 0x00013205 From 8765431c390417ed794f63238a3dfb017280d1a4 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Tue, 31 Jul 2018 19:26:56 +0530 Subject: [PATCH 227/276] ASoC: wsa881x: Fix regcache sync issue during playback When temperature on a single wsa881x device is being read, then soundwire master wakes up both wsa881x devices but regcache_sync is happening only for one wsa881x device on which the temperature is being read. This results in audio playback mute after temperature read. Fix the regcache sync during temperature read and playback usecase. Change-Id: I054f4432d7ae7bce922341ad62b20544cccd5865 Signed-off-by: Sudheer Papothi --- asoc/codecs/wsa881x.c | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/asoc/codecs/wsa881x.c b/asoc/codecs/wsa881x.c index 41a186f1936d..d2a3938a5a2c 100644 --- a/asoc/codecs/wsa881x.c +++ b/asoc/codecs/wsa881x.c @@ -76,6 +76,7 @@ struct swr_port { enum { WSA881X_DEV_DOWN, WSA881X_DEV_UP, + WSA881X_DEV_READY, }; /* @@ -99,6 +100,7 @@ struct wsa881x_priv { int version; struct mutex bg_lock; struct mutex res_lock; + struct mutex temp_lock; struct snd_info_entry *entry; struct snd_info_entry *version_entry; int state; @@ -491,6 +493,17 @@ static const struct file_operations codec_debug_ops = { .read = codec_debug_read, }; +static void wsa881x_regcache_sync(struct wsa881x_priv *wsa881x) +{ + mutex_lock(&wsa881x->res_lock); + if (wsa881x->state != WSA881X_DEV_READY) { + regcache_mark_dirty(wsa881x->regmap); + regcache_sync(wsa881x->regmap); + wsa881x->state = WSA881X_DEV_READY; + } + mutex_unlock(&wsa881x->res_lock); +} + static const struct reg_sequence wsa881x_pre_pmu_pa[] = { {WSA881X_SPKR_DRV_GAIN, 0x41, 0}, {WSA881X_SPKR_MISC_CTL1, 0x01, 0}, @@ -850,7 +863,9 @@ static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + mutex_lock(&wsa881x->temp_lock); wsa881x_resource_acquire(codec, ENABLE); + mutex_unlock(&wsa881x->temp_lock); wsa881x_boost_ctrl(codec, ENABLE); break; case SND_SOC_DAPM_POST_PMD: @@ -858,7 +873,9 @@ static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w, wsa881x->swr_slave->dev_num, false); wsa881x_boost_ctrl(codec, DISABLE); + mutex_lock(&wsa881x->temp_lock); wsa881x_resource_acquire(codec, DISABLE); + mutex_unlock(&wsa881x->temp_lock); break; } return 0; @@ -1103,13 +1120,8 @@ static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec, return -EINVAL; } } - mutex_lock(&wsa881x->res_lock); - if (!wsa881x->clk_cnt) { - regcache_mark_dirty(wsa881x->regmap); - regcache_sync(wsa881x->regmap); - } - mutex_unlock(&wsa881x->res_lock); - + wsa881x_regcache_sync(wsa881x); + mutex_lock(&wsa881x->temp_lock); wsa881x_resource_acquire(codec, ENABLE); snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00); @@ -1122,6 +1134,7 @@ static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec, wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4); wsa881x_resource_acquire(codec, DISABLE); + mutex_unlock(&wsa881x->temp_lock); return 0; } @@ -1137,7 +1150,6 @@ static int wsa881x_probe(struct snd_soc_codec *codec) dev = wsa881x->swr_slave; wsa881x->codec = codec; mutex_init(&wsa881x->bg_lock); - mutex_init(&wsa881x->res_lock); wsa881x_init(codec); snprintf(wsa881x->tz_pdata.name, sizeof(wsa881x->tz_pdata.name), "%s.%x", "wsatz", (u8)dev->addr); @@ -1159,7 +1171,6 @@ static int wsa881x_remove(struct snd_soc_codec *codec) if (wsa881x->tz_pdata.tz_dev) wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev); mutex_destroy(&wsa881x->bg_lock); - mutex_destroy(&wsa881x->res_lock); return 0; } @@ -1342,6 +1353,8 @@ static int wsa881x_swr_probe(struct swr_device *pdev) __func__); goto dev_err; } + mutex_init(&wsa881x->res_lock); + mutex_init(&wsa881x->temp_lock); return 0; @@ -1364,6 +1377,8 @@ static int wsa881x_swr_remove(struct swr_device *pdev) } debugfs_remove_recursive(debugfs_wsa881x_dent); debugfs_wsa881x_dent = NULL; + mutex_destroy(&wsa881x->res_lock); + mutex_destroy(&wsa881x->temp_lock); snd_soc_unregister_codec(&pdev->dev); if (wsa881x->pd_gpio) gpio_free(wsa881x->pd_gpio); @@ -1422,6 +1437,11 @@ static int wsa881x_swr_reset(struct swr_device *pdev) dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__); return -EINVAL; } + if (wsa881x->state == WSA881X_DEV_READY) { + dev_dbg(&pdev->dev, "%s: device already active\n", __func__); + return 0; + } + wsa881x->bg_cnt = 0; wsa881x->clk_cnt = 0; while (swr_get_logical_dev_num(pdev, pdev->addr, &devnum) && retry--) { @@ -1429,8 +1449,8 @@ static int wsa881x_swr_reset(struct swr_device *pdev) usleep_range(1000, 1100); } pdev->dev_num = devnum; - regcache_mark_dirty(wsa881x->regmap); - regcache_sync(wsa881x->regmap); + wsa881x_regcache_sync(wsa881x); + return 0; } From 549d76d81e454fd6ed5ce43a171e143efb4ad34f Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Wed, 29 Nov 2017 15:44:14 +0530 Subject: [PATCH 228/276] ASoC: remove error prints and warning at bootup log. Cleanup for errors and warning print in audio drivers. CRs-Fixed: 2080345 Change-Id: Ib473dad9226127930be7079063e7795a802ca279 Signed-off-by: Laxminath Kasam --- asoc/codecs/sdm660_cdc/msm-analog-cdc.c | 15 +-------------- asoc/codecs/sdm660_cdc/msm-digital-cdc.c | 6 ++++++ asoc/msm-pcm-routing-v2.c | 15 +++++++++++++++ asoc/sdm660-internal.c | 3 +++ 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c index 35e00a3930bf..a1732b722b9e 100644 --- a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -3630,18 +3630,6 @@ static const struct sdm660_cdc_reg_mask_val {MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0xFF, 0xFF}, }; -static void msm_anlg_cdc_codec_init_cache(struct snd_soc_codec *codec) -{ - u32 i; - - regcache_cache_only(codec->component.regmap, true); - /* update cache with POR values */ - for (i = 0; i < ARRAY_SIZE(msm89xx_pmic_cdc_defaults); i++) - snd_soc_write(codec, msm89xx_pmic_cdc_defaults[i].reg, - msm89xx_pmic_cdc_defaults[i].def); - regcache_cache_only(codec->component.regmap, false); -} - static void msm_anlg_cdc_codec_init_reg(struct snd_soc_codec *codec) { u32 i; @@ -3687,7 +3675,7 @@ static struct regulator *msm_anlg_cdc_find_regulator( return sdm660_cdc->supplies[i].consumer; } - dev_err(sdm660_cdc->dev, "Error: regulator not found:%s\n" + dev_dbg(sdm660_cdc->dev, "Error: regulator not found:%s\n" , name); return NULL; } @@ -4139,7 +4127,6 @@ static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec) ARRAY_SIZE(hph_type_detect_controls)); msm_anlg_cdc_bringup(codec); - msm_anlg_cdc_codec_init_cache(codec); msm_anlg_cdc_codec_init_reg(codec); msm_anlg_cdc_update_reg_defaults(codec); diff --git a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c index 928c908e3da6..2c3d0d66afca 100644 --- a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -1397,6 +1397,9 @@ static const struct snd_soc_dapm_route audio_dig_map[] = { {"RX2 MIX1 INP2", "RX3", "I2S RX3"}, {"RX2 MIX1 INP2", "IIR1", "IIR1"}, {"RX2 MIX1 INP2", "IIR2", "IIR2"}, + {"RX2 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX2 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX2 MIX1 INP3", "RX3", "I2S RX3"}, {"RX3 MIX1 INP1", "RX1", "I2S RX1"}, {"RX3 MIX1 INP1", "RX2", "I2S RX2"}, @@ -1408,6 +1411,9 @@ static const struct snd_soc_dapm_route audio_dig_map[] = { {"RX3 MIX1 INP2", "RX3", "I2S RX3"}, {"RX3 MIX1 INP2", "IIR1", "IIR1"}, {"RX3 MIX1 INP2", "IIR2", "IIR2"}, + {"RX3 MIX1 INP3", "RX1", "I2S RX1"}, + {"RX3 MIX1 INP3", "RX2", "I2S RX2"}, + {"RX3 MIX1 INP3", "RX3", "I2S RX3"}, {"RX1 MIX2 INP1", "IIR1", "IIR1"}, {"RX2 MIX2 INP1", "IIR1", "IIR1"}, diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index fe4b74cc4226..e58f1d4372a0 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -12814,8 +12814,16 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("INT0_MI2S_RX", "INT0 MI2S Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT2_MI2S_RX", "INT2 MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT3_MI2S_RX", "INT3 MI2S Playback", + 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT5_MI2S_RX", "INT5 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("INT4_MI2S_TX", "INT4 MI2S Capture", + 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), @@ -12826,6 +12834,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 0, 0, 0, 0), SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX", "Tertiary MI2S Capture", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("INT0_MI2S_TX", "INT0 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", @@ -16232,6 +16242,9 @@ static const struct snd_soc_dapm_route intercon[] = { {"BE_OUT", NULL, "PRI_MI2S_RX"}, {"BE_OUT", NULL, "INT0_MI2S_RX"}, {"BE_OUT", NULL, "INT4_MI2S_RX"}, + {"BE_OUT", NULL, "INT2_MI2S_RX"}, + {"BE_OUT", NULL, "INT3_MI2S_RX"}, + {"BE_OUT", NULL, "INT5_MI2S_RX"}, {"BE_OUT", NULL, "INT_BT_SCO_RX"}, {"BE_OUT", NULL, "INT_BT_A2DP_RX"}, {"BE_OUT", NULL, "INT_FM_RX"}, @@ -16276,8 +16289,10 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUIN_MI2S_TX", NULL, "BE_IN"}, {"PRI_MI2S_TX", NULL, "BE_IN"}, {"TERT_MI2S_TX", NULL, "BE_IN"}, + {"INT0_MI2S_TX", NULL, "BE_IN"}, {"INT2_MI2S_TX", NULL, "BE_IN"}, {"INT3_MI2S_TX", NULL, "BE_IN"}, + {"INT4_MI2S_TX", NULL, "BE_IN"}, {"INT5_MI2S_TX", NULL, "BE_IN"}, {"SEC_MI2S_TX", NULL, "BE_IN"}, {"SENARY_MI2S_TX", NULL, "BE_IN" }, diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c index dbbd6960a8d9..14b04cf493d6 100644 --- a/asoc/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -1278,6 +1278,9 @@ static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dapm_ignore_suspend(dapm, "AMIC1"); snd_soc_dapm_ignore_suspend(dapm, "AMIC2"); snd_soc_dapm_ignore_suspend(dapm, "AMIC3"); + snd_soc_dapm_sync(dapm); + + dapm = snd_soc_codec_get_dapm(dig_cdc); snd_soc_dapm_ignore_suspend(dapm, "DMIC1"); snd_soc_dapm_ignore_suspend(dapm, "DMIC2"); snd_soc_dapm_ignore_suspend(dapm, "DMIC3"); From eed798574658eeaf800a6b768fc299cdb47d443f Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Tue, 5 Dec 2017 15:30:24 +0530 Subject: [PATCH 229/276] asoc: add multiple sampling rates support for a2dp. Add support of 44.1Khz, 88.2Khz and 96Khz over split-a2dp path. CRs-Fixed: 2153696 Change-Id: I69f20c7a9c465c974e847f42d754c69511138fbb Signed-off-by: Preetam Singh Ranawat --- asoc/sdm660-external.c | 25 ++++++++++++++++++++++++- asoc/sdm660-internal.c | 22 +++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/asoc/sdm660-external.c b/asoc/sdm660-external.c index d4928664a975..8ff98eddef0e 100644 --- a/asoc/sdm660-external.c +++ b/asoc/sdm660-external.c @@ -121,7 +121,9 @@ static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16", "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 char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; 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); @@ -312,7 +314,16 @@ static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, * value. */ switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: ucontrol->value.integer.value[0] = 2; break; case SAMPLING_RATE_16KHZ: @@ -338,9 +349,21 @@ static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; break; case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; case 0: default: slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c index 14b04cf493d6..7e02e21f3629 100644 --- a/asoc/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -151,7 +151,9 @@ 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 char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; 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); @@ -836,7 +838,16 @@ static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol, * value. */ switch (bt_fm_cfg[BT_SLIM7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: ucontrol->value.integer.value[0] = 2; break; case SAMPLING_RATE_16KHZ: @@ -861,8 +872,17 @@ static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_16KHZ; break; case 2: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_48KHZ; break; + case 4: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_96KHZ; + break; case 0: default: bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_8KHZ; From 560b5075c9e192e5c56b5d9a60a9c61e5aaa7387 Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Mon, 11 Dec 2017 16:13:32 +0800 Subject: [PATCH 230/276] dsp: send spkr prot calibration after default acdb. send dynamic limiter threshold value for spv3 after default ACDB. Otherwise, dynamic value gets overwritten by default ACDB value. Change-Id: Icf509ba3398920439f8bad2a861f946c7472fe78 Signed-off-by: Xiaojun Sang --- dsp/q6afe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index d73f2177ebc2..c1f58802dd1f 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -1638,8 +1638,8 @@ void afe_send_cal(u16 port_id) if (ret < 0) send_afe_cal_type(AFE_LSM_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); + afe_send_cal_spkr_prot_rx(port_id); } } From 822b29645ce72ef0523af6203eb51a70b063d68f Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Thu, 4 Jan 2018 11:04:00 +0530 Subject: [PATCH 231/276] asoc: Add new dais to support compress record. Add new dais to support compress record usecase. Update media_encoder_format api to v4 version in compress driver. CRs-Fixed: 2167556 Change-Id: I8a2f8a4cdcec4bb31ad94730eb4f1259e800c70c Signed-off-by: Sachin Mohan Gadag --- asoc/msm-compress-q6-v2.c | 5 +- asoc/msm-dai-fe.c | 48 +++++++- asoc/msm-pcm-routing-v2.c | 225 +++++++++++++++++++++++++++++++++++++- asoc/msm-pcm-routing-v2.h | 8 +- 4 files changed, 275 insertions(+), 11 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index 497566b27f82..cbfd76c221ba 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -1521,9 +1521,10 @@ static int msm_compr_configure_dsp_for_capture(struct snd_compr_stream *cstream) 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, + ret = q6asm_enc_cfg_blk_pcm_format_support_v4(prtd->audio_client, prtd->sample_rate, prtd->num_channels, - bits_per_sample, sample_word_size); + bits_per_sample, sample_word_size, + ASM_LITTLE_ENDIAN, DEFAULT_QF); return ret; } diff --git a/asoc/msm-dai-fe.c b/asoc/msm-dai-fe.c index e57c3e3a2605..c9f35afb7109 100644 --- a/asoc/msm-dai-fe.c +++ b/asoc/msm-dai-fe.c @@ -2483,7 +2483,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .capture = { .stream_name = "MultiMedia17 Capture", .aif_name = "MM_UL17", - .rates = (SNDRV_PCM_RATE_8000_48000| + .rates = (SNDRV_PCM_RATE_8000_192000| SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | @@ -2491,7 +2491,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .ops = &msm_fe_Multimedia_dai_ops, .compress_new = snd_soc_new_compress, @@ -2502,7 +2502,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .capture = { .stream_name = "MultiMedia18 Capture", .aif_name = "MM_UL18", - .rates = (SNDRV_PCM_RATE_8000_48000| + .rates = (SNDRV_PCM_RATE_8000_192000| SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | @@ -2521,7 +2521,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .capture = { .stream_name = "MultiMedia19 Capture", .aif_name = "MM_UL19", - .rates = (SNDRV_PCM_RATE_8000_48000| + .rates = (SNDRV_PCM_RATE_8000_192000| SNDRV_PCM_RATE_KNOT), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | @@ -2529,7 +2529,7 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .channels_min = 1, .channels_max = 8, .rate_min = 8000, - .rate_max = 48000, + .rate_max = 192000, }, .ops = &msm_fe_Multimedia_dai_ops, .compress_new = snd_soc_new_compress, @@ -2569,6 +2569,44 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .name = "MultiMedia20", .probe = fe_dai_probe, }, + { + .capture = { + .stream_name = "MultiMedia28 Capture", + .aif_name = "MM_UL28", + .rates = (SNDRV_PCM_RATE_8000_192000| + 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 = 192000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia28", + .probe = fe_dai_probe, + }, + { + .capture = { + .stream_name = "MultiMedia29 Capture", + .aif_name = "MM_UL29", + .rates = (SNDRV_PCM_RATE_8000_192000| + 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 = 192000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .compress_new = snd_soc_new_compress, + .name = "MultiMedia29", + .probe = fe_dai_probe, + }, }; static int msm_fe_dai_dev_probe(struct platform_device *pdev) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index e58f1d4372a0..3af74c2cbe43 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -644,6 +644,12 @@ static struct msm_pcm_routing_fdai_data {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* MULTIMEDIA20 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA28 */ + {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, + {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, + /* MULTIMEDIA29 */ {{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} }, {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } }, /* VOIP */ @@ -3771,6 +3777,16 @@ static const struct snd_kcontrol_new ext_ec_ref_mux_ul19 = 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_ul28 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL28 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_ul29 = + SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL29 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) { @@ -3914,6 +3930,12 @@ static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_PRI_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = { @@ -3974,6 +3996,12 @@ static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SEC_I2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new spdif_rx_mixer_controls[] = { @@ -4034,7 +4062,12 @@ static const struct snd_kcontrol_new spdif_rx_mixer_controls[] = { 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), - + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SPDIF_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new slimbus_2_rx_mixer_controls[] = { @@ -4146,6 +4179,12 @@ static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SLIMBUS_5_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = { @@ -4206,6 +4245,12 @@ static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SLIMBUS_0_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = { @@ -4266,6 +4311,12 @@ static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = { @@ -4326,6 +4377,12 @@ static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = { @@ -4386,6 +4443,13 @@ static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_QUINARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + }; static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = { @@ -4440,6 +4504,12 @@ static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_TERTIARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new secondary_mi2s_rx2_mixer_controls[] = { @@ -4506,6 +4576,12 @@ static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SECONDARY_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = { @@ -4566,6 +4642,13 @@ static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_PRI_MI2S_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + }; static const struct snd_kcontrol_new int0_mi2s_rx_mixer_controls[] = { @@ -4728,6 +4811,12 @@ static const struct snd_kcontrol_new hdmi_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_HDMI_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new display_port_mixer_controls[] = { @@ -5038,6 +5127,12 @@ static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_INT_BT_SCO_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new int_bt_a2dp_rx_mixer_controls[] = { @@ -5149,6 +5244,12 @@ static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_INT_FM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = { @@ -5209,6 +5310,12 @@ static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_AFE_PCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = { @@ -5269,6 +5376,12 @@ static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = { @@ -5329,6 +5442,12 @@ static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = { 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), + SOC_SINGLE_EXT("MultiMedia28", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia29", MSM_BACKEND_DAI_SEC_AUXPCM_RX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new tert_auxpcm_rx_mixer_controls[] = { @@ -7874,6 +7993,9 @@ static const struct snd_kcontrol_new mmul17_mixer_controls[] = { 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("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_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), @@ -7898,6 +8020,9 @@ static const struct snd_kcontrol_new mmul18_mixer_controls[] = { 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("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX, + MSM_FRONTEND_DAI_MULTIMEDIA18, 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_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), @@ -7925,6 +8050,9 @@ static const struct snd_kcontrol_new mmul19_mixer_controls[] = { 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("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_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), @@ -8020,6 +8148,60 @@ static const struct snd_kcontrol_new mmul20_mixer_controls[] = { msm_routing_put_audio_mixer), }; +static const struct snd_kcontrol_new mmul28_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA28, 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_MULTIMEDIA28, 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_MULTIMEDIA28, 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_MULTIMEDIA28, 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_MULTIMEDIA28, 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_MULTIMEDIA28, 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_MULTIMEDIA28, 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_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), +}; + +static const struct snd_kcontrol_new mmul29_mixer_controls[] = { + SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX, + MSM_FRONTEND_DAI_MULTIMEDIA29, 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_MULTIMEDIA29, 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_MULTIMEDIA29, 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_MULTIMEDIA29, 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_MULTIMEDIA29, 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_MULTIMEDIA29, 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_MULTIMEDIA29, 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_MULTIMEDIA29, 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("Voip", MSM_BACKEND_DAI_PRI_I2S_RX, MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer, @@ -13277,6 +13459,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { mmul19_mixer_controls, ARRAY_SIZE(mmul19_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia20 Mixer", SND_SOC_NOPM, 0, 0, mmul20_mixer_controls, ARRAY_SIZE(mmul20_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia28 Mixer", SND_SOC_NOPM, 0, 0, + mmul28_mixer_controls, ARRAY_SIZE(mmul28_mixer_controls)), + SND_SOC_DAPM_MIXER("MultiMedia29 Mixer", SND_SOC_NOPM, 0, 0, + mmul29_mixer_controls, ARRAY_SIZE(mmul29_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, @@ -13859,14 +14045,27 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia17 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia18 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia19 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia28 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, + {"MultiMedia29 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"}, + {"MultiMedia28 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia29 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, {"MultiMedia8 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"MultiMedia17 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia18 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia19 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia28 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia29 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"MultiMedia17 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"MultiMedia18 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia19 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia28 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"MultiMedia29 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"MultiMedia8 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia3 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, {"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"}, @@ -14557,6 +14756,11 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia10 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, {"MultiMedia16 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia17 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia18 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia19 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia28 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"}, + {"MultiMedia29 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"}, @@ -14864,6 +15068,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"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"}, + {"MultiMedia28 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, + {"MultiMedia29 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"}, {"MultiMedia16 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"}, @@ -14873,6 +15079,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia17 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MultiMedia18 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, {"MultiMedia19 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia28 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"}, + {"MultiMedia29 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"}, @@ -14884,6 +15092,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MultiMedia17 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia18 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia19 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia28 Mixer", "AFE_PCM_TX", "PCM_TX"}, + {"MultiMedia29 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia8 Mixer", "AFE_PCM_TX", "PCM_TX"}, {"MultiMedia16 Mixer", "AFE_PCM_TX", "PCM_TX"}, @@ -14902,6 +15112,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MM_UL18", NULL, "MultiMedia18 Mixer"}, {"MM_UL19", NULL, "MultiMedia19 Mixer"}, {"MM_UL20", NULL, "MultiMedia20 Mixer"}, + {"MM_UL28", NULL, "MultiMedia28 Mixer"}, + {"MM_UL29", NULL, "MultiMedia29 Mixer"}, {"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"}, {"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"}, @@ -15246,6 +15458,15 @@ static const struct snd_soc_dapm_route intercon[] = { {"AUDIO_REF_EC_UL19 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, {"AUDIO_REF_EC_UL19 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + {"AUDIO_REF_EC_UL28 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL28 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL28 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL28 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, + + {"AUDIO_REF_EC_UL29 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"}, + {"AUDIO_REF_EC_UL29 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"}, + {"AUDIO_REF_EC_UL29 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"}, + {"AUDIO_REF_EC_UL29 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"}, {"LSM1_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, {"LSM2_UL_HL", NULL, "AUDIO_REF_EC_UL1 MUX"}, @@ -15269,6 +15490,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"MM_UL17", NULL, "AUDIO_REF_EC_UL17 MUX"}, {"MM_UL18", NULL, "AUDIO_REF_EC_UL18 MUX"}, {"MM_UL19", NULL, "AUDIO_REF_EC_UL19 MUX"}, + {"MM_UL28", NULL, "AUDIO_REF_EC_UL28 MUX"}, + {"MM_UL29", NULL, "AUDIO_REF_EC_UL29 MUX"}, {"VoiceMMode1_Tx Mixer", "PRI_TX_MMode1", "PRI_I2S_TX"}, {"VoiceMMode1_Tx Mixer", "PRI_MI2S_TX_MMode1", "PRI_MI2S_TX"}, diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index a1977b4b3737..19c710c4b4a5 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -211,6 +211,8 @@ enum { MSM_FRONTEND_DAI_MULTIMEDIA18, MSM_FRONTEND_DAI_MULTIMEDIA19, MSM_FRONTEND_DAI_MULTIMEDIA20, + MSM_FRONTEND_DAI_MULTIMEDIA28, + MSM_FRONTEND_DAI_MULTIMEDIA29, MSM_FRONTEND_DAI_VOIP, MSM_FRONTEND_DAI_AFE_RX, MSM_FRONTEND_DAI_AFE_TX, @@ -232,8 +234,8 @@ enum { MSM_FRONTEND_DAI_MAX, }; -#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA20 + 1) -#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA20 +#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA29 + 1) +#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA29 enum { MSM_BACKEND_DAI_PRI_I2S_RX = 0, From 08fd27436fc37a1212a64eb93e69080542b56ae0 Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Mon, 8 Jan 2018 15:13:33 +0530 Subject: [PATCH 232/276] asoc: sdm660: Add new dais for multiple record session. Add new dais to support multiple record session in compress path. CRs-Fixed: 2167556 Change-Id: I2020adc2fd2e56e7ad76529e53a772963a64cdd9 Signed-off-by: Sachin Mohan Gadag --- asoc/sdm660-internal.c | 78 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/asoc/sdm660-internal.c b/asoc/sdm660-internal.c index 7e02e21f3629..fc141e32530e 100644 --- a/asoc/sdm660-internal.c +++ b/asoc/sdm660-internal.c @@ -2360,6 +2360,79 @@ static struct snd_soc_dai_link msm_int_wsa_dai[] = { }, }; +static struct snd_soc_dai_link msm_int_compress_capture_dai[] = { + {/* hw:x,41 */ + .name = "Compress9", + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia17", + .platform_name = "msm-compress-dsp", + .dynamic = 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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA17, + }, + {/* hw:x,42 */ + .name = "Compress10", + .stream_name = "Compress10", + .cpu_dai_name = "MultiMedia18", + .platform_name = "msm-compress-dsp", + .dynamic = 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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA18, + }, + {/* hw:x,43 */ + .name = "Compress11", + .stream_name = "Compress11", + .cpu_dai_name = "MultiMedia19", + .platform_name = "msm-compress-dsp", + .dynamic = 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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA19, + }, + {/* hw:x,44 */ + .name = "Compress12", + .stream_name = "Compress12", + .cpu_dai_name = "MultiMedia28", + .platform_name = "msm-compress-dsp", + .dynamic = 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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA28, + }, + {/* hw:x,45 */ + .name = "Compress13", + .stream_name = "Compress13", + .cpu_dai_name = "MultiMedia29", + .platform_name = "msm-compress-dsp", + .dynamic = 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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA29, + }, +}; + static struct snd_soc_dai_link msm_int_be_dai[] = { /* Backend I2S DAI Links */ { @@ -3064,6 +3137,7 @@ static struct snd_soc_dai_link ext_disp_be_dai_link[] = { static struct snd_soc_dai_link msm_int_dai_links[ ARRAY_SIZE(msm_int_dai) + ARRAY_SIZE(msm_int_wsa_dai) + +ARRAY_SIZE(msm_int_compress_capture_dai) + ARRAY_SIZE(msm_int_be_dai) + ARRAY_SIZE(msm_mi2s_be_dai_links) + ARRAY_SIZE(msm_auxpcm_be_dai_links)+ @@ -3142,6 +3216,10 @@ static struct snd_soc_card *msm_int_populate_sndcard_dailinks( sizeof(msm_int_wsa_dai)); len1 += ARRAY_SIZE(msm_int_wsa_dai); } + memcpy(dailink + len1, msm_int_compress_capture_dai, + sizeof(msm_int_compress_capture_dai)); + len1 += ARRAY_SIZE(msm_int_compress_capture_dai); + memcpy(dailink + len1, msm_int_be_dai, sizeof(msm_int_be_dai)); len1 += ARRAY_SIZE(msm_int_be_dai); From 9691a770290f61a929c1b80c3ac9b3a5c503a184 Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Tue, 2 Jan 2018 18:17:22 +0530 Subject: [PATCH 233/276] asoc: msm-cpe-lsm: use kmalloc for slim_port_xfer buffers. Use kmalloc instead of dma_alloc_coherent for slim buffers to support dma_mapping api in interrupt context. Change-Id: Idf06832b6a3bb30f6a16c168b4e5e5f13ad0a0ab Signed-off-by: Rohit kumar --- asoc/msm-cpe-lsm.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/asoc/msm-cpe-lsm.c b/asoc/msm-cpe-lsm.c index 99507d1167f4..256b236e3806 100644 --- a/asoc/msm-cpe-lsm.c +++ b/asoc/msm-cpe-lsm.c @@ -478,18 +478,21 @@ static int msm_cpe_lab_buf_alloc(struct snd_pcm_substream *substream, 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); + pcm_buf->mem = kzalloc(dma_alloc, GFP_DMA); if (!pcm_buf->mem) { - dev_err(rtd->dev, - "%s:DMA alloc failed size = %x\n", - __func__, dma_alloc); rc = -ENOMEM; goto fail; } + pcm_buf->phys = dma_map_single(dma_data->sdev->dev.parent, + pcm_buf->mem, dma_alloc, DMA_BIDIRECTIONAL); + if (dma_mapping_error(dma_data->sdev->dev.parent, pcm_buf->phys)) { + dev_err(rtd->dev, "%s Error mapping DMA buffers\n", __func__); + pcm_buf->phys = (phys_addr_t)NULL; + rc = -EFAULT; + goto fail; + } + count = 0; while (count < bufcnt) { pcm_buf[count].mem = pcm_buf[0].mem + (count * bufsz); @@ -504,13 +507,10 @@ static int msm_cpe_lab_buf_alloc(struct snd_pcm_substream *substream, 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; - } + if (pcm_buf) + kfree(pcm_buf->mem); + kfree(pcm_buf); + lab_d->pcm_buf = NULL; exit: return rc; } @@ -544,8 +544,12 @@ static int msm_cpe_lab_buf_dealloc(struct snd_pcm_substream *substream, 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); + if (pcm_buf->phys) + dma_unmap_single(dma_data->sdev->dev.parent, + pcm_buf->phys, dma_alloc, DMA_BIDIRECTIONAL); + if (pcm_buf) + kfree(pcm_buf->mem); + kfree(pcm_buf); lab_d->pcm_buf = NULL; return rc; @@ -633,7 +637,7 @@ static int msm_cpe_lab_thread(void *data) 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, + lab_d->pcm_buf[0].mem, hw_params->buf_sz, &lab_d->comp); if (rc) { dev_err(rtd->dev, @@ -643,7 +647,7 @@ static int msm_cpe_lab_thread(void *data) } rc = slim_port_xfer(dma_data->sdev, dma_data->ph, - lab_d->pcm_buf[1].phys, + lab_d->pcm_buf[1].mem, hw_params->buf_sz, &lab_d->comp); if (rc) { dev_err(rtd->dev, @@ -678,7 +682,7 @@ static int msm_cpe_lab_thread(void *data) lab_d->thread_status != MSM_LSM_LAB_THREAD_ERROR) { rc = slim_port_xfer(dma_data->sdev, dma_data->ph, - next_buf->phys, + next_buf->mem, hw_params->buf_sz, &lab_d->comp); if (rc) { dev_err(rtd->dev, From c735dc67e1d8571419f4747d2fcdea257225523a Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Thu, 25 Jan 2018 14:57:20 +0530 Subject: [PATCH 234/276] asoc: codecs: add a null pointer check in mbhc driver. Add a missing function pointer null check condition in mbhc driver to avoid null access during fast insertion-removal. Change-Id: I6aa64325cea6f7aa551115969d1e6ba184ec8d14 Signed-off-by: Ramprasad Katkam --- asoc/codecs/wcd-mbhc-legacy.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/asoc/codecs/wcd-mbhc-legacy.c b/asoc/codecs/wcd-mbhc-legacy.c index d03d8342a8df..9ebdeca01fff 100644 --- a/asoc/codecs/wcd-mbhc-legacy.c +++ b/asoc/codecs/wcd-mbhc-legacy.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -533,7 +533,8 @@ static void wcd_correct_swch_plug(struct work_struct *work) mbhc->hs_detect_work_stop); wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE); - if (mbhc->micbias_enable) { + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic && + mbhc->micbias_enable) { mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( mbhc->codec, MIC_BIAS_2, false); if (mbhc->mbhc_cb->set_micbias_value) @@ -558,7 +559,8 @@ static void wcd_correct_swch_plug(struct work_struct *work) mbhc->hs_detect_work_stop); wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE); - if (mbhc->micbias_enable) { + if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic && + mbhc->micbias_enable) { mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic( mbhc->codec, MIC_BIAS_2, false); if (mbhc->mbhc_cb->set_micbias_value) From dc3ddef9762f420d900fa38f75195fd77b33fb01 Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Thu, 8 Feb 2018 14:31:51 +0530 Subject: [PATCH 235/276] ASoC: wcd934x: Update OCP before HPH PA enable/disable Observe mute issue when PDR triggered while music playback on HPH. As per HW recommendation, ensure OCP is turned off before PA enable/disable. During PA enablement, turn off OCP to avoid false OCP interrupts. CRs-Fixed: 2167007 Change-Id: I0dc2cc6ded3416b635d14dc3b7aafa7997fe9a4d Signed-off-by: Laxminath Kasam --- asoc/codecs/wcd934x/wcd934x.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 607b74224d20..ddbb90839f1b 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -2185,6 +2185,18 @@ static void tavil_codec_clear_anc_tx_hold(struct tavil_priv *tavil) tavil_codec_set_tx_hold(tavil->codec, WCD934X_ANA_AMIC4, false); } + +static void tavil_ocp_control(struct snd_soc_codec *codec, bool enable) +{ + if (enable) { + snd_soc_update_bits(codec, WCD934X_HPH_OCP_CTL, 0x10, 0x10); + snd_soc_update_bits(codec, WCD934X_RX_OCP_CTL, 0x0F, 0x02); + } else { + snd_soc_update_bits(codec, WCD934X_RX_OCP_CTL, 0x0F, 0x0F); + snd_soc_update_bits(codec, WCD934X_HPH_OCP_CTL, 0x10, 0x00); + } +} + static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -2198,6 +2210,7 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + tavil_ocp_control(codec, false); if (TAVIL_IS_1_0(tavil->wcd9xxx)) snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, 0x06, (0x03 << 1)); @@ -2294,8 +2307,10 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, ret = tavil_codec_enable_anc(w, kcontrol, event); } tavil_codec_override(codec, tavil->hph_mode, event); + tavil_ocp_control(codec, true); break; case SND_SOC_DAPM_PRE_PMD: + tavil_ocp_control(codec, false); blocking_notifier_call_chain(&tavil->mbhc->notifier, WCD_EVENT_PRE_HPHR_PA_OFF, &tavil->mbhc->wcd_mbhc); @@ -2334,6 +2349,7 @@ static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w, WCD934X_CDC_RX2_RX_PATH_CFG0, 0x10, 0x00); } + tavil_ocp_control(codec, true); break; }; @@ -2353,6 +2369,7 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_PRE_PMU: + tavil_ocp_control(codec, false); if (TAVIL_IS_1_0(tavil->wcd9xxx)) snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL, 0x06, (0x03 << 1)); @@ -2446,8 +2463,10 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, ret = tavil_codec_enable_anc(w, kcontrol, event); } tavil_codec_override(codec, tavil->hph_mode, event); + tavil_ocp_control(codec, true); break; case SND_SOC_DAPM_PRE_PMD: + tavil_ocp_control(codec, false); blocking_notifier_call_chain(&tavil->mbhc->notifier, WCD_EVENT_PRE_HPHL_PA_OFF, &tavil->mbhc->wcd_mbhc); @@ -2487,6 +2506,7 @@ static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CFG0, 0x10, 0x00); } + tavil_ocp_control(codec, true); break; }; From 9c6861bc82bcbb13818a4fd4719d43e3e8d7ed58 Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Fri, 23 Feb 2018 16:34:40 +0530 Subject: [PATCH 236/276] asoc: add voice routing for QUAT and QUIN MI2S interfaces Add Tx mixer routes for Quaternary and Quinary MI2S to support voice call over these interfaces. CRs-Fixed: 2194204 Change-Id: I33b0c0528fbbfe8cf17ce3fe14dbf2230f05d860 Signed-off-by: Aditya Bavanari --- asoc/msm-pcm-routing-v2.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 3af74c2cbe43..1b1d2209a6fb 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -8749,6 +8749,12 @@ static const struct snd_kcontrol_new tx_voicemmode1_mixer_controls[] = { SOC_SINGLE_EXT("QUAT_TDM_TX_0_MMode1", MSM_BACKEND_DAI_QUAT_TDM_TX_0, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QUAT_MI2S_TX_MMode1", + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QUIN_MI2S_TX_MMode1", + MSM_BACKEND_DAI_QUINARY_MI2S_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[] = { @@ -8800,6 +8806,12 @@ static const struct snd_kcontrol_new tx_voicemmode2_mixer_controls[] = { 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), + SOC_SINGLE_EXT("QUAT_MI2S_TX_MMode2", + MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, + msm_routing_get_voice_mixer, msm_routing_put_voice_mixer), + SOC_SINGLE_EXT("QUIN_MI2S_TX_MMode2", + MSM_BACKEND_DAI_QUINARY_MI2S_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[] = { @@ -15510,6 +15522,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"VoiceMMode1_Tx Mixer", "QUAT_AUX_PCM_TX_MMode1", "QUAT_AUX_PCM_TX"}, {"VoiceMMode1_Tx Mixer", "QUIN_AUX_PCM_TX_MMode1", "QUIN_AUX_PCM_TX"}, {"VoiceMMode1_Tx Mixer", "QUAT_TDM_TX_0_MMode1", "QUAT_TDM_TX_0"}, + {"VoiceMMode1_Tx Mixer", "QUAT_MI2S_TX_MMode1", "QUAT_MI2S_TX"}, + {"VoiceMMode1_Tx Mixer", "QUIN_MI2S_TX_MMode1", "QUIN_MI2S_TX"}, {"VOICEMMODE1_UL", NULL, "VoiceMMode1_Tx Mixer"}, {"VoiceMMode2_Tx Mixer", "PRI_TX_MMode2", "PRI_I2S_TX"}, @@ -15528,6 +15542,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"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_Tx Mixer", "QUIN_AUX_PCM_TX_MMode2", "QUIN_AUX_PCM_TX"}, + {"VoiceMMode2_Tx Mixer", "QUAT_MI2S_TX_MMode2", "QUAT_MI2S_TX"}, + {"VoiceMMode2_Tx Mixer", "QUIN_MI2S_TX_MMode2", "QUIN_MI2S_TX"}, {"VOICEMMODE2_UL", NULL, "VoiceMMode2_Tx Mixer"}, {"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"}, From 6b4843de323e0481313a3db71bb5d23c7b0232fc Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Mon, 2 Apr 2018 18:33:49 +0530 Subject: [PATCH 237/276] asoc: sdm660: Add new dais for multiple record session. Add new dais to support multiple record session in compress path for tavil codec. Change-Id: I112e51019975393ccf9455418c5697f322265614 Signed-off-by: Sachin Mohan Gadag --- asoc/sdm660-ext-dai-links.c | 105 +++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 13 deletions(-) diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c index 02d35a9ebf7d..6a785b753b15 100644 --- a/asoc/sdm660-ext-dai-links.c +++ b/asoc/sdm660-ext-dai-links.c @@ -595,6 +595,79 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { }, }; +static struct snd_soc_dai_link msm_ext_compress_capture_dai[] = { + {/* tavil:hw:x,40 */ + .name = "Compress9", + .stream_name = "Compress9", + .cpu_dai_name = "MultiMedia17", + .platform_name = "msm-compress-dsp", + .dynamic = 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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA17, + }, + {/* tavil:hw:x,41 */ + .name = "Compress10", + .stream_name = "Compress10", + .cpu_dai_name = "MultiMedia18", + .platform_name = "msm-compress-dsp", + .dynamic = 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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA18, + }, + {/* tavil:hw:x,42 */ + .name = "Compress11", + .stream_name = "Compress11", + .cpu_dai_name = "MultiMedia19", + .platform_name = "msm-compress-dsp", + .dynamic = 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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA19, + }, + {/* tavil:hw:x,43 */ + .name = "Compress12", + .stream_name = "Compress12", + .cpu_dai_name = "MultiMedia28", + .platform_name = "msm-compress-dsp", + .dynamic = 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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA28, + }, + {/* tavil:hw:x,44 */ + .name = "Compress13", + .stream_name = "Compress13", + .cpu_dai_name = "MultiMedia29", + .platform_name = "msm-compress-dsp", + .dynamic = 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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA29, + }, +}; + static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = { { .name = LPASS_BE_SLIMBUS_0_RX, @@ -2010,6 +2083,7 @@ ARRAY_SIZE(ext_disp_be_dai_link)]; 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_compress_capture_dai) + ARRAY_SIZE(msm_ext_common_be_dai) + ARRAY_SIZE(msm_ext_tavil_be_dai) + ARRAY_SIZE(msm_mi2s_be_dai_links) + @@ -2029,7 +2103,7 @@ struct snd_soc_card *populate_snd_card_dailinks(struct device *dev, { struct snd_soc_card *card; struct snd_soc_dai_link *msm_ext_dai_links = NULL; - int ret, len1, len2, len3, len4; + int ret, len1, len2, len3, len4, len5; enum codec_variant codec_ver = 0; if (snd_card_val == EXT_SND_CARD_TASHA) { @@ -2098,50 +2172,55 @@ struct snd_soc_card *populate_snd_card_dailinks(struct device *dev, sizeof(ext_disp_be_dai_link)); len4 += ARRAY_SIZE(ext_disp_be_dai_link); } + len5 = len4; 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); + len3 = len2 + ARRAY_SIZE(msm_ext_compress_capture_dai); + len4 = len3 + 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)); + msm_ext_compress_capture_dai, + sizeof(msm_ext_compress_capture_dai)); memcpy(msm_ext_tavil_dai_links + len3, + msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai)); + memcpy(msm_ext_tavil_dai_links + len4, msm_ext_tavil_be_dai, sizeof(msm_ext_tavil_be_dai)); - len4 = len3 + ARRAY_SIZE(msm_ext_tavil_be_dai); + len5 = len4 + 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, + memcpy(msm_ext_tavil_dai_links + len5, msm_mi2s_be_dai_links, sizeof(msm_mi2s_be_dai_links)); - len4 += ARRAY_SIZE(msm_mi2s_be_dai_links); + len5 += 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, + memcpy(msm_ext_tavil_dai_links + len5, msm_auxpcm_be_dai_links, sizeof(msm_auxpcm_be_dai_links)); - len4 += ARRAY_SIZE(msm_auxpcm_be_dai_links); + len5 += 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, + memcpy(msm_ext_tavil_dai_links + len5, msm_wcn_be_dai_links, sizeof(msm_wcn_be_dai_links)); - len4 += ARRAY_SIZE(msm_wcn_be_dai_links); + len5 += 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_ext_tavil_dai_links + len4, + memcpy(msm_ext_tavil_dai_links + len5, ext_disp_be_dai_link, sizeof(ext_disp_be_dai_link)); - len4 += ARRAY_SIZE(ext_disp_be_dai_link); + len5 += ARRAY_SIZE(ext_disp_be_dai_link); } msm_ext_dai_links = msm_ext_tavil_dai_links; } else { @@ -2150,7 +2229,7 @@ struct snd_soc_card *populate_snd_card_dailinks(struct device *dev, return NULL; } card->dai_link = msm_ext_dai_links; - card->num_links = len4; + card->num_links = len5; return card; } From 7d70a0a2b97746c56516b598c81f98b9f5570733 Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Thu, 5 Apr 2018 14:31:12 +0530 Subject: [PATCH 238/276] asoc: sdm660: Add new dais for multiple record session. Add new dais to support multiple record session in compress path for tasha codec. Change-Id: Id787ac80a32afc78bbdb99bd41d0df649b5d17bb Signed-off-by: Sachin Mohan Gadag --- asoc/sdm660-ext-dai-links.c | 52 ++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/asoc/sdm660-ext-dai-links.c b/asoc/sdm660-ext-dai-links.c index 6a785b753b15..b26430ce9816 100644 --- a/asoc/sdm660-ext-dai-links.c +++ b/asoc/sdm660-ext-dai-links.c @@ -596,7 +596,7 @@ static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = { }; static struct snd_soc_dai_link msm_ext_compress_capture_dai[] = { - {/* tavil:hw:x,40 */ + {/* hw:x,37 */ .name = "Compress9", .stream_name = "Compress9", .cpu_dai_name = "MultiMedia17", @@ -610,7 +610,7 @@ static struct snd_soc_dai_link msm_ext_compress_capture_dai[] = { .ignore_suspend = 1, .id = MSM_FRONTEND_DAI_MULTIMEDIA17, }, - {/* tavil:hw:x,41 */ + {/* hw:x,38 */ .name = "Compress10", .stream_name = "Compress10", .cpu_dai_name = "MultiMedia18", @@ -624,7 +624,7 @@ static struct snd_soc_dai_link msm_ext_compress_capture_dai[] = { .ignore_suspend = 1, .id = MSM_FRONTEND_DAI_MULTIMEDIA18, }, - {/* tavil:hw:x,42 */ + {/* hw:x,39 */ .name = "Compress11", .stream_name = "Compress11", .cpu_dai_name = "MultiMedia19", @@ -638,7 +638,7 @@ static struct snd_soc_dai_link msm_ext_compress_capture_dai[] = { .ignore_suspend = 1, .id = MSM_FRONTEND_DAI_MULTIMEDIA19, }, - {/* tavil:hw:x,43 */ + {/* hw:x,40 */ .name = "Compress12", .stream_name = "Compress12", .cpu_dai_name = "MultiMedia28", @@ -652,7 +652,7 @@ static struct snd_soc_dai_link msm_ext_compress_capture_dai[] = { .ignore_suspend = 1, .id = MSM_FRONTEND_DAI_MULTIMEDIA28, }, - {/* tavil:hw:x,44 */ + {/* hw:x,41 */ .name = "Compress13", .stream_name = "Compress13", .cpu_dai_name = "MultiMedia29", @@ -2072,6 +2072,7 @@ static struct snd_soc_dai_link ext_disp_be_dai_link[] = { static struct snd_soc_dai_link msm_ext_tasha_dai_links[ ARRAY_SIZE(msm_ext_common_fe_dai) + +ARRAY_SIZE(msm_ext_compress_capture_dai) + ARRAY_SIZE(msm_ext_tasha_fe_dai) + ARRAY_SIZE(msm_ext_common_be_dai) + ARRAY_SIZE(msm_ext_tasha_be_dai) + @@ -2082,8 +2083,8 @@ ARRAY_SIZE(ext_disp_be_dai_link)]; 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_compress_capture_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) + @@ -2130,62 +2131,65 @@ struct snd_soc_card *populate_snd_card_dailinks(struct device *dev, card->name = "sdm660-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); + len2 = len1 + ARRAY_SIZE(msm_ext_compress_capture_dai); + len3 = len2 + ARRAY_SIZE(msm_ext_tasha_fe_dai); + len4 = len3 + 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)); + msm_ext_compress_capture_dai, + sizeof(msm_ext_compress_capture_dai)); memcpy(msm_ext_tasha_dai_links + len2, - msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai)); + msm_ext_tasha_fe_dai, sizeof(msm_ext_tasha_fe_dai)); memcpy(msm_ext_tasha_dai_links + len3, + msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai)); + memcpy(msm_ext_tasha_dai_links + len4, msm_ext_tasha_be_dai, sizeof(msm_ext_tasha_be_dai)); - len4 = len3 + ARRAY_SIZE(msm_ext_tasha_be_dai); + len5 = len4 + 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, + memcpy(msm_ext_tasha_dai_links + len5, msm_mi2s_be_dai_links, sizeof(msm_mi2s_be_dai_links)); - len4 += ARRAY_SIZE(msm_mi2s_be_dai_links); + len5 += 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, + memcpy(msm_ext_tasha_dai_links + len5, msm_auxpcm_be_dai_links, sizeof(msm_auxpcm_be_dai_links)); - len4 += ARRAY_SIZE(msm_auxpcm_be_dai_links); + len5 += 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, + memcpy(msm_ext_tasha_dai_links + len5, msm_wcn_be_dai_links, sizeof(msm_wcn_be_dai_links)); - len4 += ARRAY_SIZE(msm_wcn_be_dai_links); + len5 += 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_ext_tasha_dai_links + len4, + memcpy(msm_ext_tasha_dai_links + len5, ext_disp_be_dai_link, sizeof(ext_disp_be_dai_link)); - len4 += ARRAY_SIZE(ext_disp_be_dai_link); + len5 += ARRAY_SIZE(ext_disp_be_dai_link); } - len5 = len4; 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_compress_capture_dai); + len2 = len1 + ARRAY_SIZE(msm_ext_compress_capture_dai); + len3 = len2 + ARRAY_SIZE(msm_ext_tavil_fe_dai); len4 = len3 + 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_compress_capture_dai, sizeof(msm_ext_compress_capture_dai)); + memcpy(msm_ext_tavil_dai_links + len2, + msm_ext_tavil_fe_dai, sizeof(msm_ext_tavil_fe_dai)); memcpy(msm_ext_tavil_dai_links + len3, msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai)); memcpy(msm_ext_tavil_dai_links + len4, From 9bda1d1142c18ea56ce615b4b457a95c952872aa Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Thu, 5 Jul 2018 15:48:12 +0530 Subject: [PATCH 239/276] asoc: Fix bytes_read in compress record usecase with timestamp In compress_capture usecase with timestamp enabled, distortion is observed when we record for long time. This is because of incorrect calculation of buffer_sent, to fix add timestamp header offset along with buffer length to bytes_read. Change-Id: I82fcab1d242380a5b2732953feb91b9353b26b12 Signed-off-by: Sachin Mohan Gadag --- asoc/msm-compress-q6-v2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index cbfd76c221ba..a976c05adb3b 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -540,7 +540,7 @@ static int msm_compr_read_buffer(struct msm_compr_audio *prtd) __func__, ret); return ret; } - prtd->bytes_read += buffer_length; + prtd->bytes_read += buffer_length + prtd->ts_header_offset; prtd->bytes_read_offset += buffer_length + prtd->ts_header_offset; if (prtd->bytes_read_offset >= prtd->buffer_size) prtd->bytes_read_offset -= prtd->buffer_size; From 89f3083702a5211b46204487d5149318a99f0068 Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Mon, 25 Jun 2018 15:59:23 +0530 Subject: [PATCH 240/276] asoc: add BT RX and TX mixer ctrls. Add mixer ctrls to independently configure TX and RX sample rates to allow BT RX and TX backends. CRs-Fixed: 2267622 Change-Id: Ieb49275231d63d03862148350afb2e235f8dedc7 Signed-off-by: Preetam Singh Ranawat --- asoc/sdm660-external.c | 139 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 1 deletion(-) diff --git a/asoc/sdm660-external.c b/asoc/sdm660-external.c index 8ff98eddef0e..91accff85fa1 100644 --- a/asoc/sdm660-external.c +++ b/asoc/sdm660-external.c @@ -124,7 +124,12 @@ static const char *const spk_function_text[] = {"Off", "On"}; static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_44P1", "KHZ_48", "KHZ_88P2", "KHZ_96"}; - +static char const *bt_sample_rate_rx_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; +static char const *bt_sample_rate_tx_text[] = {"KHZ_8", "KHZ_16", + "KHZ_44P1", "KHZ_48", + "KHZ_88P2", "KHZ_96"}; 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); @@ -143,6 +148,8 @@ 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(bt_sample_rate_rx, bt_sample_rate_rx_text); +static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate_tx, bt_sample_rate_tx_text); static int slim_get_sample_rate_val(int sample_rate) { @@ -379,6 +386,130 @@ static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol, return 0; } +static int msm_bt_sample_rate_rx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim_rx_cfg[SLIM_RX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + 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_rx_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; + break; + case 2: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_rx = %d, value = %d\n", + __func__, + slim_rx_cfg[SLIM_RX_7].sample_rate, + ucontrol->value.enumerated.item[0]); + + return 0; +} + +static int msm_bt_sample_rate_tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (slim_tx_cfg[SLIM_TX_7].sample_rate) { + case SAMPLING_RATE_96KHZ: + ucontrol->value.integer.value[0] = 5; + break; + case SAMPLING_RATE_88P2KHZ: + ucontrol->value.integer.value[0] = 4; + break; + case SAMPLING_RATE_48KHZ: + ucontrol->value.integer.value[0] = 3; + break; + case SAMPLING_RATE_44P1KHZ: + 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_tx_cfg[SLIM_TX_7].sample_rate); + + return 0; +} + +static int msm_bt_sample_rate_tx_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + switch (ucontrol->value.integer.value[0]) { + case 1: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ; + break; + case 2: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_44P1KHZ; + break; + case 3: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ; + break; + case 4: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_88P2KHZ; + break; + case 5: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_96KHZ; + break; + case 0: + default: + slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ; + break; + } + pr_debug("%s: sample rates: slim7_tx = %d, value = %d\n", + __func__, + 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) { @@ -750,6 +881,12 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { SOC_ENUM_EXT("BT SampleRate", bt_sample_rate, msm_bt_sample_rate_get, msm_bt_sample_rate_put), + SOC_ENUM_EXT("BT SampleRate RX", bt_sample_rate_rx, + msm_bt_sample_rate_rx_get, + msm_bt_sample_rate_rx_put), + SOC_ENUM_EXT("BT SampleRate TX", bt_sample_rate_tx, + msm_bt_sample_rate_tx_get, + msm_bt_sample_rate_tx_put), }; static int msm_slim_get_ch_from_beid(int32_t id) From 6e21d4d69ca14e1065512b2e4851d65d77e0137f Mon Sep 17 00:00:00 2001 From: Preetam Singh Ranawat Date: Thu, 31 May 2018 11:57:51 +0530 Subject: [PATCH 241/276] asoc: codecs: Fix out of bound register access For TX5 MUX registers, offset is not followed in TXn order. Update driver to read/write correct register offset when TX5 MUX registers access. CRs-Fixed: 2218938 Change-Id: I8958b6cd1847967cbd37e7145c9f3909b0b8853b Signed-off-by: Laxminath Kasam --- asoc/codecs/sdm660_cdc/msm-digital-cdc.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c index 2c3d0d66afca..61b2ad4dbd8a 100644 --- a/asoc/codecs/sdm660_cdc/msm-digital-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-digital-cdc.c @@ -39,6 +39,7 @@ #define CF_MIN_3DB_75HZ 0x1 #define CF_MIN_3DB_150HZ 0x2 +#define DEC_SVA 5 #define MSM_DIG_CDC_VERSION_ENTRY_SIZE 32 static unsigned long rx_digital_gain_reg[] = { @@ -208,6 +209,9 @@ static int msm_dig_cdc_put_dec_enum(struct snd_kcontrol *kcontrol, tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL + 32 * (decimator - 1); + if (decimator == DEC_SVA) + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX5_MUX_CTL; + snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x1, adc_dmic_sel); ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); @@ -604,6 +608,9 @@ static void tx_hpf_corner_freq_callback(struct work_struct *work) tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL + (hpf_work->decimator - 1) * 32; + if (hpf_work->decimator == DEC_SVA) + tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX5_MUX_CTL; + dev_dbg(codec->dev, "%s(): decimator %u hpf_cut_of_freq 0x%x\n", __func__, hpf_work->decimator, (unsigned int)hpf_cut_of_freq); msm_dig_cdc->update_clkdiv(msm_dig_cdc->handle, 0x51); @@ -934,7 +941,7 @@ static int msm_dig_cdc_codec_enable_dec(struct snd_soc_dapm_widget *w, 32 * (decimator - 1); tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL + 32 * (decimator - 1); - if (decimator == 5) { + if (decimator == DEC_SVA) { tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG; tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX5_MUX_CTL; } @@ -1245,15 +1252,19 @@ static void sdm660_tx_mute_update_callback(struct work_struct *work) dig_cdc = tx_mute_dwork->dig_cdc; codec = dig_cdc->codec; - for (i = 0; i < (NUM_DECIMATORS - 1); i++) { + for (i = 0; i < NUM_DECIMATORS; i++) { if (dig_cdc->dec_active[i]) decimator = i + 1; - if (decimator && decimator < NUM_DECIMATORS) { + if (decimator && decimator <= NUM_DECIMATORS) { /* unmute decimators corresponding to Tx DAI's*/ tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG + 32 * (decimator - 1); - snd_soc_update_bits(codec, tx_vol_ctl_reg, + if (decimator == DEC_SVA) + tx_vol_ctl_reg = + MSM89XX_CDC_CORE_TX5_VOL_CTL_CFG; + + snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00); } decimator = 0; From 0be1a8917175708f52cc1dd05c49290de7bdfbb8 Mon Sep 17 00:00:00 2001 From: Tanya Dixit Date: Tue, 4 Sep 2018 19:20:29 +0530 Subject: [PATCH 242/276] rtac: Add mutex lock to ensure proper fops access Add mutex lock in rtac_open and rtac_release to avoid usage count discrepancies leading to multiple calls to unmap memory resulting in null pointer dereference. CRs-Fixed: 2271712 Change-Id: Ie6da28837c352030b8d7e377d68a70cf04e7072a Signed-off-by: Tanya Dixit --- dsp/rtac.c | 44 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/dsp/rtac.c b/dsp/rtac.c index 976a67e3d039..baa33c7e449e 100644 --- a/dsp/rtac.c +++ b/dsp/rtac.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -53,6 +53,7 @@ struct rtac_cal_block_data rtac_cal[MAX_RTAC_BLOCKS] = { struct rtac_common_data { atomic_t usage_count; atomic_t apr_err_code; + struct mutex rtac_fops_mutex; }; static struct rtac_common_data rtac_common; @@ -125,10 +126,6 @@ struct mutex rtac_asm_apr_mutex; struct mutex rtac_voice_mutex; struct mutex rtac_voice_apr_mutex; struct mutex rtac_afe_apr_mutex; -struct mutex rtac_asm_cal_mutex; -struct mutex rtac_adm_cal_mutex; -struct mutex rtac_afe_cal_mutex; -struct mutex rtac_voice_cal_mutex; int rtac_clear_mapping(uint32_t cal_type) { @@ -329,7 +326,9 @@ static int rtac_open(struct inode *inode, struct file *f) pr_debug("%s\n", __func__); + mutex_lock(&rtac_common.rtac_fops_mutex); atomic_inc(&rtac_common.usage_count); + mutex_unlock(&rtac_common.rtac_fops_mutex); return result; } @@ -341,12 +340,15 @@ static int rtac_release(struct inode *inode, struct file *f) pr_debug("%s\n", __func__); + mutex_lock(&rtac_common.rtac_fops_mutex); 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) + if (atomic_read(&rtac_common.usage_count) > 0) { + mutex_unlock(&rtac_common.rtac_fops_mutex); goto done; + } for (i = 0; i < MAX_RTAC_BLOCKS; i++) { result2 = rtac_unmap_cal_buffer(i); @@ -363,6 +365,7 @@ static int rtac_release(struct inode *inode, struct file *f) result = result2; } } + mutex_unlock(&rtac_common.rtac_fops_mutex); done: return result; } @@ -1727,62 +1730,42 @@ static long rtac_ioctl_shared(struct file *f, } case AUDIO_GET_RTAC_ADM_CAL: - mutex_lock(&rtac_adm_cal_mutex); result = send_adm_apr((void *)arg, ADM_CMD_GET_PP_PARAMS_V5); - mutex_unlock(&rtac_adm_cal_mutex); break; case AUDIO_SET_RTAC_ADM_CAL: - mutex_lock(&rtac_adm_cal_mutex); result = send_adm_apr((void *)arg, ADM_CMD_SET_PP_PARAMS_V5); - mutex_unlock(&rtac_adm_cal_mutex); break; case AUDIO_GET_RTAC_ASM_CAL: - mutex_lock(&rtac_asm_cal_mutex); result = send_rtac_asm_apr((void *)arg, ASM_STREAM_CMD_GET_PP_PARAMS_V2); - mutex_unlock(&rtac_asm_cal_mutex); break; case AUDIO_SET_RTAC_ASM_CAL: - mutex_lock(&rtac_asm_cal_mutex); result = send_rtac_asm_apr((void *)arg, ASM_STREAM_CMD_SET_PP_PARAMS_V2); - mutex_unlock(&rtac_asm_cal_mutex); break; case AUDIO_GET_RTAC_CVS_CAL: - mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVS, (void *) arg, VSS_ICOMMON_CMD_GET_PARAM_V2); - mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_SET_RTAC_CVS_CAL: - mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVS, (void *) arg, VSS_ICOMMON_CMD_SET_PARAM_V2); - mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_GET_RTAC_CVP_CAL: - mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVP, (void *) arg, VSS_ICOMMON_CMD_GET_PARAM_V2); - mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_SET_RTAC_CVP_CAL: - mutex_lock(&rtac_voice_cal_mutex); result = send_voice_apr(RTAC_CVP, (void *) arg, VSS_ICOMMON_CMD_SET_PARAM_V2); - mutex_unlock(&rtac_voice_cal_mutex); break; case AUDIO_GET_RTAC_AFE_CAL: - mutex_lock(&rtac_afe_cal_mutex); result = send_rtac_afe_apr((void *)arg, AFE_PORT_CMD_GET_PARAM_V2); - mutex_unlock(&rtac_afe_cal_mutex); break; case AUDIO_SET_RTAC_AFE_CAL: - mutex_lock(&rtac_afe_cal_mutex); result = send_rtac_afe_apr((void *)arg, AFE_PORT_CMD_SET_PARAM_V2); - mutex_unlock(&rtac_afe_cal_mutex); break; default: pr_err("%s: Invalid IOCTL, command = %d!\n", @@ -1798,6 +1781,7 @@ static long rtac_ioctl(struct file *f, { int result = 0; + mutex_lock(&rtac_common.rtac_fops_mutex); if (!arg) { pr_err("%s: No data sent to driver!\n", __func__); result = -EFAULT; @@ -1805,6 +1789,7 @@ static long rtac_ioctl(struct file *f, result = rtac_ioctl_shared(f, cmd, (void __user *)arg); } + mutex_unlock(&rtac_common.rtac_fops_mutex); return result; } @@ -1827,6 +1812,7 @@ static long rtac_compat_ioctl(struct file *f, { int result = 0; + mutex_lock(&rtac_common.rtac_fops_mutex); if (!arg) { pr_err("%s: No data sent to driver!\n", __func__); result = -EINVAL; @@ -1879,6 +1865,7 @@ static long rtac_compat_ioctl(struct file *f, break; } done: + mutex_unlock(&rtac_common.rtac_fops_mutex); return result; } #else @@ -1906,6 +1893,7 @@ static int __init rtac_init(void) /* Driver */ atomic_set(&rtac_common.usage_count, 0); atomic_set(&rtac_common.apr_err_code, 0); + mutex_init(&rtac_common.rtac_fops_mutex); /* ADM */ memset(&rtac_adm_data, 0, sizeof(rtac_adm_data)); @@ -1914,7 +1902,6 @@ static int __init rtac_init(void) init_waitqueue_head(&rtac_adm_apr_data.cmd_wait); mutex_init(&rtac_adm_mutex); mutex_init(&rtac_adm_apr_mutex); - mutex_init(&rtac_adm_cal_mutex); rtac_adm_buffer = kzalloc( rtac_cal[ADM_RTAC_CAL].map_data.map_size, GFP_KERNEL); @@ -1928,7 +1915,6 @@ static int __init rtac_init(void) init_waitqueue_head(&rtac_asm_apr_data[i].cmd_wait); } mutex_init(&rtac_asm_apr_mutex); - mutex_init(&rtac_asm_cal_mutex); rtac_asm_buffer = kzalloc( rtac_cal[ASM_RTAC_CAL].map_data.map_size, GFP_KERNEL); @@ -1942,7 +1928,6 @@ static int __init rtac_init(void) atomic_set(&rtac_afe_apr_data.cmd_state, 0); init_waitqueue_head(&rtac_afe_apr_data.cmd_wait); mutex_init(&rtac_afe_apr_mutex); - mutex_init(&rtac_afe_cal_mutex); rtac_afe_buffer = kzalloc( rtac_cal[AFE_RTAC_CAL].map_data.map_size, GFP_KERNEL); @@ -1961,7 +1946,6 @@ static int __init rtac_init(void) } mutex_init(&rtac_voice_mutex); mutex_init(&rtac_voice_apr_mutex); - mutex_init(&rtac_voice_cal_mutex); rtac_voice_buffer = kzalloc( rtac_cal[VOICE_RTAC_CAL].map_data.map_size, GFP_KERNEL); From 59094d9d049bfe0872698723e58293688296ea4f Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Fri, 31 Aug 2018 11:02:53 -0700 Subject: [PATCH 243/276] asoc: codecs: add missing mbhc register for wcd9335 IN2P clamp state mbhc register was added in tavil codec to fix fake insertion irq. Add this register in wcd9335 as well for proper indexing of mbhc registers. Change-Id: I93038c4e215bd75b42e895835773b6854d523dd5 Signed-off-by: Karthikeyan Mani --- asoc/codecs/wcd9335.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/asoc/codecs/wcd9335.c b/asoc/codecs/wcd9335.c index bebbf9486c21..aec090daa798 100644 --- a/asoc/codecs/wcd9335.c +++ b/asoc/codecs/wcd9335.c @@ -613,6 +613,8 @@ static struct wcd_mbhc_register WCD9335_MBHC_CTL_2, 0x03, 0, 0), WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT", WCD9335_ANA_MBHC_RESULT_3, 0x08, 3, 0), + WCD_MBHC_REGISTER("WCD_MBHC_IN2P_CLAMP_STATE", + WCD9335_ANA_MBHC_RESULT_3, 0x10, 4, 0), WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT", WCD9335_ANA_MBHC_RESULT_3, 0x20, 5, 0), WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT", From f75e956a313850877c26248c469158febc21e12b Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Wed, 15 Aug 2018 18:13:19 -0700 Subject: [PATCH 244/276] ASoC: wcd934x-dsp-cntl: add debugfs node to perform debug dumps Currently, for any WDSP errors, the driver collects the debug dumps/information based on trigger from userspace. This is protected under CONFIG_DEBUG_FS and should not be run unless stress tests are executed and this functionality is explicitly enabled since performing debug dumps could possible leave the WDSP in bad state. Add debug node to control dumping debug information and not rely only on CONFIG_DEBUG_FS To enable debug dumps: echo 1 > /sys/kernel/debug/wdsp0/debug_dump_enable To disable debug dumps: echo 0 > /sys/kernel/debug/wdsp0/debug_dump_enable Change-Id: I67d8781839d9caee16e6bf6a5befd9010ccaafc0 Signed-off-by: Bhalchandra Gajare Signed-off-by: Xiaoyu Ye --- asoc/codecs/wcd934x/wcd934x-dsp-cntl.c | 20 +++++++++++++++----- asoc/codecs/wcd934x/wcd934x-dsp-cntl.h | 1 + 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c index de4f3997d476..f18cc81a91f8 100644 --- a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c +++ b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.c @@ -757,7 +757,8 @@ static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl) if (!ret) { dev_err(codec->dev, "%s: WDSP boot timed out\n", __func__); - wcd_cntl_collect_debug_dumps(cntl, true); + if (cntl->dbg_dmp_enable) + wcd_cntl_collect_debug_dumps(cntl, true); ret = -ETIMEDOUT; goto err_boot; } else { @@ -976,6 +977,8 @@ static void wcd_cntl_debugfs_init(char *dir, struct wcd_dsp_cntl *cntl) cntl->entry, &cntl->debug_mode); debugfs_create_bool("ramdump_enable", 0644, cntl->entry, &cntl->ramdump_enable); + debugfs_create_bool("debug_dump_enable", 0644, + cntl->entry, &cntl->dbg_dmp_enable); done: return; } @@ -1038,16 +1041,23 @@ static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf, } else if (val[0] == '0') { if (cntl->boot_reqs == 0) { dev_err(cntl->codec->dev, - "%s: WDSP already disabled\n", __func__); + "%s: WDSP already disabled\n", + __func__); ret = -EINVAL; goto done; } cntl->boot_reqs--; vote = false; } else if (!strcmp(val, "DEBUG_DUMP")) { - dev_dbg(cntl->codec->dev, - "%s: Collect dumps for debug use\n", __func__); - wcd_cntl_collect_debug_dumps(cntl, false); + if (cntl->dbg_dmp_enable) { + dev_dbg(cntl->codec->dev, + "%s: Collect dumps for debug use\n", __func__); + wcd_cntl_collect_debug_dumps(cntl, false); + } + /* + * simply ignore the request from userspace + * if dbg_dump_enable is not set from debugfs + */ goto done; } else { dev_err(cntl->codec->dev, "%s: Invalid value %s\n", diff --git a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h index c4571d05038d..4cd41e304c6f 100644 --- a/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h +++ b/asoc/codecs/wcd934x/wcd934x-dsp-cntl.h @@ -90,6 +90,7 @@ struct wcd_dsp_cntl { struct dentry *entry; u32 debug_mode; bool ramdump_enable; + bool dbg_dmp_enable; /* WDSP manager drivers data */ struct device *m_dev; From 34a933a9e4ae0f9b739c33cb850bf8f02ba25975 Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Tue, 25 Sep 2018 16:40:29 -0700 Subject: [PATCH 245/276] asoc: update log level to avoid execessive logging Excessive logging causes throttling during bootup. Update log level from error to debug in get functions to avoid excessive logging. CRs-Fixed: 2321589 Change-Id: If2a23144adf76cdd9bb2d9048a967b63186d7f6e Signed-off-by: Vignesh Kulothungan --- asoc/msm-compress-q6-v2.c | 2 +- asoc/msm-dai-q6-hdmi-v2.c | 2 +- asoc/msm-dai-q6-v2.c | 2 +- asoc/msm-pcm-loopback-v2.c | 2 +- asoc/msm-pcm-q6-noirq.c | 2 +- asoc/msm-pcm-q6-v2.c | 4 ++-- asoc/msm-pcm-routing-v2.c | 10 +++++----- asoc/msm-qti-pp-config.c | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c index a976c05adb3b..bd9fe6f2b22e 100644 --- a/asoc/msm-compress-q6-v2.c +++ b/asoc/msm-compress-q6-v2.c @@ -3237,7 +3237,7 @@ static int msm_compr_audio_effects_config_get(struct snd_kcontrol *kcontrol, cstream = pdata->cstream[fe_id]; audio_effects = pdata->audio_effects[fe_id]; if (!cstream || !audio_effects) { - pr_err("%s: stream or effects inactive\n", __func__); + pr_debug("%s: stream or effects inactive\n", __func__); return -EINVAL; } prtd = cstream->runtime->private_data; diff --git a/asoc/msm-dai-q6-hdmi-v2.c b/asoc/msm-dai-q6-hdmi-v2.c index 6b2d2e799a7c..f1fd1ab8e0ab 100644 --- a/asoc/msm-dai-q6-hdmi-v2.c +++ b/asoc/msm-dai-q6-hdmi-v2.c @@ -142,7 +142,7 @@ static int msm_dai_q6_ext_disp_drift_get(struct snd_kcontrol *kcontrol, struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev); if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { - pr_err("%s: afe port not started. status_mask = %ld\n", + pr_debug("%s: afe port not started. status_mask = %ld\n", __func__, *dai_data->status_mask); goto done; } diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 6aad74db927d..fcb726f31daf 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -2601,7 +2601,7 @@ static int msm_dai_q6_slim_rx_drift_get(struct snd_kcontrol *kcontrol, struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev); if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) { - pr_err("%s: afe port not started. dai_data->status_mask = %ld\n", + pr_debug("%s: afe port not started. dai_data->status_mask = %ld\n", __func__, *dai_data->status_mask); goto done; } diff --git a/asoc/msm-pcm-loopback-v2.c b/asoc/msm-pcm-loopback-v2.c index 9b42d62aef93..3b6b3a840eab 100644 --- a/asoc/msm-pcm-loopback-v2.c +++ b/asoc/msm-pcm-loopback-v2.c @@ -517,7 +517,7 @@ static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, pr_debug("%s\n", __func__); if ((!substream) || (!substream->runtime)) { - pr_err("%s substream or runtime not found\n", __func__); + pr_debug("%s substream or runtime not found\n", __func__); rc = -ENODEV; goto exit; } diff --git a/asoc/msm-pcm-q6-noirq.c b/asoc/msm-pcm-q6-noirq.c index 693014dfbeb5..386b52a65fcc 100644 --- a/asoc/msm-pcm-q6-noirq.c +++ b/asoc/msm-pcm-q6-noirq.c @@ -653,7 +653,7 @@ static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, return -ENODEV; } if (!substream->runtime) { - pr_err("%s substream runtime not found\n", __func__); + pr_debug("%s substream runtime not found\n", __func__); return 0; } prtd = substream->runtime->private_data; diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index f690ee369341..dddb0791682a 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -1322,7 +1322,7 @@ static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, return -ENODEV; } if (!substream->runtime) { - pr_err("%s substream runtime not found\n", __func__); + pr_debug("%s substream runtime not found\n", __func__); return 0; } prtd = substream->runtime->private_data; @@ -1409,7 +1409,7 @@ static int msm_pcm_compress_ctl_get(struct snd_kcontrol *kcontrol, return -EINVAL; } if (!substream->runtime) { - pr_err("%s substream runtime not found\n", __func__); + pr_debug("%s substream runtime not found\n", __func__); return 0; } prtd = substream->runtime->private_data; diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 1b1d2209a6fb..53d2642e8d54 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -12036,7 +12036,7 @@ static int msm_voice_sound_focus_get(struct snd_kcontrol *kcontrol, ret = voc_get_sound_focus(&soundFocusData); if (ret) { - pr_err("%s: Error getting Sound Focus Params, err=%d\n", + pr_debug("%s: Error getting Sound Focus Params, err=%d\n", __func__, ret); ret = -EINVAL; @@ -12068,7 +12068,7 @@ static int msm_voice_source_tracking_get(struct snd_kcontrol *kcontrol, ret = voc_get_source_tracking(&sourceTrackingData); if (ret) { - pr_err("%s: Error getting Source Tracking Params, err=%d\n", + pr_debug("%s: Error getting Source Tracking Params, err=%d\n", __func__, ret); ret = -EINVAL; @@ -12124,7 +12124,7 @@ static int msm_audio_get_copp_idx_from_port_id(int port_id, int session_type, break; } if (i >= MSM_FRONTEND_DAI_MM_SIZE) { - pr_err("%s: Invalid FE, exiting\n", __func__); + pr_debug("%s: Invalid FE, exiting\n", __func__); ret = -EINVAL; goto done; @@ -12234,7 +12234,7 @@ static int msm_audio_sound_focus_get(struct snd_kcontrol *kcontrol, 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", + pr_debug("%s: Could not get copp idx for port_id=%d\n", __func__, port_id); ret = -EINVAL; @@ -12277,7 +12277,7 @@ static int msm_audio_source_tracking_get(struct snd_kcontrol *kcontrol, 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", + pr_debug("%s: Could not get copp idx for port_id=%d\n", __func__, port_id); ret = -EINVAL; diff --git a/asoc/msm-qti-pp-config.c b/asoc/msm-qti-pp-config.c index f5f9ee1ae952..317c8e5255f5 100644 --- a/asoc/msm-qti-pp-config.c +++ b/asoc/msm-qti-pp-config.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, 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 @@ -1077,7 +1077,7 @@ int msm_adsp_stream_callback_get(struct snd_kcontrol *kcontrol, kctl_prtd = (struct dsp_stream_callback_prtd *) kcontrol->private_data; if (kctl_prtd == NULL) { - pr_err("%s: ASM Stream PP event queue is not initialized.\n", + pr_debug("%s: ASM Stream PP event queue is not initialized.\n", __func__); ret = -EINVAL; goto done; From da2dcfa14d14191a7b74770a88e0e14348f6dbd5 Mon Sep 17 00:00:00 2001 From: Pallavi Date: Thu, 16 Aug 2018 17:54:29 +0530 Subject: [PATCH 246/276] ASoC: wcd934x: Update I2S mux register to enable dual mic Set dual mic bit in I2S mux register for data 1 line to enable fluence in sdxpoorwills. CRs-fixed: 2316323 Change-Id: Id94a3df9e8d88b17af00def715094a4910fa3bde Signed-off-by: Pallavi --- asoc/codecs/wcd934x/wcd934x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index ddbb90839f1b..27c37875b39f 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -1684,7 +1684,7 @@ static int tavil_codec_set_i2s_tx_ch(struct snd_soc_dapm_widget *w, snd_soc_update_bits(codec, WCD934X_DATA_HUB_I2S_TX0_CFG, - 0x0C, 0x01); + 0x0C, 0x04); snd_soc_update_bits(codec, WCD934X_DATA_HUB_I2S_TX1_0_CFG, @@ -9487,7 +9487,7 @@ static const struct tavil_reg_mask_val tavil_codec_reg_i2c_defaults[] = { {WCD934X_DATA_HUB_RX2_CFG, 0x03, 0x01}, {WCD934X_DATA_HUB_RX3_CFG, 0x03, 0x01}, {WCD934X_DATA_HUB_I2S_TX0_CFG, 0x01, 0x01}, - {WCD934X_DATA_HUB_I2S_TX0_CFG, 0x04, 0x01}, + {WCD934X_DATA_HUB_I2S_TX0_CFG, 0x04, 0x04}, {WCD934X_DATA_HUB_I2S_TX1_0_CFG, 0x01, 0x01}, {WCD934X_DATA_HUB_I2S_TX1_1_CFG, 0x05, 0x05}, {WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN, 0x1, 0x1}, From 1c6537823d1d18632c50651e2416407f8cec56c6 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Thu, 6 Sep 2018 14:34:03 +0800 Subject: [PATCH 247/276] ASoC: rate limit for error log Use rate limit to reduce redundant log. Change-Id: I7cd7403c42ddce7915c5ecb82504e6c38bcf0572 Signed-off-by: Xiaojun Sang --- dsp/codecs/audio_aac.c | 6 +++--- dsp/codecs/audio_alac.c | 6 +++--- dsp/codecs/audio_ape.c | 6 +++--- dsp/codecs/audio_utils_aio.c | 36 ++++++++++++++++++------------------ dsp/codecs/audio_wma.c | 6 +++--- dsp/codecs/audio_wmapro.c | 6 +++--- dsp/q6asm.c | 12 ++++++------ 7 files changed, 39 insertions(+), 39 deletions(-) diff --git a/dsp/codecs/audio_aac.c b/dsp/codecs/audio_aac.c index 006b21c6793a..237f274c0631 100644 --- a/dsp/codecs/audio_aac.c +++ b/dsp/codecs/audio_aac.c @@ -2,7 +2,7 @@ * * Copyright (C) 2008 Google, Inc. * Copyright (C) 2008 HTC Corporation - * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2010-2018, 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 @@ -230,7 +230,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 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", + pr_err_ratelimited("%s[%pK]:Failed in utils_ioctl: %d\n", __func__, audio, rc); } } @@ -339,7 +339,7 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd, 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", + pr_err_ratelimited("%s[%pK]:Failed in utils_ioctl: %d\n", __func__, audio, rc); } } diff --git a/dsp/codecs/audio_alac.c b/dsp/codecs/audio_alac.c index d0b86c684808..d33a37d174c7 100644 --- a/dsp/codecs/audio_alac.c +++ b/dsp/codecs/audio_alac.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -151,7 +151,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) default: { rc = audio->codec_ioctl(file, cmd, arg); if (rc) - pr_err("Failed in utils_ioctl: %d\n", rc); + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); break; } } @@ -253,7 +253,7 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd, default: { rc = audio->codec_compat_ioctl(file, cmd, arg); if (rc) - pr_err("Failed in utils_ioctl: %d\n", rc); + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); break; } } diff --git a/dsp/codecs/audio_ape.c b/dsp/codecs/audio_ape.c index d7dc0648aeda..a451ffdb68cc 100644 --- a/dsp/codecs/audio_ape.c +++ b/dsp/codecs/audio_ape.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 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 @@ -137,7 +137,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 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); + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); break; } } @@ -235,7 +235,7 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd, 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); + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); break; } } diff --git a/dsp/codecs/audio_utils_aio.c b/dsp/codecs/audio_utils_aio.c index e8f22e79f8cb..9c8b3b84541c 100644 --- a/dsp/codecs/audio_utils_aio.c +++ b/dsp/codecs/audio_utils_aio.c @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Google, Inc. * Copyright (C) 2008 HTC Corporation - * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2009-2018, 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 @@ -201,17 +201,17 @@ static int audio_aio_pause(struct q6audio_aio *audio) if (audio->enabled) { rc = q6asm_cmd(audio->ac, CMD_PAUSE); if (rc < 0) - pr_err("%s[%pK]: pause cmd failed rc=%d\n", + pr_err_ratelimited("%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", + pr_err_ratelimited("%s[%pK]: suspend cmd failed rc=%d\n", __func__, audio, rc); } else - pr_err("%s[%pK]: not sending suspend since pause failed\n", + pr_err_ratelimited("%s[%pK]: not sending suspend since pause failed\n", __func__, audio); } else @@ -230,7 +230,7 @@ static int audio_aio_flush(struct q6audio_aio *audio) 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", + pr_err_ratelimited("%s[%pK}: pause cmd failed rc=%d\n", __func__, audio, rc); else @@ -238,13 +238,13 @@ static int audio_aio_flush(struct q6audio_aio *audio) } rc = q6asm_cmd(audio->ac, CMD_FLUSH); if (rc < 0) - pr_err("%s[%pK]: flush cmd failed rc=%d\n", + pr_err_ratelimited("%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", + pr_err_ratelimited("%s[%pK]:audio re-enable failed\n", __func__, audio); else { audio->enabled = 1; @@ -268,7 +268,7 @@ static int audio_aio_outport_flush(struct q6audio_aio *audio) rc = q6asm_cmd(audio->ac, CMD_OUT_FLUSH); if (rc < 0) - pr_err("%s[%pK}: output port flush cmd failed rc=%d\n", + pr_err_ratelimited("%s[%pK}: output port flush cmd failed rc=%d\n", __func__, audio, rc); return rc; } @@ -402,7 +402,7 @@ int audio_aio_disable(struct q6audio_aio *audio) /* 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", + pr_err_ratelimited("%s[%pK]:Failed to close the session rc=%d\n", __func__, audio, rc); audio->stopped = 1; wake_up(&audio->write_wait); @@ -677,7 +677,7 @@ int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync) pr_debug("%s[%pK]: EOS cmd sent to DSP\n", __func__, audio); if (rc < 0) - pr_err("%s[%pK]: q6asm_cmd failed, rc = %d", + pr_err_ratelimited("%s[%pK]: q6asm_cmd failed, rc = %d", __func__, audio, rc); pr_debug("%s[%pK]: wait for RENDERED_EOS from DSP\n" @@ -1422,7 +1422,7 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd, mutex_lock(&audio->read_lock); rc = audio_aio_outport_flush(audio); if (rc < 0) { - pr_err("%s[%pK]: AUDIO_OUTPORT_FLUSH failed\n", + pr_err_ratelimited("%s[%pK]: AUDIO_OUTPORT_FLUSH failed\n", __func__, audio); rc = -EINTR; } @@ -1436,7 +1436,7 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd, audio->stopped = 1; rc = audio_aio_flush(audio); if (rc < 0) { - pr_err("%s[%pK]:Audio Stop procedure failed rc=%d\n", + pr_err_ratelimited("%s[%pK]:Audio Stop procedure failed rc=%d\n", __func__, audio, rc); mutex_unlock(&audio->lock); break; @@ -1457,7 +1457,7 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd, if (arg == 1) { rc = audio_aio_pause(audio); if (rc < 0) { - pr_err("%s[%pK]: pause FAILED rc=%d\n", + pr_err_ratelimited("%s[%pK]: pause FAILED rc=%d\n", __func__, audio, rc); mutex_unlock(&audio->lock); break; @@ -1467,7 +1467,7 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd, if (audio->drv_status & ADRV_STATUS_PAUSE) { rc = audio_aio_enable(audio); if (rc) - pr_err("%s[%pK]: audio enable failed\n", + pr_err_ratelimited("%s[%pK]: audio enable failed\n", __func__, audio); else { audio->drv_status &= ~ADRV_STATUS_PAUSE; @@ -1494,7 +1494,7 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd, /* Flush input / Output buffer in software*/ audio_aio_ioport_reset(audio); if (rc < 0) { - pr_err("%s[%pK]:AUDIO_FLUSH interrupted\n", + pr_err_ratelimited("%s[%pK]:AUDIO_FLUSH interrupted\n", __func__, audio); rc = -EINTR; } else { @@ -1514,7 +1514,7 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd, 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", + pr_err_ratelimited("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n", __func__); rc = -EFAULT; } @@ -1524,7 +1524,7 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd, case AUDIO_PM_AWAKE: { if ((audio->audio_ws_mgr == NULL) || (audio->miscdevice == NULL)) { - pr_err("%s[%pK]: invalid ws_mgr or miscdevice", + pr_err_ratelimited("%s[%pK]: invalid ws_mgr or miscdevice", __func__, audio); rc = -EACCES; break; @@ -1544,7 +1544,7 @@ static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd, case AUDIO_PM_RELAX: { if ((audio->audio_ws_mgr == NULL) || (audio->miscdevice == NULL)) { - pr_err("%s[%pK]: invalid ws_mgr or miscdevice", + pr_err_ratelimited("%s[%pK]: invalid ws_mgr or miscdevice", __func__, audio); rc = -EACCES; break; diff --git a/dsp/codecs/audio_wma.c b/dsp/codecs/audio_wma.c index e35334a3313a..1bd362431a09 100644 --- a/dsp/codecs/audio_wma.c +++ b/dsp/codecs/audio_wma.c @@ -2,7 +2,7 @@ * * Copyright (C) 2008 Google, Inc. * Copyright (C) 2008 HTC Corporation - * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2009-2018, 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 @@ -126,7 +126,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 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); + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); break; } } @@ -215,7 +215,7 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd, 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); + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); break; } } diff --git a/dsp/codecs/audio_wmapro.c b/dsp/codecs/audio_wmapro.c index 3cb9db15f872..57de1ceabe1f 100644 --- a/dsp/codecs/audio_wmapro.c +++ b/dsp/codecs/audio_wmapro.c @@ -2,7 +2,7 @@ * * Copyright (C) 2008 Google, Inc. * Copyright (C) 2008 HTC Corporation - * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2009-2018, 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 @@ -177,7 +177,7 @@ static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 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); + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); break; } } @@ -287,7 +287,7 @@ static long audio_compat_ioctl(struct file *file, unsigned int cmd, 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); + pr_err_ratelimited("Failed in utils_ioctl: %d\n", rc); break; } } diff --git a/dsp/q6asm.c b/dsp/q6asm.c index fce9f4d099d4..4d6147970362 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -8744,11 +8744,11 @@ static int __q6asm_cmd(struct audio_client *ac, int cmd, uint32_t stream_id) int cnt = 0; if (!ac) { - pr_err("%s: APR handle NULL\n", __func__); + pr_err_ratelimited("%s: APR handle NULL\n", __func__); return -EINVAL; } if (ac->apr == NULL) { - pr_err("%s: AC APR handle NULL\n", __func__); + pr_err_ratelimited("%s: AC APR handle NULL\n", __func__); return -EINVAL; } q6asm_stream_add_hdr(ac, &hdr, sizeof(hdr), TRUE, stream_id); @@ -8874,11 +8874,11 @@ static int __q6asm_cmd_nowait(struct audio_client *ac, int cmd, int rc; if (!ac) { - pr_err("%s: APR handle NULL\n", __func__); + pr_err_ratelimited("%s: APR handle NULL\n", __func__); return -EINVAL; } if (ac->apr == NULL) { - pr_err("%s: AC APR handle NULL\n", __func__); + pr_err_ratelimited("%s: AC APR handle NULL\n", __func__); return -EINVAL; } q6asm_stream_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE, stream_id); @@ -8950,11 +8950,11 @@ int __q6asm_send_meta_data(struct audio_client *ac, uint32_t stream_id, int rc = 0; if (!ac) { - pr_err("%s: APR handle NULL\n", __func__); + pr_err_ratelimited("%s: APR handle NULL\n", __func__); return -EINVAL; } if (ac->apr == NULL) { - pr_err("%s: AC APR handle NULL\n", __func__); + pr_err_ratelimited("%s: AC APR handle NULL\n", __func__); return -EINVAL; } pr_debug("%s: session[%d]\n", __func__, ac->session); From 0f1fee01a22195fb9277dbad8082ace19dcc72a4 Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Tue, 27 Nov 2018 18:12:11 +0530 Subject: [PATCH 248/276] soc: swr-wcd: Apply div2 setting on slave side before bank switch In soundwire controller, bank switch happen twice for a playback session with stereo speakers. Ensure the setting of div2 applied to inactive bank before bank switch occurs to avoid impact based on bank chosen. Change-Id: I033b19e78309485ca9da85ec67b54409e6fe22cc Signed-off-by: Laxminath Kasam --- soc/swr-wcd-ctrl.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/soc/swr-wcd-ctrl.c b/soc/swr-wcd-ctrl.c index f4c63792342a..723244819a21 100644 --- a/soc/swr-wcd-ctrl.c +++ b/soc/swr-wcd-ctrl.c @@ -654,6 +654,10 @@ static u8 get_inactive_bank_num(struct swr_mstr_ctrl *swrm) static void enable_bank_switch(struct swr_mstr_ctrl *swrm, u8 bank, u8 row, u8 col) { + /* apply div2 setting for inactive bank before bank switch */ + swrm_cmd_fifo_wr_cmd(swrm, 0x01, 0xF, 0x00, + SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(bank)); + swrm_cmd_fifo_wr_cmd(swrm, ((row << 3) | col), 0xF, 0xF, SWRS_SCP_FRAME_CTRL_BANK(bank)); } @@ -891,10 +895,6 @@ static void swrm_apply_port_config(struct swr_master *master) dev_dbg(swrm->dev, "%s: enter bank: %d master_ports: %d\n", __func__, bank, master->num_port); - - swrm_cmd_fifo_wr_cmd(swrm, 0x01, 0xF, 0x00, - SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(bank)); - swrm_copy_data_port_config(master, bank); } From c08e65e12a41d637b2b42d57f71c7856e43fea94 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Thu, 17 Jan 2019 15:45:13 +0800 Subject: [PATCH 249/276] asoc: check payload length against structure size Payload length must exceed structure size. Otherwise, it may lead to out-of-boundary memory access. Change-Id: I090de5116ab04a4ca2b9c485e17617fe9e861ad5 Signed-off-by: Xiaojun Sang --- asoc/msm-qti-pp-config.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/asoc/msm-qti-pp-config.c b/asoc/msm-qti-pp-config.c index 317c8e5255f5..a408d31080fd 100644 --- a/asoc/msm-qti-pp-config.c +++ b/asoc/msm-qti-pp-config.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -998,6 +998,13 @@ int msm_adsp_inform_mixer_ctl(struct snd_soc_pcm_runtime *rtd, } event_data = (struct msm_adsp_event_data *)payload; + if (event_data->payload_len < sizeof(struct msm_adsp_event_data)) { + pr_err("%s: event_data size of %x is less than expected.\n", + __func__, event_data->payload_len); + ret = -EINVAL; + goto done; + } + kctl->info(kctl, &kctl_info); if (event_data->payload_len > From 258efb21152abb8c8dcfc04604b121665c147ff0 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Mon, 4 Feb 2019 11:22:49 +0530 Subject: [PATCH 250/276] dsp: asm: Add check for num_channels before calling q6asm_map_channels Channel_mapping array size varies for different commands. Add check for num_channels before calling q6asm_map_channels. Change-Id: Iccbcfe82f716fc0ffe0a26b1779dcaa1c3cb805b Signed-off-by: Rohit kumar --- dsp/q6asm.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 4d6147970362..d16336de8148 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -3616,6 +3616,12 @@ int q6asm_open_shared_io(struct audio_client *ac, if (!ac || !config) return -EINVAL; + if (config->channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, + config->channels); + return -EINVAL; + } + bufsz = config->bufsz; bufcnt = config->bufcnt; num_watermarks = 0; @@ -4075,6 +4081,12 @@ int q6asm_set_encdec_chan_map(struct audio_client *ac, u8 *channel_mapping; int rc = 0; + if (num_channels > MAX_CHAN_MAP_CHANNELS) { + pr_err("%s: Invalid channel count %d\n", __func__, + num_channels); + return -EINVAL; + } + 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); @@ -4155,6 +4167,12 @@ int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac, goto fail_cmd; } + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + 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); @@ -4254,6 +4272,12 @@ int q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac, goto fail_cmd; } + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + 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); @@ -4336,6 +4360,11 @@ int q6asm_enc_cfg_blk_pcm_v2(struct audio_client *ac, return -EINVAL; } + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__, ac->session, rate, channels); @@ -4491,9 +4520,13 @@ int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac, struct asm_multi_channel_pcm_enc_cfg_v2 enc_cfg; u8 *channel_mapping; u32 frames_per_buf = 0; - int rc = 0; + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__, ac->session, rate, channels); @@ -4981,6 +5014,11 @@ static int __q6asm_media_format_block_pcm(struct audio_client *ac, u8 *channel_mapping; int rc = 0; + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate, channels); @@ -5063,6 +5101,11 @@ static int __q6asm_media_format_block_pcm_v3(struct audio_client *ac, u8 *channel_mapping; int rc; + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + 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); @@ -5146,6 +5189,11 @@ static int __q6asm_media_format_block_pcm_v4(struct audio_client *ac, u8 *channel_mapping; int rc; + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + 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); @@ -5334,6 +5382,11 @@ static int __q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, u8 *channel_mapping; int rc = 0; + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate, channels); @@ -5401,6 +5454,11 @@ static int __q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac, u8 *channel_mapping; int rc; + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + 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); @@ -5472,6 +5530,11 @@ static int __q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac, u8 *channel_mapping; int rc; + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + 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); @@ -5629,6 +5692,11 @@ int q6asm_media_format_block_gen_compr(struct audio_client *ac, u8 *channel_mapping; int rc = 0; + if (channels > PCM_FORMAT_MAX_NUM_CHANNEL) { + pr_err("%s: Invalid channel count %d\n", __func__, channels); + return -EINVAL; + } + pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]\n", __func__, ac->session, rate, channels, bits_per_sample); From b51a40df57647e1249e9b91323454cedeb54c51b Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Tue, 15 Jan 2019 16:36:08 -0800 Subject: [PATCH 251/276] ipc: apr: check for packet size to header size comparison Check if packet size is large enough to hold the header. Change-Id: I7261f8111d8b5f4f7c181e469de248a732242d64 Signed-off-by: Karthikeyan Mani --- ipc/apr.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/ipc/apr.c b/ipc/apr.c index c1d007c41874..9febea9fee06 100644 --- a/ipc/apr.c +++ b/ipc/apr.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2014, 2016-2018 The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2014, 2016-2019 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 @@ -617,6 +617,12 @@ void apr_cb_func(void *buf, int len, void *priv) pr_err("APR: Wrong paket size\n"); return; } + + if (hdr->pkt_size < hdr_size) { + pr_err("APR: Packet size less than header 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) { From 27aed256084ff52235cf407b40a28b713fba5177 Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Wed, 6 Feb 2019 18:31:12 -0800 Subject: [PATCH 252/276] asoc: pcm: add qtime mixer control Add qtime mixer control to receive qtime value in pcm driver. Add mechanism to delay asm using the time value received. Update platform data structure to hold pointers to all pcm's created in a platform. CRs-Fixed: 2409954 Change-Id: I623e568196357af7909f3113d998df3c64501420 Signed-off-by: Vignesh Kulothungan --- asoc/msm-pcm-q6-v2.c | 128 ++++++++++++++++++++++++++++++++++++++----- asoc/msm-pcm-q6-v2.h | 9 +-- 2 files changed, 119 insertions(+), 18 deletions(-) diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index dddb0791682a..e2f7c9d4a4ba 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -582,8 +582,17 @@ static int msm_pcm_trigger(struct snd_pcm_substream *substream, int 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); + if (prtd->qtime_lsw) { + pr_debug("%s: Trigger start at msw: %u, lsw: %u msec", + __func__, prtd->qtime_msw, prtd->qtime_lsw); + ret = q6asm_run_nowait(prtd->audio_client, 1, + prtd->qtime_msw, prtd->qtime_lsw); + prtd->qtime_lsw = 0; + prtd->qtime_msw = 0; + } else { + 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"); @@ -1118,7 +1127,8 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, goto done; } - substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + substream = pdata->pcm[kcontrol->private_value]-> + streams[SNDRV_PCM_STREAM_PLAYBACK].substream; if (!substream) { pr_err("%s substream not found\n", __func__); ret = -EINVAL; @@ -1403,7 +1413,8 @@ static int msm_pcm_compress_ctl_get(struct snd_kcontrol *kcontrol, pr_err("%s pdata is NULL\n", __func__); return -ENODEV; } - substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + substream = pdata->pcm[kcontrol->private_value]-> + streams[SNDRV_PCM_STREAM_PLAYBACK].substream; if (!substream) { pr_err("%s substream not found\n", __func__); return -EINVAL; @@ -1433,7 +1444,8 @@ static int msm_pcm_compress_ctl_put(struct snd_kcontrol *kcontrol, pr_err("%s pdata is NULL\n", __func__); return -ENODEV; } - substream = pdata->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + substream = pdata->pcm[kcontrol->private_value]-> + streams[SNDRV_PCM_STREAM_PLAYBACK].substream; pr_debug("%s: compress : 0x%x\n", __func__, compress); if (!substream) { pr_err("%s substream not found\n", __func__); @@ -1493,15 +1505,13 @@ static int msm_pcm_add_compress_control(struct snd_soc_pcm_runtime *rtd) pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str); pdata = dev_get_drvdata(rtd->platform->dev); if (pdata) { - if (!pdata->pcm) { - pdata->pcm = rtd->pcm; - snd_soc_add_platform_controls(rtd->platform, - pcm_compress_control, - ARRAY_SIZE - (pcm_compress_control)); - pr_debug("%s: add control success plt = %pK\n", + pdata->pcm[rtd->dai_link->id] = rtd->pcm; + snd_soc_add_platform_controls(rtd->platform, + pcm_compress_control, + ARRAY_SIZE + (pcm_compress_control)); + pr_debug("%s: add control success plt = %pK\n", __func__, rtd->platform); - } } else { pr_err("%s: NULL pdata\n", __func__); ret = -EINVAL; @@ -1755,6 +1765,90 @@ static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd) return 0; } +static int msm_pcm_qtimer_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0x200000; + return 0; +} + +static int msm_pcm_qtimer_ctl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct snd_soc_platform *platform = snd_soc_component_to_platform(comp); + struct msm_plat_data *pdata = dev_get_drvdata(platform->dev); + struct snd_pcm_substream *substream; + struct msm_audio *prtd; + + if (!pdata) { + pr_err("%s pdata is NULL\n", __func__); + return -ENODEV; + } + + substream = pdata->pcm[kcontrol->private_value]-> + streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (!substream) { + pr_err("%s substream not found\n", __func__); + return -EINVAL; + } + + if (!substream->runtime) { + pr_err("%s substream runtime not found\n", __func__); + return 0; + } + + prtd = substream->runtime->private_data; + if (prtd) { + prtd->qtime_lsw = ucontrol->value.integer.value[0]; + prtd->qtime_msw = ucontrol->value.integer.value[1]; + } + + return 0; +} + +static int msm_pcm_add_qtimer_control(struct snd_soc_pcm_runtime *rtd) +{ + const char *mixer_ctl_name = "QTimer"; + const char *deviceNo = "NN"; + char *mixer_str = NULL; + int ctl_len = 0, ret = 0; + struct snd_kcontrol_new qtimer_control[1] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "?", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = msm_pcm_qtimer_ctl_info, + .put = msm_pcm_qtimer_ctl_put, + .private_value = 0, + } + }; + + ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1; + mixer_str = kzalloc(ctl_len, GFP_KERNEL); + if (!mixer_str) { + ret = -ENOMEM; + goto done; + } + + snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device); + qtimer_control[0].name = mixer_str; + qtimer_control[0].private_value = rtd->dai_link->id; + ret = snd_soc_add_platform_controls(rtd->platform, + qtimer_control, + ARRAY_SIZE(qtimer_control)); + if (ret < 0) + pr_err("%s: failed add ctl %s. err = %d\n", + __func__, mixer_str, ret); + + kfree(mixer_str); +done: + return ret; +} + static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd) { int ret = 0; @@ -1767,6 +1861,12 @@ static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd) if (ret) pr_err("%s: pcm add app type controls failed:%d\n", __func__, ret); + + ret = msm_pcm_add_qtimer_control(rtd); + if (ret) + pr_err("%s: pcm add qtimer controls failed:%d\n", + __func__, ret); + return ret; } diff --git a/asoc/msm-pcm-q6-v2.h b/asoc/msm-pcm-q6-v2.h index 0177b2d6ce79..810a0082d5b3 100644 --- a/asoc/msm-pcm-q6-v2.h +++ b/asoc/msm-pcm-q6-v2.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2008 Google, Inc. * Copyright (C) 2008 HTC Corporation - * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2017,2019 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 @@ -20,8 +20,7 @@ #define _MSM_PCM_H #include #include - - +#include "msm-pcm-routing-v2.h" /* Support unconventional sample rates 12000, 24000 as well */ #define USE_RATE \ @@ -110,6 +109,8 @@ struct msm_audio { bool meta_data_mode; uint32_t volume; bool compress_enable; + uint32_t qtime_lsw; + uint32_t qtime_msw; /* array of frame info */ struct msm_audio_in_frame_info in_frame_info[CAPTURE_MAX_NUM_PERIODS]; }; @@ -124,7 +125,7 @@ struct output_meta_data_st { struct msm_plat_data { int perf_mode; - struct snd_pcm *pcm; + struct snd_pcm *pcm[MSM_FRONTEND_DAI_MAX]; }; #endif /*_MSM_PCM_H*/ From e409514e073ffeb62fa7cf3a8c74eb2a79461821 Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Wed, 6 Feb 2019 18:20:54 -0800 Subject: [PATCH 253/276] asoc: Add FE dai's for Haptics Audio Add two frontends dais Multimedia 21 and 22. Configure routing path from the above frontends to backends QUAT RX0 and QUAT RX1. Add support to set tdm slot mask. CRs-Fixed: 2409954 Change-Id: I2e6396efee757603979c07c78a70b6e0f7123702 Signed-off-by: Vignesh Kulothungan --- asoc/msm-dai-fe.c | 40 ++++++++++++++++++++++++++++++++++++++- asoc/msm-dai-q6-v2.c | 31 ++++++++++++++++++++++++++++-- asoc/msm-pcm-routing-v2.c | 36 ++++++++++++++++++++++++++++++++++- asoc/msm-pcm-routing-v2.h | 4 +++- 4 files changed, 106 insertions(+), 5 deletions(-) diff --git a/asoc/msm-dai-fe.c b/asoc/msm-dai-fe.c index c9f35afb7109..77ca7729f827 100644 --- a/asoc/msm-dai-fe.c +++ b/asoc/msm-dai-fe.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -2569,6 +2569,44 @@ static struct snd_soc_dai_driver msm_fe_dais[] = { .name = "MultiMedia20", .probe = fe_dai_probe, }, + { + .playback = { + .stream_name = "MultiMedia21 Playback", + .aif_name = "MM_DL21", + .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 = 32, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia21", + .probe = fe_dai_probe, + }, + { + .playback = { + .stream_name = "MultiMedia22 Playback", + .aif_name = "MM_DL22", + .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 = 32, + .rate_min = 8000, + .rate_max = 384000, + }, + .ops = &msm_fe_Multimedia_dai_ops, + .name = "MultiMedia22", + .probe = fe_dai_probe, + }, { .capture = { .stream_name = "MultiMedia28 Capture", diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index fcb726f31daf..616840c417c5 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -6853,6 +6853,31 @@ static int msm_dai_q6_tdm_set_channel_map(struct snd_soc_dai *dai, return rc; } +static unsigned int tdm_param_set_slot_mask(u16 *slot_offset, int slot_width, + int slots_per_frame) +{ + unsigned int i = 0; + unsigned int slot_index = 0; + unsigned long slot_mask = 0; + unsigned int slot_width_bytes = slot_width / 8; + + for (i = 0; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++) { + if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID) { + slot_index = slot_offset[i] / slot_width_bytes; + if (slot_index < slots_per_frame) + set_bit(slot_index, &slot_mask); + else { + pr_err("%s: invalid slot map setting\n", + __func__); + return 0; + } + } else + break; + } + + return slot_mask; +} + static int msm_dai_q6_tdm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -6941,7 +6966,9 @@ static int msm_dai_q6_tdm_hw_params(struct snd_pcm_substream *substream, */ tdm->nslots_per_frame = tdm_group->nslots_per_frame; tdm->slot_width = tdm_group->slot_width; - tdm->slot_mask = tdm_group->slot_mask; + tdm->slot_mask = tdm_param_set_slot_mask(slot_mapping->offset, + tdm_group->slot_width, + tdm_group->nslots_per_frame); pr_debug("%s: TDM:\n" "num_channels=%d sample_rate=%d bit_width=%d\n" diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c index 53d2642e8d54..1ecb74ae2417 100644 --- a/asoc/msm-pcm-routing-v2.c +++ b/asoc/msm-pcm-routing-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -6470,6 +6470,12 @@ static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_0, MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia22", MSM_BACKEND_DAI_QUAT_TDM_RX_0, + MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_tx_0_mixer_controls[] = { @@ -6575,6 +6581,12 @@ static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_1, MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia22", MSM_BACKEND_DAI_QUAT_TDM_RX_1, + MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = { @@ -6629,6 +6641,12 @@ static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_2, MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia22", MSM_BACKEND_DAI_QUAT_TDM_RX_2, + MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = { @@ -6683,6 +6701,12 @@ static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = { SOC_SINGLE_EXT("MultiMedia20", MSM_BACKEND_DAI_QUAT_TDM_RX_3, MSM_FRONTEND_DAI_MULTIMEDIA20, 1, 0, msm_routing_get_audio_mixer, msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia21", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA21, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), + SOC_SINGLE_EXT("MultiMedia22", MSM_BACKEND_DAI_QUAT_TDM_RX_3, + MSM_FRONTEND_DAI_MULTIMEDIA22, 1, 0, msm_routing_get_audio_mixer, + msm_routing_put_audio_mixer), }; static const struct snd_kcontrol_new quin_tdm_rx_0_mixer_controls[] = { @@ -12598,6 +12622,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { 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("MM_DL20", "MultiMedia20 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL21", "MultiMedia21 Playback", 0, 0, 0, 0), + SND_SOC_DAPM_AIF_IN("MM_DL22", "MultiMedia22 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), @@ -14534,6 +14560,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Audio Mixer"}, {"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -14571,6 +14599,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Audio Mixer"}, {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -14590,6 +14620,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Audio Mixer"}, {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"}, @@ -14609,6 +14641,8 @@ static const struct snd_soc_dapm_route intercon[] = { {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"}, {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"}, {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia20", "MM_DL20"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia21", "MM_DL21"}, + {"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia22", "MM_DL22"}, {"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Audio Mixer"}, {"QUIN_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"}, diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h index 19c710c4b4a5..e25c256eb024 100644 --- a/asoc/msm-pcm-routing-v2.h +++ b/asoc/msm-pcm-routing-v2.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -211,6 +211,8 @@ enum { MSM_FRONTEND_DAI_MULTIMEDIA18, MSM_FRONTEND_DAI_MULTIMEDIA19, MSM_FRONTEND_DAI_MULTIMEDIA20, + MSM_FRONTEND_DAI_MULTIMEDIA21, + MSM_FRONTEND_DAI_MULTIMEDIA22, MSM_FRONTEND_DAI_MULTIMEDIA28, MSM_FRONTEND_DAI_MULTIMEDIA29, MSM_FRONTEND_DAI_VOIP, From 14487053842c6885711d1e5ef9a8d3928d39f0ad Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Tue, 22 Jan 2019 10:31:21 -0800 Subject: [PATCH 254/276] dsp: asm: validate ADSP data size before access Check the size of ADSP payload before accessing it. Validate buffer index obtained from ADSP token before using it. CRs-Fixed: 2372302 Change-Id: I3f5e1b6f515935a10a8c59c324452be0a71f9473 Signed-off-by: Vignesh Kulothungan --- dsp/q6asm.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index d16336de8148..58db9e08fed4 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2019, The Linux Foundation. All rights reserved. * Author: Brian Swetland * * This software is licensed under the terms of the GNU General Public @@ -1851,9 +1851,12 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) data->dest_port); if ((data->opcode != ASM_DATA_EVENT_RENDERED_EOS) && (data->opcode != ASM_DATA_EVENT_EOS) && + (data->opcode != ASM_SESSION_EVENTX_OVERFLOW) && (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) { - if (payload == NULL) { - pr_err("%s: payload is null\n", __func__); + if (payload == NULL || + (data->payload_size < (2 * sizeof(uint32_t)))) { + pr_err("%s: payload is null or invalid size[%d]\n", + __func__, data->payload_size); spin_unlock_irqrestore( &(session[session_id].session_lock), flags); return -EINVAL; @@ -2029,6 +2032,16 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) } spin_lock_irqsave(&port->dsp_lock, dsp_flags); buf_index = asm_token._token.buf_index; + if (buf_index < 0 || buf_index >= port->max_buf_cnt) { + pr_debug("%s: Invalid buffer index %u\n", + __func__, buf_index); + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); + return -EINVAL; + } if (lower_32_bits(port->buf[buf_index].phys) != payload[0] || msm_audio_populate_upper_32_bits( @@ -2119,6 +2132,16 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) } spin_lock_irqsave(&port->dsp_lock, dsp_flags); buf_index = asm_token._token.buf_index; + if (buf_index < 0 || buf_index >= port->max_buf_cnt) { + pr_debug("%s: Invalid buffer index %u\n", + __func__, buf_index); + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); + return -EINVAL; + } port->buf[buf_index].used = 0; if (lower_32_bits(port->buf[buf_index].phys) != payload[READDONE_IDX_BUFADD_LSW] || From 8c90f226e45aeecc918623e2fea08ad8869db931 Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Wed, 6 Feb 2019 18:20:54 -0800 Subject: [PATCH 255/276] asoc: Add support for Haptics Audio Enable frontends dais Multimedia 21 and 22. Enable backend dai QUAT_TDM_RX_1. Add support to configure TDM slots. CRs-Fixed: 2409954 Change-Id: Ib06152e3c6e84ce6275fbefff54527f38877ff39 Signed-off-by: Vignesh Kulothungan --- asoc/sdm845.c | 297 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 248 insertions(+), 49 deletions(-) diff --git a/asoc/sdm845.c b/asoc/sdm845.c index 52a985da4951..79acd46436c1 100644 --- a/asoc/sdm845.c +++ b/asoc/sdm845.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, 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 @@ -75,6 +75,9 @@ #define MSM_HIFI_ON 1 #define MSM_LL_QOS_VALUE 300 /* time in us to ensure LPM doesn't go in C3/C4 */ +#define TDM_MAX_SLOTS 8 +#define TDM_SLOT_WIDTH_BITS 32 + enum { SLIM_RX_0 = 0, SLIM_RX_1, @@ -135,6 +138,10 @@ struct dev_config { u32 channels; }; +struct tdm_dev_config { + unsigned int tdm_slot_offset[TDM_MAX_SLOTS]; +}; + enum { DP_RX_IDX = 0, EXT_DISP_RX_IDX_MAX, @@ -160,6 +167,8 @@ struct msm_pinctrl_info { enum pinctrl_pin_state curr_state; }; +static atomic_t pinctrl_ref_count; + struct msm_asoc_mach_data { u32 mclk_freq; int us_euro_gpio; /* used by gpio driver API */ @@ -294,6 +303,89 @@ static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { } }; +static struct tdm_dev_config tdm_cfg[TDM_INTERFACE_MAX * 2] + [TDM_PORT_MAX] = { + { /* PRI TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, + { /* SEC TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, + { /* TERT TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, + { /* QUAT TDM */ + { {0, 4, 0xFFFF} }, /* RX_0 */ + { {8, 12, 0xFFFF} }, /* RX_1 */ + { {16, 20, 0xFFFF} }, /* RX_2 */ + { {24, 28, 0xFFFF} }, /* RX_3 */ + { {0xFFFF} }, /* RX_4 */ + { {0xFFFF} }, /* RX_5 */ + { {0xFFFF} }, /* RX_6 */ + { {0xFFFF} }, /* RX_7 */ + }, + { + { {0, 4, 0xFFFF} }, /* TX_0 */ + { {8, 12, 0xFFFF} }, /* TX_1 */ + { {16, 20, 0xFFFF} }, /* TX_2 */ + { {24, 28, 0xFFFF} }, /* TX_3 */ + { {0xFFFF} }, /* TX_4 */ + { {0xFFFF} }, /* TX_5 */ + { {0xFFFF} }, /* TX_6 */ + { {0xFFFF} }, /* TX_7 */ + }, +}; /* Default configuration of slimbus channels */ static struct dev_config slim_rx_cfg[] = { @@ -2177,6 +2269,43 @@ static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol, return ret; } +static int tdm_slot_map_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int interface = ucontrol->value.integer.value[0]; + int channel = ucontrol->value.integer.value[1]; + unsigned int offset_val; + unsigned int *slot_offset; + + if (interface < 0 || interface >= (TDM_INTERFACE_MAX * 2)) { + pr_err("%s: incorrect interface = %d", __func__, interface); + return -EINVAL; + } + if (channel < 0 || channel >= TDM_PORT_MAX) { + pr_err("%s: incorrect channel = %d", __func__, channel); + return -EINVAL; + } + + pr_debug("%s: interface = %d, channel = %d", __func__, + interface, channel); + + slot_offset = tdm_cfg[interface][channel].tdm_slot_offset; + + /* Currenly can configure only two slots */ + offset_val = ucontrol->value.integer.value[2]; + /* Offset value can only be 0, 4, 8, ..28 */ + if (offset_val % 4 == 0 && offset_val <= 28) + slot_offset[0] = offset_val; + pr_debug("%s: slot offset[0] = %d\n", __func__, slot_offset[0]); + + offset_val = ucontrol->value.integer.value[3]; + if (offset_val % 4 == 0 && offset_val <= 28) + slot_offset[1] = offset_val; + pr_debug("%s: slot offset[1] = %d\n", __func__, slot_offset[1]); + + return 0; +} + static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol) { int idx; @@ -2936,18 +3065,27 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { 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_RX_1 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_RX_1 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_RX_1 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), @@ -3051,6 +3189,8 @@ static const struct snd_kcontrol_new msm_snd_controls[] = { msm_hifi_put), SOC_ENUM_EXT("MultiMedia5_RX QOS Vote", qos_vote, msm_qos_ctl_get, msm_qos_ctl_put), + SOC_SINGLE_MULTI_EXT("TDM Slot Map", SND_SOC_NOPM, 0, 255, 0, 4, + NULL, tdm_slot_map_put), }; static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, @@ -3268,8 +3408,9 @@ static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, void *config = NULL; struct snd_soc_codec *codec = NULL; - pr_debug("%s: format = %d, rate = %d\n", - __func__, params_format(params), params_rate(params)); + pr_debug("%s: dai_id= %d, format = %d, rate = %d\n", + __func__, dai_link->id, params_format(params), + params_rate(params)); switch (dai_link->id) { case MSM_BACKEND_DAI_SLIMBUS_0_RX: @@ -4492,6 +4633,7 @@ static int msm_get_pinctrl(struct platform_device *pdev) goto err; } pinctrl_info->curr_state = STATE_DISABLE; + atomic_set(&pinctrl_ref_count, 0); return 0; @@ -4517,6 +4659,13 @@ static int msm_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format); rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate; + } else if (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX_1) { + channels->min = channels->max = + tdm_rx_cfg[TDM_QUAT][TDM_1].channels; + param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, + tdm_rx_cfg[TDM_QUAT][TDM_1].bit_format); + rate->min = rate->max = + tdm_rx_cfg[TDM_QUAT][TDM_1].sample_rate; } else if (cpu_dai->id == AFE_PORT_ID_SECONDARY_TDM_RX) { channels->min = channels->max = tdm_rx_cfg[TDM_SEC][TDM_0].channels; @@ -4542,52 +4691,49 @@ static int sdm845_tdm_snd_hw_params(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; - int slot_width = 32; - int channels, slots; + int slot_width = TDM_SLOT_WIDTH_BITS; + int channels, slots = TDM_MAX_SLOTS; unsigned int slot_mask, rate, clk_freq; - unsigned int slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + unsigned int *slot_offset; + unsigned int path_dir = 0, interface = 0, channel_interface = 0; pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); - /* currently only supporting TDM_RX_0 and TDM_TX_0 */ - switch (cpu_dai->id) { - case AFE_PORT_ID_PRIMARY_TDM_RX: - slots = tdm_rx_cfg[TDM_PRI][TDM_0].channels; - break; - case AFE_PORT_ID_SECONDARY_TDM_RX: - slots = tdm_rx_cfg[TDM_SEC][TDM_0].channels; - break; - case AFE_PORT_ID_TERTIARY_TDM_RX: - slots = tdm_rx_cfg[TDM_TERT][TDM_0].channels; - break; - case AFE_PORT_ID_QUATERNARY_TDM_RX: - slots = tdm_rx_cfg[TDM_QUAT][TDM_0].channels; - break; - case AFE_PORT_ID_PRIMARY_TDM_TX: - slots = tdm_tx_cfg[TDM_PRI][TDM_0].channels; - break; - case AFE_PORT_ID_SECONDARY_TDM_TX: - slots = tdm_tx_cfg[TDM_SEC][TDM_0].channels; - break; - case AFE_PORT_ID_TERTIARY_TDM_TX: - slots = tdm_tx_cfg[TDM_TERT][TDM_0].channels; - break; - case AFE_PORT_ID_QUATERNARY_TDM_TX: - slots = tdm_tx_cfg[TDM_QUAT][TDM_0].channels; - break; - default: + if (cpu_dai->id < AFE_PORT_ID_TDM_PORT_RANGE_START) { pr_err("%s: dai id 0x%x not supported\n", __func__, cpu_dai->id); return -EINVAL; } + /* RX or TX */ + path_dir = cpu_dai->id % 2; + + /* PRI, SEC, TERT, QUAT, QUIN */ + interface = (cpu_dai->id - AFE_PORT_ID_TDM_PORT_RANGE_START) + / (2 * TDM_PORT_MAX); + + /* 0, 1, 2, .. 7 */ + channel_interface = + ((cpu_dai->id - AFE_PORT_ID_TDM_PORT_RANGE_START) / 2) + % TDM_PORT_MAX; + + pr_debug("%s: interface %u, channel interface %u\n", __func__, + interface, channel_interface); + + slot_offset = tdm_cfg[(interface * 2) + path_dir][channel_interface] + .tdm_slot_offset; + + if (path_dir) + channels = tdm_tx_cfg[interface][channel_interface].channels; + else + channels = tdm_rx_cfg[interface][channel_interface].channels; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { /*2 slot config - bits 0 and 1 set for the first two slots */ slot_mask = 0x0000FFFF >> (16-slots); - channels = slots; - pr_debug("%s: tdm rx slot_width %d slots %d\n", - __func__, slot_width, slots); + pr_debug("%s: tdm rx slot_width %d slots %d slot_mask %x\n", + __func__, slot_width, slots, slot_mask); ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask, slots, slot_width); @@ -4597,6 +4743,8 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, goto end; } + pr_debug("%s: tdm rx slot_offset[0]: %d, slot_offset[1]: %d\n", + __func__, slot_offset[0], slot_offset[1]); ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, channels, slot_offset); if (ret < 0) { @@ -4607,7 +4755,6 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { /*2 slot config - bits 0 and 1 set for the first two slots */ slot_mask = 0x0000FFFF >> (16-slots); - channels = slots; pr_debug("%s: tdm tx slot_width %d slots %d\n", __func__, slot_width, slots); @@ -4654,13 +4801,17 @@ static int sdm845_tdm_snd_startup(struct snd_pcm_substream *substream) struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; - /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + /* currently only supporting TDM_RX_0/TDM_RX_1 and TDM_TX_0 */ if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || - (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX)) { - ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); - if (ret) - pr_err("%s: TDM TLMM pinctrl set failed with %d\n", - __func__, ret); + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX_1)) { + if (atomic_read(&pinctrl_ref_count) == 0) { + ret = msm_set_pinctrl(pinctrl_info, STATE_TDM_ACTIVE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } + atomic_inc(&pinctrl_ref_count); } return ret; @@ -4675,13 +4826,17 @@ static void sdm845_tdm_snd_shutdown(struct snd_pcm_substream *substream) struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card); struct msm_pinctrl_info *pinctrl_info = &pdata->pinctrl_info; - /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + /* currently only supporting TDM_RX_0/TDM_RX_1 and TDM_TX_0 */ if ((cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX) || - (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX)) { - ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); - if (ret) - pr_err("%s: TDM TLMM pinctrl set failed with %d\n", - __func__, ret); + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_TX) || + (cpu_dai->id == AFE_PORT_ID_QUATERNARY_TDM_RX_1)) { + atomic_dec(&pinctrl_ref_count); + if (atomic_read(&pinctrl_ref_count) == 0) { + ret = msm_set_pinctrl(pinctrl_info, STATE_DISABLE); + if (ret) + pr_err("%s: TDM TLMM pinctrl set failed with %d\n", + __func__, ret); + } } } @@ -5507,6 +5662,36 @@ static struct snd_soc_dai_link msm_common_misc_fe_dai_links[] = { .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", }, + { + .name = "MultiMedia21 Playback", + .stream_name = "MultiMedia21", + .cpu_dai_name = "MultiMedia21", + .platform_name = "msm-pcm-dsp.1", + .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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA21, + }, + { + .name = "MultiMedia22 Playback", + .stream_name = "MultiMedia22", + .cpu_dai_name = "MultiMedia22", + .platform_name = "msm-pcm-dsp.1", + .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, + .id = MSM_FRONTEND_DAI_MULTIMEDIA22, + }, }; static struct snd_soc_dai_link msm_common_be_dai_links[] = { @@ -5734,6 +5919,20 @@ static struct snd_soc_dai_link msm_common_be_dai_links[] = { .ops = &sdm845_tdm_be_ops, .ignore_suspend = 1, }, + { + .name = LPASS_BE_QUAT_TDM_RX_1, + .stream_name = "Quaternary TDM1 Playback", + .cpu_dai_name = "msm-dai-q6-tdm.36914", + .platform_name = "msm-pcm-routing", + .codec_name = "msm-stub-codec.1", + .codec_dai_name = "msm-stub-rx", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_QUAT_TDM_RX_1, + .be_hw_params_fixup = msm_tdm_be_hw_params_fixup, + .ops = &sdm845_tdm_be_ops, + .ignore_suspend = 1, + }, }; static struct snd_soc_dai_link msm_tavil_be_dai_links[] = { From c0d32d41dbc9f2c4910ba757ccbc2d9454e8d796 Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Tue, 15 Jan 2019 16:49:02 +0800 Subject: [PATCH 256/276] dsp: validate token before usage as array index Token from DSP might be invalid for array index. Validate the token before being used as array index. Change-Id: I9f47e1328d75d9f9acf7e85ddb452019b6eced0a Signed-off-by: Xiaojun Sang Signed-off-by: Meghana Reddy Mula --- dsp/q6afe.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index d19d348b2b62..4bf54393b0d8 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -290,6 +290,15 @@ static int32_t sp_make_afe_callback(uint32_t *payload, uint32_t payload_size) return 0; } +static bool afe_token_is_valid(uint32_t token) +{ + if (token >= AFE_MAX_PORTS) { + pr_err("%s: token %d is invalid.\n", __func__, token); + return false; + } + return true; +} + static int32_t afe_callback(struct apr_client_data *data, void *priv) { if (!data) { @@ -364,7 +373,10 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) data->payload_size)) return -EINVAL; } - wake_up(&this_afe.wait[data->token]); + if (afe_token_is_valid(data->token)) + wake_up(&this_afe.wait[data->token]); + else + return -EINVAL; } else if (data->payload_size) { uint32_t *payload; uint16_t port_id = 0; @@ -395,7 +407,10 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) 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]); + if (afe_token_is_valid(data->token)) + wake_up(&this_afe.wait[data->token]); + else + return -EINVAL; break; case AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER: break; @@ -407,7 +422,10 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) break; case AFE_CMD_ADD_TOPOLOGIES: atomic_set(&this_afe.state, 0); - wake_up(&this_afe.wait[data->token]); + if (afe_token_is_valid(data->token)) + wake_up(&this_afe.wait[data->token]); + else + return -EINVAL; pr_debug("%s: AFE_CMD_ADD_TOPOLOGIES cmd 0x%x\n", __func__, payload[1]); break; @@ -429,7 +447,10 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) else this_afe.mmap_handle = payload[0]; atomic_set(&this_afe.state, 0); - wake_up(&this_afe.wait[data->token]); + if (afe_token_is_valid(data->token)) + wake_up(&this_afe.wait[data->token]); + else + return -EINVAL; } else if (data->opcode == AFE_EVENT_RT_PROXY_PORT_STATUS) { port_id = (uint16_t)(0x0000FFFF & payload[0]); } From 44ce02a6862c2a4debe6cd816118d44db5c62e65 Mon Sep 17 00:00:00 2001 From: Xiaoyu Ye Date: Fri, 1 Feb 2019 17:55:06 -0800 Subject: [PATCH 257/276] dsp: codecs: fix range check for audio buffer copying The range checking for audio buffer copying in function "audio_in_write" is using the incorrect buffer size. Change it to the actual allocated audio buffer size. Change-Id: Icb9a9e9c3b3d3c6e55e7bad20b34f94784b82674 Signed-off-by: Xiaoyu Ye --- dsp/codecs/audio_utils.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/dsp/codecs/audio_utils.c b/dsp/codecs/audio_utils.c index 15ee9f51a7d8..9067855afb61 100644 --- a/dsp/codecs/audio_utils.c +++ b/dsp/codecs/audio_utils.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017, 2019 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 @@ -903,9 +903,8 @@ ssize_t audio_in_write(struct file *file, __func__, audio->ac->session); } } - xfer = (count > (audio->pcm_cfg.buffer_size)) ? - (audio->pcm_cfg.buffer_size) : count; + xfer = (count > size) ? size : count; if (copy_from_user(cpy_ptr, buf, xfer)) { rc = -EFAULT; break; From bdad2178b3326ef3babb6918507883d8d38c4bf1 Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Thu, 14 Mar 2019 13:40:35 -0700 Subject: [PATCH 258/276] dsp: asm: Remove redundant payload size check Due to redundant payload checks, ASM get param requests in RTAC mode fail with timeout errors. Fix this by removing the redundant payload checks. CRs-Fixed: 2372302 Change-Id: If08ec942f3530e132b5980da579ea1766d21c52b Signed-off-by: Vignesh Kulothungan --- dsp/q6asm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 58db9e08fed4..fe1759fd5859 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -1853,10 +1853,8 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) (data->opcode != ASM_DATA_EVENT_EOS) && (data->opcode != ASM_SESSION_EVENTX_OVERFLOW) && (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) { - if (payload == NULL || - (data->payload_size < (2 * sizeof(uint32_t)))) { - pr_err("%s: payload is null or invalid size[%d]\n", - __func__, data->payload_size); + if (payload == NULL) { + pr_err("%s: payload is null\n", __func__); spin_unlock_irqrestore( &(session[session_id].session_lock), flags); return -EINVAL; From 08b87c10b8f34a82470e1693371873aed962c02b Mon Sep 17 00:00:00 2001 From: Xiaojun Sang Date: Thu, 17 Jan 2019 13:28:25 +0800 Subject: [PATCH 259/276] dsp: asm: validate payload size before access Payload size is not checked before payload access. Check size to avoid out-of-boundary memory access. Change-Id: Iaa39ee4ea5489bb5579e7b7d5dfada12d88c5809 Signed-off-by: Xiaojun Sang Signed-off-by: Meghana Reddy Mula --- dsp/q6asm.c | 278 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 188 insertions(+), 90 deletions(-) diff --git a/dsp/q6asm.c b/dsp/q6asm.c index 58db9e08fed4..666e33d1d7bd 100644 --- a/dsp/q6asm.c +++ b/dsp/q6asm.c @@ -1627,14 +1627,14 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) return 0; } - if (data->payload_size > sizeof(int)) { + if (data->payload_size >= 2 * sizeof(uint32_t)) { 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)) { + } else if (data->payload_size == sizeof(uint32_t)) { 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, @@ -1648,7 +1648,8 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) case ASM_CMD_SHARED_MEM_MAP_REGIONS: case ASM_CMD_SHARED_MEM_UNMAP_REGIONS: case ASM_CMD_ADD_TOPOLOGIES: - if (payload[1] != 0) { + if (data->payload_size >= 2 * sizeof(uint32_t) && + 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); @@ -1666,8 +1667,12 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) 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", + if (data->payload_size >= 2 * sizeof(uint32_t)) + dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x]\n", __func__, payload[0], payload[1]); + else + dev_vdbg(ac->dev, "%s: Payload size of %d is less than expected.\n", + __func__, data->payload_size); break; default: pr_debug("%s: command[0x%x] not expecting rsp\n", @@ -1696,8 +1701,14 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) 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]); + if (data->payload_size >= 2 * sizeof(uint32_t)) + pr_debug("%s: PL#0[0x%x]PL#1 [0x%x]\n", + __func__, payload[0], + payload[1]); + else + pr_debug("%s: Payload size of %d is less than expected.\n", + __func__, data->payload_size); + spin_lock_irqsave(&port->dsp_lock, dsp_flags); if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1) wake_up(&ac->mem_wait); @@ -1706,8 +1717,12 @@ static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv) break; } default: - pr_debug("%s: command[0x%x]success [0x%x]\n", - __func__, payload[0], payload[1]); + if (data->payload_size >= 2 * sizeof(uint32_t)) + pr_debug("%s: command[0x%x]success [0x%x]\n", + __func__, payload[0], payload[1]); + else + pr_debug("%s: Payload size of %d is less than expected.\n", + __func__, data->payload_size); } if (ac->cb) ac->cb(data->opcode, data->token, @@ -1861,8 +1876,12 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) &(session[session_id].session_lock), flags); 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->payload_size >= 2 * sizeof(uint32_t)) + dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n", + __func__, payload[0], payload[1], data->opcode); + else + dev_vdbg(ac->dev, "%s: Payload size of %d is less than expected.\n", + __func__, data->payload_size); } if (data->opcode == APR_BASIC_RSP_RESULT) { switch (payload[0]) { @@ -1906,29 +1925,41 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) 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) { - if ((is_adsp_reg_event(payload[0]) >= 0) - || (payload[0] == - ASM_STREAM_CMD_SET_PP_PARAMS_V2)) - atomic_set(&ac->cmd_state_pp, + if (data->payload_size >= 2 * sizeof(uint32_t)) { + 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) { + if ( + (is_adsp_reg_event(payload[0]) + >=0) || + (payload[0] == + ASM_STREAM_CMD_SET_PP_PARAMS_V2 + ) + ) + atomic_set( + &ac->cmd_state_pp, payload[1]); - else - atomic_set(&ac->cmd_state, + else + atomic_set( + &ac->cmd_state, payload[1]); - wake_up(&ac->cmd_wait); + wake_up(&ac->cmd_wait); + } + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); + return 0; } - spin_unlock_irqrestore( - &(session[session_id].session_lock), - flags); - return 0; + } else { + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); } if ((is_adsp_reg_event(payload[0]) >= 0) || (payload[0] == ASM_STREAM_CMD_SET_PP_PARAMS_V2)) { @@ -1949,19 +1980,27 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) (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); + if (data->payload_size >= 2 * sizeof(uint32_t)) { + 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); + } + spin_unlock_irqrestore( + &(session[session_id].session_lock), + flags); + return 0; } - spin_unlock_irqrestore( - &(session[session_id].session_lock), - flags); - return 0; + } else { + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); } if (atomic_read(&ac->mem_state) && wakeup_flag) { atomic_set(&ac->mem_state, 0); @@ -1972,8 +2011,12 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) (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]); + if (data->payload_size >= 2 * sizeof(uint32_t)) + pr_debug("%s: Watermark opcode[0x%x] status[0x%x]", + __func__, payload[0], payload[1]); + else + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); break; } case ASM_STREAM_CMD_GET_PP_PARAMS_V2: @@ -1985,11 +2028,17 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) /* 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, + if (data->payload_size >= 2 * sizeof(uint32_t)) { + 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); + } + } else { + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); } break; case ASM_STREAM_CMD_REGISTER_PP_EVENTS: @@ -1997,11 +2046,16 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) __func__, ac->session, data->opcode, data->token, data->src_port, data->dest_port); - if (payload[1] != 0) - pr_err("%s: ASM get param error = %d, resuming\n", - __func__, payload[1]); - atomic_set(&ac->cmd_state_pp, payload[1]); - wake_up(&ac->cmd_wait); + if (data->payload_size >= 2 * sizeof(uint32_t)) { + if (payload[1] != 0) + pr_err("%s: ASM get param error = %d, resuming\n", + __func__, payload[1]); + atomic_set(&ac->cmd_state_pp, payload[1]); + wake_up(&ac->cmd_wait); + } else { + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + } break; default: pr_debug("%s: command[0x%x] not expecting rsp\n", @@ -2017,10 +2071,13 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) 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 (data->payload_size >= 2 * sizeof(uint32_t)) + dev_vdbg(ac->dev, "%s: Rxed opcode[0x%x] status[0x%x] token[%d]", + __func__, payload[0], payload[1], + data->token); + else + dev_err(ac->dev, "%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); if (ac->io_mode & SYNC_IO_MODE) { if (port->buf == NULL) { pr_err("%s: Unexpected Write Done\n", @@ -2036,22 +2093,24 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) pr_debug("%s: Invalid buffer index %u\n", __func__, buf_index); spin_unlock_irqrestore(&port->dsp_lock, - dsp_flags); + dsp_flags); spin_unlock_irqrestore( &(session[session_id].session_lock), flags); return -EINVAL; } - if (lower_32_bits(port->buf[buf_index].phys) != - payload[0] || - msm_audio_populate_upper_32_bits( - port->buf[buf_index].phys) != payload[1]) { + if (data->payload_size >= 2 * sizeof(uint32_t) && + (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); + __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); + dsp_flags); spin_unlock_irqrestore( &(session[session_id].session_lock), flags); @@ -2080,14 +2139,30 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) } 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]); + if (data->payload_size >= 4 * sizeof(uint32_t)) + pr_debug("%s: payload[1] = 0x%x, payload[2]=0x%x, payload[3]=0x%x\n", + __func__, payload[1], + payload[2], payload[3]); + else + pr_err("%s: payload size of %x is less than expected.\n", + __func__, + data->payload_size); + + if (data->payload_size >= + (4 + (payload[3]>>2)) * sizeof(uint32_t) + ) + {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]); + } + } else { + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); } pr_debug("%s: callback size in ints = %i\n", __func__, @@ -2184,11 +2259,17 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) 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 (data->payload_size >= 3 * sizeof(uint32_t)) { + 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]); + } else { + dev_err(ac->dev, "%s: payload size of %x is less than expected.n", + __func__, data->payload_size); + } if (atomic_cmpxchg(&ac->time_flag, 1, 0)) wake_up(&ac->time_wait); break; @@ -2198,10 +2279,14 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) __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]); + if (data->payload_size >= 4 * sizeof(uint32_t)) + 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]); + else + pr_debug("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); break; case ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2: q6asm_process_mtmx_get_param_rsp(ac, (void *) payload); @@ -2209,8 +2294,12 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) case ASM_STREAM_PP_EVENT: case ASM_STREAM_CMD_ENCDEC_EVENTS: case ASM_STREAM_CMD_REGISTER_IEC_61937_FMT_UPDATE: - pr_debug("%s: ASM_STREAM_EVENT payload[0][0x%x] payload[1][0x%x]", - __func__, payload[0], payload[1]); + if (data->payload_size >= 2 * sizeof(uint32_t)) + pr_debug("%s: ASM_STREAM_EVENT payload[0][0x%x] payload[1][0x%x]", + __func__, payload[0], payload[1]); + else + pr_debug("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); i = is_adsp_raise_event(data->opcode); if (i < 0) { spin_unlock_irqrestore( @@ -2249,16 +2338,25 @@ static int32_t q6asm_callback(struct apr_client_data *data, void *priv) &(session[session_id].session_lock), flags); return 0; case ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2: - pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n", - __func__, ac->session, payload[0], payload[2], - payload[1]); + if (data->payload_size >= 3 * sizeof(uint32_t)) + pr_debug("%s: ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2 sesion %d status 0x%x msw %u lsw %u\n", + __func__, ac->session, payload[0], payload[2], + payload[1]); + else + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); wake_up(&ac->cmd_wait); 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) { + if (data->payload_size >= 3 * sizeof(uint32_t)) + 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]); + else + pr_err("%s: payload size of %x is less than expected.\n", + __func__, data->payload_size); + if (payload[0] == 0 && + data->payload_size >= 2 * sizeof(uint32_t)) { atomic_set(&ac->cmd_state, 0); /* ignore msw, as a delay that large shouldn't happen */ ac->path_delay = payload[1]; From 21d17b1ce3e32db2b1c124516fb77cca47d1dbda Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Tue, 22 Jan 2019 11:13:09 -0800 Subject: [PATCH 260/276] dsp: adm: validate ADSP payload size before access Check the size of ADSP payload before accessing it. CRs-Fixed: 2380694 Change-Id: Ib0c0f0bf6c7f7cf659df0eb70a3f66cee580cb66 Signed-off-by: Vignesh Kulothungan --- dsp/q6adm.c | 66 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/dsp/q6adm.c b/dsp/q6adm.c index 4224d0602f9f..372f2043eae6 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -1381,7 +1381,7 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) } adm_callback_debug_print(data); - if (data->payload_size) { + if (data->payload_size >= sizeof(uint32_t)) { copp_idx = (data->token) & 0XFF; port_idx = ((data->token) >> 16) & 0xFF; client_id = ((data->token) >> 8) & 0xFF; @@ -1403,6 +1403,15 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) if (data->opcode == APR_BASIC_RSP_RESULT) { pr_debug("%s: APR_BASIC_RSP_RESULT id 0x%x\n", __func__, payload[0]); + if (!((client_id != ADM_CLIENT_ID_SOURCE_TRACKING) && + (payload[0] == ADM_CMD_SET_PP_PARAMS_V5))) { + if (data->payload_size < + (2 * sizeof(uint32_t))) { + pr_err("%s: Invalid payload size %d\n", + __func__, data->payload_size); + return 0; + } + } if (payload[1] != 0) { pr_err("%s: cmd = 0x%x returned error = 0x%x\n", __func__, payload[0], payload[1]); @@ -1521,9 +1530,16 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) switch (data->opcode) { case ADM_CMDRSP_DEVICE_OPEN_V5: case ADM_CMDRSP_DEVICE_OPEN_V6: { - struct adm_cmd_rsp_device_open_v5 *open = - (struct adm_cmd_rsp_device_open_v5 *)data->payload; + struct adm_cmd_rsp_device_open_v5 *open = NULL; + if (data->payload_size < + sizeof(struct adm_cmd_rsp_device_open_v5)) { + pr_err("%s: Invalid payload size %d\n", + __func__, data->payload_size); + return 0; + } + 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); @@ -1596,25 +1612,31 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) 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]); + } else if (data->payload_size >= + (2 * sizeof(uint32_t))) { + 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]); + } } - } + } else + pr_err("%s: Invalid payload size %d\n", + __func__, data->payload_size); atomic_set(&this_adm.copp.stat [port_idx][copp_idx], payload[0]); wake_up(&this_adm.copp.wait[port_idx][copp_idx]); From 0a90300f3f369720fb03aaf5e47808c94f59ad04 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Tue, 5 Mar 2019 15:52:15 +0530 Subject: [PATCH 261/276] dsp: q6voice: Check size of shared memory buffer before access Check buffer size in qdsp_cvs_callback before access in ul_pkt. Change-Id: Ic19994b46086709231656ec747d2df988b7a512f Signed-off-by: Vatsal Bucha --- dsp/q6voice.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dsp/q6voice.c b/dsp/q6voice.c index cac734005aec..8a7281ca5843 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -7487,6 +7487,11 @@ static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv) cvs_voc_pkt = v->shmem_info.sh_buf.buf[1].data; if (cvs_voc_pkt != NULL && common.mvs_info.ul_cb != NULL) { + if (v->shmem_info.sh_buf.buf[1].size < + ((3 * sizeof(uint32_t)) + cvs_voc_pkt[2])) { + pr_err("%s: invalid voc pkt size\n", __func__); + return -EINVAL; + } /* cvs_voc_pkt[0] contains tx timestamp */ common.mvs_info.ul_cb((uint8_t *)&cvs_voc_pkt[3], cvs_voc_pkt[2], From 3ac796e513f87df901e114179fd095b20d9ceec8 Mon Sep 17 00:00:00 2001 From: Vatsal Bucha Date: Mon, 28 Jan 2019 18:44:29 +0530 Subject: [PATCH 262/276] dsp: q6usm: Check size of payload before access Check size of payload array before access in q6usm_callback. Change-Id: Id0c85209a053f9dfdb53133aeb6b2510ecf18eb8 Signed-off-by: Vatsal Bucha Signed-off-by: Meghana Reddy Mula --- dsp/q6usm.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/dsp/q6usm.c b/dsp/q6usm.c index 5b822ae99358..8236a192752d 100644 --- a/dsp/q6usm.c +++ b/dsp/q6usm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 2019 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 @@ -563,6 +563,11 @@ static int32_t q6usm_callback(struct apr_client_data *data, void *priv) } if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size < (2 * sizeof(uint32_t))) { + pr_err("%s: payload has invalid size[%d]\n", __func__, + data->payload_size); + return -EINVAL; + } /* status field check */ if (payload[1]) { pr_err("%s: wrong response[%d] on cmd [%d]\n", @@ -626,6 +631,14 @@ static int32_t q6usm_callback(struct apr_client_data *data, void *priv) opcode = Q6USM_EVENT_READ_DONE; spin_lock_irqsave(&port->dsp_lock, dsp_flags); + if (data->payload_size < + (sizeof(uint32_t)*(READDONE_IDX_STATUS + 1))) { + pr_err("%s: Invalid payload size for READDONE[%d]\n", + __func__, data->payload_size); + spin_unlock_irqrestore(&port->dsp_lock, + dsp_flags); + return -EINVAL; + } if (payload[READDONE_IDX_STATUS]) { pr_err("%s: wrong READDONE[%d]; token[%d]\n", __func__, @@ -672,6 +685,12 @@ static int32_t q6usm_callback(struct apr_client_data *data, void *priv) struct us_port_data *port = &usc->port[IN]; opcode = Q6USM_EVENT_WRITE_DONE; + if (data->payload_size < + (sizeof(uint32_t)*(WRITEDONE_IDX_STATUS + 1))) { + pr_err("%s: Invalid payload size for WRITEDONE[%d]\n", + __func__, data->payload_size); + return -EINVAL; + } if (payload[WRITEDONE_IDX_STATUS]) { pr_err("%s: wrong WRITEDONE_IDX_STATUS[%d]\n", __func__, From 01737939ee512476de86e59862c95314e4da4958 Mon Sep 17 00:00:00 2001 From: Karthikeyan Mani Date: Fri, 18 Jan 2019 16:50:58 -0800 Subject: [PATCH 263/276] dsp: afe: check for payload size before payload access Check if payload data is big enough before accessing the data in it. Change-Id: I939f205a8cebf6ef4859f81fae5429bca013d540 Signed-off-by: Karthikeyan Mani --- dsp/q6afe.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/dsp/q6afe.c b/dsp/q6afe.c index 4bf54393b0d8..999ff36b5a6c 100644 --- a/dsp/q6afe.c +++ b/dsp/q6afe.c @@ -361,14 +361,20 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) return -EINVAL; } + if (rtac_make_afe_callback(data->payload, + data->payload_size)) + return 0; + + if (data->payload_size < 3 * sizeof(uint32_t)) { + pr_err("%s: Error: size %d is less than expected\n", + __func__, data->payload_size); + return -EINVAL; + } + if (payload[2] == AFE_PARAM_ID_DEV_TIMING_STATS) { av_dev_drift_afe_cb_handler(data->payload, data->payload_size); } else { - if (rtac_make_afe_callback(data->payload, - data->payload_size)) - return 0; - if (sp_make_afe_callback(data->payload, data->payload_size)) return -EINVAL; @@ -383,6 +389,11 @@ static int32_t afe_callback(struct apr_client_data *data, void *priv) payload = data->payload; if (data->opcode == APR_BASIC_RSP_RESULT) { + if (data->payload_size < (2 * sizeof(uint32_t))) { + pr_err("%s: Error: size %d is less than expected\n", + __func__, data->payload_size); + return -EINVAL; + } pr_debug("%s:opcode = 0x%x cmd = 0x%x status = 0x%x token=%d\n", __func__, data->opcode, payload[0], payload[1], data->token); From f2b32c9985ca975c0352953bcc2e49751516166a Mon Sep 17 00:00:00 2001 From: Swetha Nagapuri Date: Mon, 28 Jan 2019 18:54:56 +0530 Subject: [PATCH 264/276] dsp: q6usm: Check size of payload before access Check size of payload before access in q6usm_mmapcallback. Change-Id: Iff0672532c2ea40e7129237a92d8365d6b554cf2 Signed-off-by: Vatsal Bucha Signed-off-by: Meghana Reddy Mula --- dsp/q6usm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dsp/q6usm.c b/dsp/q6usm.c index 5b822ae99358..03cf3dbb067b 100644 --- a/dsp/q6usm.c +++ b/dsp/q6usm.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, 2019 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 @@ -502,6 +502,11 @@ static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv) uint32_t token; uint32_t *payload = data->payload; + if (data->payload_size < (2 * sizeof(uint32_t))) { + pr_err("%s: payload has invalid size[%d]\n", __func__, + data->payload_size); + return -EINVAL; + } 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", From 1530f656793f32db395477b945716a79d89c9531 Mon Sep 17 00:00:00 2001 From: kunleiz Date: Wed, 3 Apr 2019 18:00:14 +0800 Subject: [PATCH 265/276] dsp: q6core: validate payload size before memory copy Payload size is not checked before memory copy. Check payload size to avoid out-of-boundary memory access. Change-Id: I07857564d4e8ce415df3810b25f0e9e17a60993d Signed-off-by: Kunlei Zhang --- dsp/q6core.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/dsp/q6core.c b/dsp/q6core.c index 747154d0acc2..6369bf782578 100644 --- a/dsp/q6core.c +++ b/dsp/q6core.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2019, 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 @@ -184,7 +184,7 @@ int q6core_send_uevent(struct audio_uevent_data *uevent_data, char *event) } EXPORT_SYMBOL(q6core_send_uevent); -static int parse_fwk_version_info(uint32_t *payload) +static int parse_fwk_version_info(uint32_t *payload, uint16_t payload_size) { size_t ver_size; int num_services; @@ -197,6 +197,11 @@ static int parse_fwk_version_info(uint32_t *payload) * Based on this info, we copy the payload into core * avcs version info structure. */ + if (payload_size < 5 * sizeof(uint32_t)) { + pr_err("%s: payload has invalid size %d\n", + __func__, payload_size); + return -EINVAL; + } num_services = payload[4]; if (num_services > VSS_MAX_AVCS_NUM_SERVICES) { pr_err("%s: num_services: %d greater than max services: %d\n", @@ -211,6 +216,12 @@ static int parse_fwk_version_info(uint32_t *payload) ver_size = sizeof(struct avcs_get_fwk_version) + num_services * sizeof(struct avs_svc_api_info); + if (payload_size < ver_size) { + pr_err("%s: payload has invalid size %d, expected size %zu\n", + __func__, payload_size, ver_size); + return -EINVAL; + } + q6core_lcl.q6core_avcs_ver_info.ver_info = kzalloc(ver_size, GFP_ATOMIC); if (q6core_lcl.q6core_avcs_ver_info.ver_info == NULL) @@ -247,6 +258,12 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) payload1 = data->payload; + if (data->payload_size < 2 * sizeof(uint32_t)) { + pr_err("%s: payload has invalid size %d\n", + __func__, data->payload_size); + return -EINVAL; + } + switch (payload1[0]) { case AVCS_CMD_SHARED_MEM_UNMAP_REGIONS: @@ -307,6 +324,11 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) break; } case AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS: + if (data->payload_size < sizeof(uint32_t)) { + pr_err("%s: payload has invalid size %d\n", + __func__, data->payload_size); + return -EINVAL; + } payload1 = data->payload; pr_debug("%s: AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS handle %d\n", __func__, payload1[0]); @@ -315,6 +337,11 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) wake_up(&q6core_lcl.bus_bw_req_wait); break; case AVCS_CMDRSP_ADSP_EVENT_GET_STATE: + if (data->payload_size < sizeof(uint32_t)) { + pr_err("%s: payload has invalid size %d\n", + __func__, data->payload_size); + return -EINVAL; + } payload1 = data->payload; q6core_lcl.param = payload1[0]; pr_debug("%s: Received ADSP get state response 0x%x\n", @@ -325,6 +352,11 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) wake_up(&q6core_lcl.bus_bw_req_wait); break; case AVCS_CMDRSP_GET_LICENSE_VALIDATION_RESULT: + if (data->payload_size < sizeof(uint32_t)) { + pr_err("%s: payload has invalid size %d\n", + __func__, data->payload_size); + return -EINVAL; + } payload1 = data->payload; pr_debug("%s: cmd = LICENSE_VALIDATION_RESULT, result = 0x%x\n", __func__, payload1[0]); @@ -337,7 +369,7 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) pr_debug("%s: Received AVCS_CMDRSP_GET_FWK_VERSION\n", __func__); payload1 = data->payload; - ret = parse_fwk_version_info(payload1); + ret = parse_fwk_version_info(payload1, data->payload_size); if (ret < 0) { q6core_lcl.adsp_status = ret; pr_err("%s: Failed to parse payload:%d\n", From cdb357a0aff5f74049e9e6e2d31e29347f6db4f1 Mon Sep 17 00:00:00 2001 From: kunleiz Date: Fri, 12 Apr 2019 11:28:19 +0800 Subject: [PATCH 266/276] lsm: check payload size validity before using it as array index Payload size validity is not checked before using it in array index. Check payload size to avoid out-of-boundary memory. Change-Id: Ic0b06bb331fc1753ff7543bb218ab12d6a4a3ca8 Signed-off-by: Kunlei Zhang --- asoc/msm-lsm-client.c | 45 +++++++++++++++++++++++++++++++++++-------- dsp/q6lsm.c | 18 +++++++++++++---- include/dsp/q6lsm.h | 4 ++-- 3 files changed, 53 insertions(+), 14 deletions(-) diff --git a/asoc/msm-lsm-client.c b/asoc/msm-lsm-client.c index afbe9c2bb56d..5f43e9e9159b 100644 --- a/asoc/msm-lsm-client.c +++ b/asoc/msm-lsm-client.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2019, 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 @@ -195,7 +195,8 @@ static int lsm_lab_buffer_sanity(struct lsm_priv *prtd, } static void lsm_event_handler(uint32_t opcode, uint32_t token, - void *payload, void *priv) + void *payload, uint16_t client_size, + void *priv) { unsigned long flags; struct lsm_priv *prtd = priv; @@ -265,6 +266,12 @@ static void lsm_event_handler(uint32_t opcode, uint32_t token, } case LSM_SESSION_EVENT_DETECTION_STATUS: + if (client_size < 3 * sizeof(uint8_t)) { + dev_err(rtd->dev, + "%s: client_size has invalid size[%d]\n", + __func__, client_size); + return; + } status = (uint16_t)((uint8_t *)payload)[0]; payload_size = (uint16_t)((uint8_t *)payload)[2]; index = 4; @@ -274,6 +281,12 @@ static void lsm_event_handler(uint32_t opcode, uint32_t token, break; case LSM_SESSION_EVENT_DETECTION_STATUS_V2: + if (client_size < 2 * sizeof(uint8_t)) { + dev_err(rtd->dev, + "%s: client_size has invalid size[%d]\n", + __func__, client_size); + return; + } status = (uint16_t)((uint8_t *)payload)[0]; payload_size = (uint16_t)((uint8_t *)payload)[1]; index = 2; @@ -283,6 +296,12 @@ static void lsm_event_handler(uint32_t opcode, uint32_t token, break; case LSM_SESSION_EVENT_DETECTION_STATUS_V3: + if (client_size < 2 * (sizeof(uint32_t) + sizeof(uint8_t))) { + dev_err(rtd->dev, + "%s: client_size has invalid size[%d]\n", + __func__, client_size); + return; + } event_ts_lsw = ((uint32_t *)payload)[0]; event_ts_msw = ((uint32_t *)payload)[1]; status = (uint16_t)((uint8_t *)payload)[8]; @@ -321,12 +340,22 @@ static void lsm_event_handler(uint32_t opcode, uint32_t token, 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); + if (client_size >= (payload_size + index)) { + 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: Failed to copy memory with invalid size = %d\n", + __func__, payload_size); + return; + } } else { spin_unlock_irqrestore(&prtd->event_lock, flags); dev_err(rtd->dev, diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c index 848f2cd93868..16edacef4798 100644 --- a/dsp/q6lsm.c +++ b/dsp/q6lsm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, 2019 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 @@ -168,7 +168,8 @@ static int q6lsm_callback(struct apr_client_data *data, void *priv) struct lsm_cmd_read_done read_done; token = data->token; - if (data->payload_size > sizeof(read_done)) { + if (data->payload_size > sizeof(read_done) || + data->payload_size < 6 * sizeof(payload[0])) { pr_err("%s: read done error payload size %d expected size %zd\n", __func__, data->payload_size, sizeof(read_done)); @@ -186,6 +187,7 @@ static int q6lsm_callback(struct apr_client_data *data, void *priv) if (client->cb) client->cb(data->opcode, data->token, (void *)&read_done, + sizeof(read_done), client->priv); return 0; } else if (data->opcode == APR_BASIC_RSP_RESULT) { @@ -211,6 +213,11 @@ static int q6lsm_callback(struct apr_client_data *data, void *priv) __func__, token, client->session); return -EINVAL; } + if (data->payload_size < 2 * sizeof(payload[0])) { + pr_err("%s: payload has invalid size[%d]\n", + __func__, data->payload_size); + return -EINVAL; + } client->cmd_err_code = payload[1]; if (client->cmd_err_code) pr_err("%s: cmd 0x%x failed status %d\n", @@ -231,7 +238,7 @@ static int q6lsm_callback(struct apr_client_data *data, void *priv) if (client->cb) client->cb(data->opcode, data->token, data->payload, - client->priv); + data->payload_size, client->priv); return 0; } @@ -1351,6 +1358,8 @@ static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv) "proc 0x%x SID 0x%x\n", __func__, data->opcode, data->reset_event, data->reset_proc, sid); + if (sid < LSM_MIN_SESSION_ID || sid > LSM_MAX_SESSION_ID) + pr_err("%s: Invalid session %d\n", __func__, sid); apr_reset(lsm_common.apr); lsm_common.apr = NULL; atomic_set(&lsm_common.apr_users, 0); @@ -1416,7 +1425,8 @@ static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv) } if (client->cb) client->cb(data->opcode, data->token, - data->payload, client->priv); + data->payload, data->payload_size, + client->priv); return 0; } diff --git a/include/dsp/q6lsm.h b/include/dsp/q6lsm.h index efce3a6d2076..be0e9988b8f7 100644 --- a/include/dsp/q6lsm.h +++ b/include/dsp/q6lsm.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, 2019 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 @@ -26,7 +26,7 @@ #define LSM_MAX_NUM_CHANNELS 8 typedef void (*lsm_app_cb)(uint32_t opcode, uint32_t token, - uint32_t *payload, void *priv); + uint32_t *payload, uint16_t client_size, void *priv); struct lsm_sound_model { dma_addr_t phys; From 7e5941e38667160877cafde7b506a22b42b93d0a Mon Sep 17 00:00:00 2001 From: Tanya Dixit Date: Wed, 6 Feb 2019 14:06:48 +0530 Subject: [PATCH 267/276] dsp: q6voice: Check size of payload before access Check size of payload array before access in qdsp_mvm_callback. Change-Id: I81d945f963cfb4a3cb26155700b82880d891ec5e Signed-off-by: Vatsal Bucha --- dsp/q6voice.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/dsp/q6voice.c b/dsp/q6voice.c index 8a7281ca5843..dffddcb68e16 100644 --- a/dsp/q6voice.c +++ b/dsp/q6voice.c @@ -7076,7 +7076,7 @@ void voc_config_vocoder(uint32_t media_type, static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv) { - uint32_t *ptr = NULL; + uint32_t *ptr = NULL, min_payload_size = 0; struct common_data *c = NULL; struct voice_data *v = NULL; struct vss_evt_voice_activity *voice_act_update = NULL; @@ -7147,7 +7147,7 @@ static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv) } if (data->opcode == APR_BASIC_RSP_RESULT) { - if (data->payload_size) { + if (data->payload_size >= sizeof(ptr[0]) * 2) { ptr = data->payload; pr_debug("%x %x\n", ptr[0], ptr[1]); @@ -7217,7 +7217,13 @@ static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv) } 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) { + if (data->payload_size < sizeof(ptr[0])) { + pr_err("%s: payload has invalid size[%d]\n", __func__, + data->payload_size); + return -EINVAL; + } + + if (data->token == VOIP_MEM_MAP_TOKEN) { ptr = data->payload; if (ptr[0]) { v->shmem_info.mem_handle = ptr[0]; @@ -7284,10 +7290,13 @@ static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv) pr_debug("%s: Received VSS_IVERSION_RSP_GET\n", __func__); if (data->payload_size) { + min_payload_size = min_t(u32, (int)data->payload_size, + CVD_VERSION_STRING_MAX_SIZE); version_rsp = (struct vss_iversion_rsp_get_t *)data->payload; memcpy(common.cvd_version, version_rsp->version, - CVD_VERSION_STRING_MAX_SIZE); + min_payload_size); + common.cvd_version[min_payload_size - 1] = '\0'; pr_debug("%s: CVD Version = %s\n", __func__, common.cvd_version); From 9fc867180cd2d1090ef08258c80306533608a2bd Mon Sep 17 00:00:00 2001 From: Jhansi Konathala Date: Thu, 18 Apr 2019 10:59:14 +0530 Subject: [PATCH 268/276] Asoc: Add support for TDM over I2S Add support for TDM over I2S in sdxpoorwills machine driver and dai driver. CRs-fixed: 2389464 Change-Id: I870490eb37b9e2298ed6791fb9efb690aff8520b Signed-off-by: Jhansi Konathala --- asoc/msm-dai-q6-v2.c | 3 + asoc/sdxpoorwills.c | 995 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 994 insertions(+), 4 deletions(-) diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c index 616840c417c5..7785420bf752 100644 --- a/asoc/msm-dai-q6-v2.c +++ b/asoc/msm-dai-q6-v2.c @@ -6578,6 +6578,9 @@ static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai, /* HW supports 1-32 slots configuration. Typical: 1, 2, 4, 8, 16, 32 */ switch (slots) { + case 1: + cap_mask = 0x01; + break; case 2: cap_mask = 0x03; break; diff --git a/asoc/sdxpoorwills.c b/asoc/sdxpoorwills.c index 2d1a0db2d611..67409746d6af 100644 --- a/asoc/sdxpoorwills.c +++ b/asoc/sdxpoorwills.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2019, 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 @@ -45,7 +45,10 @@ #define SAMPLE_RATE_8KHZ 8000 #define SAMPLE_RATE_16KHZ 16000 +#define SAMPLING_RATE_32KHZ 32000 #define SAMPLE_RATE_48KHZ 48000 +#define SAMPLING_RATE_176P4KHZ 176400 +#define SAMPLING_RATE_352P8KHZ 352800 #define NUM_OF_BITS_PER_SAMPLE 16 #define DEV_NAME_STR_LEN 32 @@ -80,10 +83,41 @@ enum mi2s_types { SEC_MI2S, }; +enum { + TDM_0 = 0, + TDM_1, + TDM_2, + TDM_3, + TDM_4, + TDM_5, + TDM_6, + TDM_7, + TDM_PORT_MAX, +}; + +enum tdm_types { + TDM_PRI = 0, + TDM_SEC, + TDM_INTERFACE_MAX, +}; + +struct tdm_port { + u32 mode; + u32 channel; +}; + +struct dev_config { + u32 sample_rate; + u32 bit_format; + u32 channels; +}; + struct sdx_machine_data { u32 mclk_freq; u16 prim_mi2s_mode; u16 sec_mi2s_mode; + u16 prim_tdm_mode; + u16 sec_tdm_mode; u16 prim_auxpcm_mode; struct device_node *prim_master_p; struct device_node *prim_slave_p; @@ -100,6 +134,53 @@ struct sdx_machine_data { void __iomem *lpass_mux_mic_ctl_virt_addr; }; +/* TDM default config */ +static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, + { /* SEC TDM */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */ + }, +}; + +static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = { + { /* PRI TDM */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, + { /* SEC TDM */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */ + {SAMPLE_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */ + }, +}; + struct sdx_wsa881x_dev_info { struct device_node *of_node; u32 index; @@ -144,6 +225,8 @@ static int sdx_mi2s_mode = I2S_PCM_MASTER_MODE; static int sdx_sec_mi2s_mode = I2S_PCM_MASTER_MODE; static int sdx_auxpcm_mode = I2S_PCM_MASTER_MODE; static int sdx_sec_auxpcm_mode = I2S_PCM_MASTER_MODE; +static int sdx_prim_tdm_mode = I2S_PCM_MASTER_MODE; +static int sdx_sec_tdm_mode = I2S_PCM_MASTER_MODE; static int sdx_spk_control = 1; static int sdx_hifi_control; @@ -1144,6 +1227,723 @@ static int sdx_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int tdm_get_sample_rate(int value) +{ + int sample_rate = 0; + + switch (value) { + case 0: + sample_rate = SAMPLE_RATE_8KHZ; + break; + case 1: + sample_rate = SAMPLE_RATE_16KHZ; + break; + case 2: + sample_rate = SAMPLING_RATE_32KHZ; + break; + case 3: + sample_rate = SAMPLE_RATE_48KHZ; + break; + case 4: + sample_rate = SAMPLING_RATE_176P4KHZ; + break; + case 5: + sample_rate = SAMPLING_RATE_352P8KHZ; + break; + default: + sample_rate = SAMPLE_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 SAMPLE_RATE_8KHZ: + sample_rate_val = 0; + break; + case SAMPLE_RATE_16KHZ: + sample_rate_val = 1; + break; + case SAMPLING_RATE_32KHZ: + sample_rate_val = 2; + break; + case SAMPLE_RATE_48KHZ: + sample_rate_val = 3; + break; + case SAMPLING_RATE_176P4KHZ: + sample_rate_val = 4; + break; + case SAMPLING_RATE_352P8KHZ: + sample_rate_val = 5; + break; + default: + sample_rate_val = 3; + 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 { + 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 sdx_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 sdx_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 sdx_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 sdx_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 sdx_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 sdx_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 sdx_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 sdx_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 sdx_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 sdx_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 sdx_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 sdx_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 sdx_tdm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + 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 (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + 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 AFE_PORT_ID_PRIMARY_TDM_TX: + 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 AFE_PORT_ID_SECONDARY_TDM_RX: + 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 AFE_PORT_ID_SECONDARY_TDM_TX: + 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; + + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + pr_debug("%s: dai id = 0x%x channels = %d rate = %d format = 0x%x\n", + __func__, cpu_dai->id, channels->max, rate->max, + params_format(params)); + + return 0; +} + +static int sdX_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 slot_width = 32; + int channels, slots; + unsigned int slot_mask, rate, clk_freq; + unsigned int slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; + + pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id); + + /* currently only supporting TDM_RX_0 and TDM_TX_0 */ + switch (cpu_dai->id) { + case AFE_PORT_ID_PRIMARY_TDM_RX: + slots = tdm_rx_cfg[TDM_PRI][TDM_0].channels; + break; + case AFE_PORT_ID_SECONDARY_TDM_RX: + slots = tdm_rx_cfg[TDM_SEC][TDM_0].channels; + break; + case AFE_PORT_ID_PRIMARY_TDM_TX: + slots = tdm_tx_cfg[TDM_PRI][TDM_0].channels; + break; + case AFE_PORT_ID_SECONDARY_TDM_TX: + slots = tdm_tx_cfg[TDM_SEC][TDM_0].channels; + break; + default: + pr_err("%s: dai id 0x%x not supported\n", + __func__, cpu_dai->id); + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + channels = slots; + + pr_debug("%s: tdm rx slot_width %d slots %d\n", + __func__, slot_width, slots); + + 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 rx 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 tdm rx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + /*2 slot config - bits 0 and 1 set for the first two slots */ + slot_mask = 0x0000FFFF >> (16-slots); + channels = slots; + + pr_debug("%s: tdm tx slot_width %d slots %d\n", + __func__, slot_width, slots); + + 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 tx 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 tdm tx channel map, err:%d\n", + __func__, ret); + goto end; + } + } else { + ret = -EINVAL; + pr_err("%s: invalid use case, err:%d\n", + __func__, ret); + goto end; + } + + rate = params_rate(params); + clk_freq = rate * slot_width * slots; + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, clk_freq, SND_SOC_CLOCK_OUT); + if (ret < 0) + pr_err("%s: failed to set tdm clk, err:%d\n", + __func__, ret); + +end: + return ret; +} + +static void sdx_pri_tdm_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int ret; + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + + if (pdata->prim_tdm_mode == 1) + ret = msm_cdc_pinctrl_select_sleep_state(pdata->prim_master_p); + else + ret = msm_cdc_pinctrl_select_sleep_state(pdata->prim_slave_p); + if (ret) + pr_err("%s: failed to set prim gpios to sleep: %d\n", + __func__, ret); +} + +static int sdx_pri_tdm_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + + pdata->prim_tdm_mode = sdx_prim_tdm_mode; + if (pdata->lpaif_pri_muxsel_virt_addr != NULL) { + ret = afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_ON); + if (ret < 0) { + ret = -EINVAL; + goto done; + } + iowrite32(PCM_SEL << I2S_PCM_SEL_OFFSET, + pdata->lpaif_pri_muxsel_virt_addr); + if (pdata->lpass_mux_spkr_ctl_virt_addr != NULL) { + if (pdata->prim_tdm_mode == 1) + iowrite32(PRI_TLMM_CLKS_EN_MASTER, + pdata->lpass_mux_spkr_ctl_virt_addr); + else + iowrite32(PRI_TLMM_CLKS_EN_SLAVE, + pdata->lpass_mux_spkr_ctl_virt_addr); + } else { + dev_err(card->dev, "%s lpass_mux_spkr_ctl_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + goto err; + } + } else { + dev_err(card->dev, "%s lpaif_pri_muxsel_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + goto done; + } + + if (pdata->prim_tdm_mode == 1) { + ret = msm_cdc_pinctrl_select_active_state + (pdata->prim_master_p); + if (ret < 0) + pr_err("%s pinctrl set failed\n", __func__); + goto err; + } else { + ret = msm_cdc_pinctrl_select_active_state(pdata->prim_slave_p); + if (ret < 0) + pr_err("%s pinctrl set failed\n", __func__); + goto err; + } +err: + afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_OFF); +done: + return ret; +} + +static struct snd_soc_ops sdx_pri_tdm_be_ops = { + .hw_params = sdX_tdm_snd_hw_params, + .startup = sdx_pri_tdm_startup, + .shutdown = sdx_pri_tdm_shutdown, +}; + +static void sdx_sec_tdm_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + int ret; + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + + if (pdata->sec_tdm_mode == 1) + ret = msm_cdc_pinctrl_select_sleep_state(pdata->sec_master_p); + else + ret = msm_cdc_pinctrl_select_sleep_state(pdata->sec_slave_p); + if (ret) + pr_err("%s: failed to set sec gpios to sleep: %d\n", + __func__, ret); +} + +static int sdx_sec_tdm_startup(struct snd_pcm_substream *substream) +{ + int ret = 0; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct sdx_machine_data *pdata = snd_soc_card_get_drvdata(card); + + pdata->sec_tdm_mode = sdx_sec_tdm_mode; + if (pdata->lpaif_pri_muxsel_virt_addr != NULL) { + ret = afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_ON); + if (ret < 0) { + ret = -EINVAL; + goto done; + } + iowrite32(PCM_SEL << I2S_PCM_SEL_OFFSET, + pdata->lpaif_pri_muxsel_virt_addr); + if (pdata->lpass_mux_spkr_ctl_virt_addr != NULL) { + if (pdata->sec_tdm_mode == 1) + iowrite32(SEC_TLMM_CLKS_EN_MASTER, + pdata->lpass_mux_spkr_ctl_virt_addr); + else + iowrite32(SEC_TLMM_CLKS_EN_SLAVE, + pdata->lpass_mux_spkr_ctl_virt_addr); + } else { + dev_err(card->dev, "%s lpass_mux_spkr_ctl_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + goto err; + } + } else { + dev_err(card->dev, "%s lpaif_pri_muxsel_virt_addr is NULL\n", + __func__); + ret = -EINVAL; + goto done; + } + + if (pdata->sec_tdm_mode == 1) { + ret = msm_cdc_pinctrl_select_active_state + (pdata->prim_master_p); + if (ret < 0) + pr_err("%s pinctrl set failed\n", __func__); + goto err; + } else { + ret = msm_cdc_pinctrl_select_active_state(pdata->prim_slave_p); + if (ret < 0) + pr_err("%s pinctrl set failed\n", __func__); + goto err; + } +err: + afe_enable_lpass_core_shared_clock(MI2S_RX, CLOCK_OFF); +done: + return ret; +} + +static struct snd_soc_ops sdx_sec_tdm_be_ops = { + .hw_params = sdX_tdm_snd_hw_params, + .startup = sdx_sec_tdm_startup, + .shutdown = sdx_sec_tdm_shutdown, +}; + static const struct snd_soc_dapm_widget sdx_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0, @@ -1187,6 +1987,13 @@ static const char *const mi2s_rate_text[] = {"rate_8000", "rate_16000", "rate_48000"}; static const char *const mode_text[] = {"master", "slave"}; +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_48", "KHZ_176P4", + "KHZ_352P8"}; + static const struct soc_enum sdx_enum[] = { SOC_ENUM_SINGLE_EXT(2, spk_function), SOC_ENUM_SINGLE_EXT(2, mi2s_rx_ch_text), @@ -1197,6 +2004,13 @@ static const struct soc_enum sdx_enum[] = { SOC_ENUM_SINGLE_EXT(2, mode_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 const struct snd_kcontrol_new sdx_snd_controls[] = { SOC_ENUM_EXT("Speaker Function", sdx_enum[0], sdx_mi2s_get_spk, @@ -1237,6 +2051,43 @@ static const struct snd_kcontrol_new sdx_snd_controls[] = { SOC_ENUM_EXT("SEC_AUXPCM Mode", sdx_enum[6], sdx_sec_auxpcm_mode_get, sdx_sec_auxpcm_mode_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + sdx_tdm_rx_sample_rate_get, + sdx_tdm_rx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + sdx_tdm_tx_sample_rate_get, + sdx_tdm_tx_sample_rate_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format, + sdx_tdm_rx_format_get, + sdx_tdm_rx_format_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format, + sdx_tdm_tx_format_get, + sdx_tdm_tx_format_put), + SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs, + sdx_tdm_rx_ch_get, + sdx_tdm_rx_ch_put), + SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs, + sdx_tdm_tx_ch_get, + sdx_tdm_tx_ch_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate, + sdx_tdm_rx_sample_rate_get, + sdx_tdm_rx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate, + sdx_tdm_tx_sample_rate_get, + sdx_tdm_tx_sample_rate_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format, + sdx_tdm_rx_format_get, + sdx_tdm_rx_format_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format, + sdx_tdm_tx_format_get, + sdx_tdm_tx_format_put), + SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs, + sdx_tdm_rx_ch_get, + sdx_tdm_rx_ch_put), + SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs, + sdx_tdm_tx_ch_get, + sdx_tdm_tx_ch_put), + }; static int sdx_mi2s_audrx_init(struct snd_soc_pcm_runtime *rtd) @@ -1746,6 +2597,67 @@ static struct snd_soc_dai_link sdx_common_dai_links[] = { .codec_dai_name = "snd-soc-dummy-dai", .codec_name = "snd-soc-dummy", }, + { + .name = "Primary TDM0 RX Hostless", + .stream_name = "Primary TDM0 Hostless Playback", + .cpu_dai_name = "PRI_TDM_RX_0_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, + .ignore_pmdown_time = 1, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Primary TDM0 TX Hostless", + .stream_name = "Primary TDM0 Hostless Capture", + .cpu_dai_name = "PRI_TDM_TX_0_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, + .codec_dai_name = "snd-soc-dummy-dai", + .codec_name = "snd-soc-dummy", + }, + { + .name = "Secondary TDM RX 0 Hostless", + .stream_name = "Secondary TDM RX 0 Hostless", + .cpu_dai_name = "SEC_TDM_RX_0_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 = "Secondary TDM TX 0 Hostless", + .stream_name = "Secondary TDM TX 0 Hostless", + .cpu_dai_name = "SEC_TDM_TX_0_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 sdx_common_misc_fe_dai_links[] = { @@ -1967,12 +2879,83 @@ static struct snd_soc_dai_link sdx_auxpcm_be_dai_links[] = { }, }; +static struct snd_soc_dai_link sdx_tdm_be_dai_links[] = { + /* Primary RX TDM Backend DAI Links */ + { + .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, + .id = MSM_BACKEND_DAI_PRI_TDM_RX_0, + .be_hw_params_fixup = sdx_tdm_be_hw_params_fixup, + .ops = &sdx_pri_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + /* Primary TX TDM Backend DAI Links */ + + { + .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, + .id = MSM_BACKEND_DAI_PRI_TDM_TX_0, + .be_hw_params_fixup = sdx_tdm_be_hw_params_fixup, + .ops = &sdx_pri_tdm_be_ops, + .ignore_suspend = 1, + }, + + /* Secondary RX TDM Backend DAI Links */ + + { + .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, + .id = MSM_BACKEND_DAI_SEC_TDM_RX_0, + .be_hw_params_fixup = sdx_tdm_be_hw_params_fixup, + .ops = &sdx_sec_tdm_be_ops, + .ignore_suspend = 1, + .ignore_pmdown_time = 1, + }, + /* Secondary TX TDM Backend DAI Links */ + + { + .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, + .id = MSM_BACKEND_DAI_SEC_TDM_TX_0, + .be_hw_params_fixup = sdx_tdm_be_hw_params_fixup, + .ops = &sdx_sec_tdm_be_ops, + .ignore_suspend = 1, + }, + +}; + static struct snd_soc_dai_link sdx_tavil_snd_card_dai_links[ ARRAY_SIZE(sdx_common_dai_links) + ARRAY_SIZE(sdx_common_misc_fe_dai_links) + ARRAY_SIZE(sdx_common_be_dai_links) + ARRAY_SIZE(sdx_mi2s_be_dai_links) + - ARRAY_SIZE(sdx_auxpcm_be_dai_links)]; + ARRAY_SIZE(sdx_auxpcm_be_dai_links)+ + ARRAY_SIZE(sdx_tdm_be_dai_links)]; static struct snd_soc_card snd_soc_card_tavil_sdx = { .name = "sdx-tavil-i2s-snd-card", @@ -2068,7 +3051,7 @@ static int sdx_populate_dai_link_component_of_node(struct snd_soc_card *card) static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) { struct snd_soc_card *card = NULL; - int len_1, len_2, len_3, len_4; + int len_1, len_2, len_3, len_4, len_5; int total_links; card = &snd_soc_card_tavil_sdx; @@ -2076,7 +3059,8 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) len_2 = len_1 + ARRAY_SIZE(sdx_common_misc_fe_dai_links); len_3 = len_2 + ARRAY_SIZE(sdx_common_be_dai_links); len_4 = len_3 + ARRAY_SIZE(sdx_mi2s_be_dai_links); - total_links = len_4 + ARRAY_SIZE(sdx_auxpcm_be_dai_links); + len_5 = len_4 + ARRAY_SIZE(sdx_auxpcm_be_dai_links); + total_links = len_5 + ARRAY_SIZE(sdx_tdm_be_dai_links); memcpy(sdx_tavil_snd_card_dai_links, sdx_common_dai_links, sizeof(sdx_common_dai_links)); @@ -2092,6 +3076,9 @@ static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev) memcpy(sdx_tavil_snd_card_dai_links + len_4, sdx_auxpcm_be_dai_links, sizeof(sdx_auxpcm_be_dai_links)); + memcpy(sdx_tavil_snd_card_dai_links + len_5, + sdx_tdm_be_dai_links, + sizeof(sdx_tdm_be_dai_links)); if (card) { card->dai_link = sdx_tavil_snd_card_dai_links; From 7a0a7de23ef7f49f217305e0d3d841cdf1ee10d4 Mon Sep 17 00:00:00 2001 From: Vignesh Kulothungan Date: Wed, 22 May 2019 11:53:08 -0700 Subject: [PATCH 269/276] asoc: fix silence data during concurrent capture Remove static definition of size and index variables, which are unique to each capture session. Static definition of index variable may incorrectly copy data from wrong memory offset. This might inject silence/clutter in data copied to audio HAL. CRs-Fixed: 2457887 Change-Id: I7b2f48d21545655ddaa13566ab4ea87a1cbd7424 Signed-off-by: Vignesh Kulothungan --- asoc/msm-pcm-q6-v2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index e2f7c9d4a4ba..559d91a2291d 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -891,8 +891,8 @@ static int msm_pcm_capture_copy(struct snd_pcm_substream *substream, int xfer; char *bufptr; void *data = NULL; - static uint32_t idx; - static uint32_t size; + uint32_t idx = 0; + uint32_t size = 0; uint32_t offset = 0; struct snd_pcm_runtime *runtime = substream->runtime; struct msm_audio *prtd = substream->runtime->private_data; From 2c9af26a0494d407cb2ec0b9486482d34467a12a Mon Sep 17 00:00:00 2001 From: Aditya Bavanari Date: Fri, 26 Apr 2019 14:38:29 +0530 Subject: [PATCH 270/276] asoc: Ratelimit error logs to avoid excessive logging Ratelimit error logs of boundary check conditions in audio effects driver to avoid excessive logging. CRs-Fixed: 2426159 Change-Id: Iaf10eee281389773a21340997e3ffbe88c6e79f6 Signed-off-by: Aditya Bavanari --- asoc/msm-audio-effects-q6-v2.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/asoc/msm-audio-effects-q6-v2.c b/asoc/msm-audio-effects-q6-v2.c index 2385bac36f36..53428ebf4eed 100644 --- a/asoc/msm-audio-effects-q6-v2.c +++ b/asoc/msm-audio-effects-q6-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2017, 2019 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 @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -22,7 +23,8 @@ #define GET_NEXT(ptr, upper_limit, rc) \ ({ \ if (((ptr) + 1) > (upper_limit)) { \ - pr_err("%s: param list out of boundary\n", __func__); \ + pr_err_ratelimited("%s: param list out of boundary\n", \ + __func__); \ (rc) = -EINVAL; \ } \ ((rc) == 0) ? *(ptr)++ : -EINVAL; \ @@ -31,7 +33,8 @@ #define CHECK_PARAM_LEN(len, max_len, tag, rc) \ do { \ if ((len) > (max_len)) { \ - pr_err("%s: params length overflows\n", (tag)); \ + pr_err_ratelimited("%s: params length overflows\n", \ + (tag)); \ (rc) = -EINVAL; \ } \ } while (0) @@ -250,7 +253,8 @@ int msm_audio_effects_virtualizer_handler(struct audio_client *ac, } break; default: - pr_err("%s: Invalid command to set config\n", __func__); + pr_err_ratelimited("%s: Invalid command to set config\n", + __func__); break; } } @@ -720,7 +724,8 @@ int msm_audio_effects_reverb_handler(struct audio_client *ac, } break; default: - pr_err("%s: Invalid command to set config\n", __func__); + pr_err_ratelimited("%s: Invalid command to set config\n", + __func__); break; } } @@ -855,7 +860,8 @@ int msm_audio_effects_bass_boost_handler(struct audio_client *ac, } break; default: - pr_err("%s: Invalid command to set config\n", __func__); + pr_err_ratelimited("%s: Invalid command to set config\n", + __func__); break; } } @@ -964,7 +970,8 @@ int msm_audio_effects_pbe_handler(struct audio_client *ac, } break; default: - pr_err("%s: Invalid command to set config\n", __func__); + pr_err_ratelimited("%s: Invalid command to set config\n", + __func__); break; } } @@ -1194,7 +1201,8 @@ int msm_audio_effects_popless_eq_handler(struct audio_client *ac, } break; default: - pr_err("%s: Invalid command to set config\n", __func__); + pr_err_ratelimited("%s: Invalid command to set config\n", + __func__); break; } } @@ -1349,7 +1357,7 @@ static int __msm_audio_effects_volume_handler(struct audio_client *ac, } break; default: - pr_err("%s: Invalid command id: %d to set config\n", + pr_err_ratelimited("%s: Invalid command id: %d to set config\n", __func__, command_id); break; } From 26cad1ce8585661bcc5ae359d9699c9a9e7db1bd Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Tue, 24 Jul 2018 15:11:05 +0530 Subject: [PATCH 271/276] asoc: sdm660_cdc: Add L10 and L1 to on-demand supply list As L7B regulator used as micbias regulator for audio, SDM710 PMIC regulator require L10 and L1 also to be enabled along with L7B. Add L10 and L1 to on-demand supply list of audio regulators. CRs-Fixed: 2287834 Change-Id: I8beab1bba8a054e3366a06b7a12299d83c90fe0f Signed-off-by: Laxminath Kasam --- asoc/codecs/sdm660_cdc/msm-analog-cdc.c | 34 +++++++++++++++++++++++++ asoc/codecs/sdm660_cdc/msm-analog-cdc.h | 2 ++ 2 files changed, 36 insertions(+) diff --git a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c index a1732b722b9e..2730bf1bdfdb 100644 --- a/asoc/codecs/sdm660_cdc/msm-analog-cdc.c +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc.c @@ -88,6 +88,9 @@ static bool spkr_boost_en = true; static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = { "cdc-vdd-mic-bias", + "", + "cdc-vdda18-l10", + "cdc-vdd-l1", }; static struct wcd_mbhc_register @@ -3450,6 +3453,16 @@ static const struct snd_soc_dapm_widget msm_anlg_cdc_dapm_widgets[] = { msm_anlg_cdc_codec_enable_on_demand_supply, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VDDA18_L10_REGULATOR", SND_SOC_NOPM, + ON_DEMAND_VDDA18_L10, 0, + msm_anlg_cdc_codec_enable_on_demand_supply, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("VDD_L1_REGULATOR", SND_SOC_NOPM, + ON_DEMAND_VDD_L1, 0, + msm_anlg_cdc_codec_enable_on_demand_supply, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal1", MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0, @@ -4139,6 +4152,20 @@ static int msm_anlg_cdc_soc_probe(struct snd_soc_codec *codec) atomic_set(&sdm660_cdc->on_demand_list[ON_DEMAND_MICBIAS].ref, 0); + msm_anlg_cdc_update_micbias_regulator( + sdm660_cdc, + on_demand_supply_name[ON_DEMAND_VDD_L1], + &sdm660_cdc->on_demand_list[ON_DEMAND_VDD_L1]); + atomic_set(&sdm660_cdc->on_demand_list[ON_DEMAND_VDD_L1].ref, + 0); + + msm_anlg_cdc_update_micbias_regulator( + sdm660_cdc, + on_demand_supply_name[ON_DEMAND_VDDA18_L10], + &sdm660_cdc->on_demand_list[ON_DEMAND_VDDA18_L10]); + atomic_set(&sdm660_cdc->on_demand_list[ON_DEMAND_VDDA18_L10].ref, + 0); + sdm660_cdc->fw_data = devm_kzalloc(codec->dev, sizeof(*(sdm660_cdc->fw_data)), GFP_KERNEL); @@ -4185,6 +4212,13 @@ static int msm_anlg_cdc_soc_remove(struct snd_soc_codec *codec) sdm660_cdc_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL; atomic_set(&sdm660_cdc_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0); + sdm660_cdc_priv->on_demand_list[ON_DEMAND_VDD_L1].supply = NULL; + atomic_set(&sdm660_cdc_priv->on_demand_list[ON_DEMAND_VDD_L1].ref, + 0); + sdm660_cdc_priv->on_demand_list[ON_DEMAND_VDDA18_L10].supply = NULL; + atomic_set(&sdm660_cdc_priv->on_demand_list[ON_DEMAND_VDDA18_L10].ref, + 0); + wcd_mbhc_deinit(&sdm660_cdc_priv->mbhc); return 0; diff --git a/asoc/codecs/sdm660_cdc/msm-analog-cdc.h b/asoc/codecs/sdm660_cdc/msm-analog-cdc.h index 137fa9d94414..b39e870ef7e7 100644 --- a/asoc/codecs/sdm660_cdc/msm-analog-cdc.h +++ b/asoc/codecs/sdm660_cdc/msm-analog-cdc.h @@ -119,6 +119,8 @@ enum { enum { ON_DEMAND_MICBIAS = 0, ON_DEMAND_SPKDRV, + ON_DEMAND_VDDA18_L10, + ON_DEMAND_VDD_L1, ON_DEMAND_SUPPLIES_MAX, }; From a7dc95e769986a1fba9834df1a87c220fd08d6d3 Mon Sep 17 00:00:00 2001 From: Rajesha Kini Date: Mon, 17 Jun 2019 21:57:57 +0530 Subject: [PATCH 272/276] Asoc: Add support for auto card Add support for auto card in sdxpoorwills machine driver and dai driver. CRs-Fixed: 2483317 Change-Id: I3bb83efb42e23551b4ea2a0351beeb3ef3651311 Signed-off-by: Rajesha Kini --- asoc/codecs/wcd934x/wcd934x.c | 10 +- asoc/sdxpoorwills.c | 239 ++++++++++++++++++++++++++-------- 2 files changed, 195 insertions(+), 54 deletions(-) diff --git a/asoc/codecs/wcd934x/wcd934x.c b/asoc/codecs/wcd934x/wcd934x.c index 27c37875b39f..7ffe815fe1ff 100644 --- a/asoc/codecs/wcd934x/wcd934x.c +++ b/asoc/codecs/wcd934x/wcd934x.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2019, 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 @@ -8679,6 +8679,13 @@ static int tavil_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static int tavil_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + pr_debug("%s\n", __func__); + return 0; +} + static struct snd_soc_dai_ops tavil_dai_ops = { .startup = tavil_startup, .shutdown = tavil_shutdown, @@ -8693,6 +8700,7 @@ static struct snd_soc_dai_ops tavil_i2s_dai_ops = { .shutdown = tavil_shutdown, .hw_params = tavil_hw_params, .prepare = tavil_prepare, + .set_sysclk = tavil_set_dai_sysclk, .set_fmt = tavil_set_dai_fmt, }; diff --git a/asoc/sdxpoorwills.c b/asoc/sdxpoorwills.c index 67409746d6af..d30b1d0d537a 100644 --- a/asoc/sdxpoorwills.c +++ b/asoc/sdxpoorwills.c @@ -38,7 +38,7 @@ #include "codecs/wsa881x.h" /* Machine driver Name */ -#define DRV_NAME "sdx-asoc-tavil" +#define DRV_NAME "sdx-asoc-snd" #define __CHIPSET__ "SDX " #define SDX_DAILINK_NAME(name) (__CHIPSET__#name) @@ -78,6 +78,9 @@ #define SDX_SPK_ON 1 #define SDX_HIFI_ON 1 +#define SDX_MCLK_CLK_12P288MHZ 12288000 +#define TLV_CLKIN_MCLK 0 + enum mi2s_types { PRI_MI2S, SEC_MI2S, @@ -233,6 +236,14 @@ static int sdx_hifi_control; static atomic_t mi2s_ref_count; static atomic_t sec_mi2s_ref_count; +static struct snd_soc_card snd_soc_card_tavil_sdx = { + .name = "sdx-tavil-i2s-snd-card", +}; + +static struct snd_soc_card snd_soc_card_auto_sdx = { + .name = "sdx-auto-i2s-snd-card", +}; + static int sdx_wsa881x_init(struct snd_soc_component *component) { u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106}; @@ -446,7 +457,8 @@ static int sdx_mi2s_startup(struct snd_pcm_substream *substream) goto err; } ret = snd_soc_dai_set_fmt(codec_dai, - SND_SOC_DAIFMT_CBS_CFS); + SND_SOC_DAIFMT_CBS_CFS | + SND_SOC_DAIFMT_I2S); if (ret < 0) { sdx_mi2s_clk_ctl(rtd, false, PRI_MI2S, 0, pdata->prim_mi2s_mode); @@ -454,6 +466,16 @@ static int sdx_mi2s_startup(struct snd_pcm_substream *substream) "%s Set fmt for codec dai failed\n", __func__); } + if (!strcmp(card->name, snd_soc_card_auto_sdx.name)) { + ret = snd_soc_dai_set_sysclk(codec_dai, + TLV_CLKIN_MCLK, + pdata->mclk_freq, + SND_SOC_CLOCK_OUT); + if (ret < 0) { + pr_err("%s Set sysclk for codec dai failed 0x%8x\n", + __func__, ret); + } + } } else { /* * Disable bit clk in slave mode for QC codec. @@ -2190,6 +2212,26 @@ static int sdx_mi2s_audrx_init(struct snd_soc_pcm_runtime *rtd) return ret; } +static int sdx_mi2s_audrx_init_auto(struct snd_soc_pcm_runtime *rtd) +{ + int ret = 0; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + + pr_debug("%s dev_name %s\n", __func__, dev_name(cpu_dai->dev)); + + rtd->pmdown_time = 0; + ret = snd_soc_add_codec_controls(codec, sdx_snd_controls, + ARRAY_SIZE(sdx_snd_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, %d\n", + __func__, ret); + goto done; + } +done: + return ret; +} + static void *def_tavil_mbhc_cal(void) { void *tavil_wcd_cal; @@ -2949,6 +2991,41 @@ static struct snd_soc_dai_link sdx_tdm_be_dai_links[] = { }; +static struct snd_soc_dai_link sdx_auto_dai[] = { + /* Backend 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 = "tlv320aic3x-codec", + .codec_dai_name = "tlv320aic3x-hifi", + .no_pcm = 1, + .dpcm_playback = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_RX, + .init = &sdx_mi2s_audrx_init_auto, + .be_hw_params_fixup = &sdx_mi2s_rx_be_hw_params_fixup, + .ops = &sdx_mi2s_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 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 = "tlv320aic3x-codec", + .codec_dai_name = "tlv320aic3x-hifi", + .no_pcm = 1, + .dpcm_capture = 1, + .id = MSM_BACKEND_DAI_PRI_MI2S_TX, + .be_hw_params_fixup = &sdx_mi2s_tx_be_hw_params_fixup, + .ops = &sdx_mi2s_be_ops, + .ignore_pmdown_time = 1, + .ignore_suspend = 1, + }, +}; + static struct snd_soc_dai_link sdx_tavil_snd_card_dai_links[ ARRAY_SIZE(sdx_common_dai_links) + ARRAY_SIZE(sdx_common_misc_fe_dai_links) + @@ -2957,9 +3034,12 @@ static struct snd_soc_dai_link sdx_tavil_snd_card_dai_links[ ARRAY_SIZE(sdx_auxpcm_be_dai_links)+ ARRAY_SIZE(sdx_tdm_be_dai_links)]; -static struct snd_soc_card snd_soc_card_tavil_sdx = { - .name = "sdx-tavil-i2s-snd-card", -}; +static struct snd_soc_dai_link sdx_auto_snd_card_dai_links[ + ARRAY_SIZE(sdx_common_dai_links) + + ARRAY_SIZE(sdx_common_misc_fe_dai_links) + + ARRAY_SIZE(sdx_common_be_dai_links) + + ARRAY_SIZE(sdx_auto_dai) + + ARRAY_SIZE(sdx_auxpcm_be_dai_links)]; static int sdx_populate_dai_link_component_of_node(struct snd_soc_card *card) { @@ -3048,40 +3128,82 @@ static int sdx_populate_dai_link_component_of_node(struct snd_soc_card *card) return ret; } +static const struct of_device_id sdx_asoc_machine_of_match[] = { + { .compatible = "qcom,sdx-asoc-snd-tavil", + .data = "tavil_codec" }, + { .compatible = "qcom,sdx-asoc-snd-auto", + .data = "auto_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; + const struct of_device_id *match; int len_1, len_2, len_3, len_4, len_5; int total_links; - card = &snd_soc_card_tavil_sdx; - len_1 = ARRAY_SIZE(sdx_common_dai_links); - len_2 = len_1 + ARRAY_SIZE(sdx_common_misc_fe_dai_links); - len_3 = len_2 + ARRAY_SIZE(sdx_common_be_dai_links); - len_4 = len_3 + ARRAY_SIZE(sdx_mi2s_be_dai_links); - len_5 = len_4 + ARRAY_SIZE(sdx_auxpcm_be_dai_links); - total_links = len_5 + ARRAY_SIZE(sdx_tdm_be_dai_links); - memcpy(sdx_tavil_snd_card_dai_links, - sdx_common_dai_links, - sizeof(sdx_common_dai_links)); - memcpy(sdx_tavil_snd_card_dai_links + len_1, - sdx_common_misc_fe_dai_links, - sizeof(sdx_common_misc_fe_dai_links)); - memcpy(sdx_tavil_snd_card_dai_links + len_2, - sdx_common_be_dai_links, - sizeof(sdx_common_be_dai_links)); - memcpy(sdx_tavil_snd_card_dai_links + len_3, - sdx_mi2s_be_dai_links, - sizeof(sdx_mi2s_be_dai_links)); - memcpy(sdx_tavil_snd_card_dai_links + len_4, - sdx_auxpcm_be_dai_links, - sizeof(sdx_auxpcm_be_dai_links)); - memcpy(sdx_tavil_snd_card_dai_links + len_5, - sdx_tdm_be_dai_links, - sizeof(sdx_tdm_be_dai_links)); + match = of_match_node(sdx_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, "tavil_codec")) { + len_1 = ARRAY_SIZE(sdx_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(sdx_common_misc_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(sdx_common_be_dai_links); + len_4 = len_3 + ARRAY_SIZE(sdx_mi2s_be_dai_links); + len_5 = len_4 + ARRAY_SIZE(sdx_auxpcm_be_dai_links); + total_links = len_5 + ARRAY_SIZE(sdx_tdm_be_dai_links); + memcpy(sdx_tavil_snd_card_dai_links, + sdx_common_dai_links, + sizeof(sdx_common_dai_links)); + memcpy(sdx_tavil_snd_card_dai_links + len_1, + sdx_common_misc_fe_dai_links, + sizeof(sdx_common_misc_fe_dai_links)); + memcpy(sdx_tavil_snd_card_dai_links + len_2, + sdx_common_be_dai_links, + sizeof(sdx_common_be_dai_links)); + memcpy(sdx_tavil_snd_card_dai_links + len_3, + sdx_mi2s_be_dai_links, + sizeof(sdx_mi2s_be_dai_links)); + memcpy(sdx_tavil_snd_card_dai_links + len_4, + sdx_auxpcm_be_dai_links, + sizeof(sdx_auxpcm_be_dai_links)); + memcpy(sdx_tavil_snd_card_dai_links + len_5, + sdx_tdm_be_dai_links, + sizeof(sdx_tdm_be_dai_links)); + card = &snd_soc_card_tavil_sdx; + dailink = sdx_tavil_snd_card_dai_links; + } else if (!strcmp(match->data, "auto_codec")) { + len_1 = ARRAY_SIZE(sdx_common_dai_links); + len_2 = len_1 + ARRAY_SIZE(sdx_common_misc_fe_dai_links); + len_3 = len_2 + ARRAY_SIZE(sdx_common_be_dai_links); + len_4 = len_3 + ARRAY_SIZE(sdx_auto_dai); + total_links = len_4 + ARRAY_SIZE(sdx_auxpcm_be_dai_links); + memcpy(sdx_auto_snd_card_dai_links, + sdx_common_dai_links, + sizeof(sdx_common_dai_links)); + memcpy(sdx_auto_snd_card_dai_links + len_1, + sdx_common_misc_fe_dai_links, + sizeof(sdx_common_misc_fe_dai_links)); + memcpy(sdx_auto_snd_card_dai_links + len_2, + sdx_common_be_dai_links, + sizeof(sdx_common_be_dai_links)); + memcpy(sdx_auto_snd_card_dai_links + len_3, + sdx_auto_dai, + sizeof(sdx_auto_dai)); + memcpy(sdx_auto_snd_card_dai_links + len_4, + sdx_auxpcm_be_dai_links, + sizeof(sdx_auxpcm_be_dai_links)); + card = &snd_soc_card_auto_sdx; + dailink = sdx_auto_snd_card_dai_links; + } if (card) { - card->dai_link = sdx_tavil_snd_card_dai_links; + card->dai_link = dailink; card->num_links = total_links; } @@ -3253,6 +3375,7 @@ static int sdx_asoc_machine_probe(struct platform_device *pdev) int ret; struct sdx_machine_data *pdata; struct snd_soc_card *card; + const struct of_device_id *match; if (!pdev->dev.of_node) { dev_err(&pdev->dev, @@ -3261,6 +3384,12 @@ static int sdx_asoc_machine_probe(struct platform_device *pdev) return -EINVAL; } + match = of_match_node(sdx_asoc_machine_of_match, pdev->dev.of_node); + if (!match) { + dev_err(&pdev->dev, "%s: No DT match found for sound card\n", + __func__); + return -EINVAL; + } pdata = devm_kzalloc(&pdev->dev, sizeof(struct sdx_machine_data), GFP_KERNEL); if (!pdata) @@ -3273,16 +3402,20 @@ static int sdx_asoc_machine_probe(struct platform_device *pdev) goto err; } - ret = of_property_read_u32(pdev->dev.of_node, - "qcom,tavil-mclk-clk-freq", - &pdata->mclk_freq); - if (ret) { - dev_err(&pdev->dev, - "%s Looking up %s property in node %s failed", - __func__, "qcom,tavil-mclk-clk-freq", - pdev->dev.of_node->full_name); + if (!strcmp(match->data, "tavil_codec")) { + ret = of_property_read_u32(pdev->dev.of_node, + "qcom,tavil-mclk-clk-freq", + &pdata->mclk_freq); + if (ret) { + dev_err(&pdev->dev, + "%s Looking up %s property in node %s failed", + __func__, "qcom,tavil-mclk-clk-freq", + pdev->dev.of_node->full_name); - goto err; + goto err; + } + } else { + pdata->mclk_freq = SDX_MCLK_CLK_12P288MHZ; } /* At present only 12.288MHz is supported on SDX. */ if (q6afe_check_osr_clk_freq(pdata->mclk_freq)) { @@ -3314,25 +3447,30 @@ static int sdx_asoc_machine_probe(struct platform_device *pdev) ret = snd_soc_of_parse_card_name(card, "qcom,model"); if (ret) goto err; - ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing"); - if (ret) - goto err; + if (of_property_read_bool(pdev->dev.of_node, "qcom,audio-routing")) { + ret = snd_soc_of_parse_audio_routing(card, + "qcom,audio-routing"); + if (ret) + goto err; + } ret = sdx_populate_dai_link_component_of_node(card); if (ret) { ret = -EPROBE_DEFER; goto err; } - ret = sdx_init_wsa_dev(pdev, card); - if (ret) - goto err; + /* As Two Codec Probed, set wsa init for tavil codec */ + if (!strcmp(match->data, "tavil_codec")) { + ret = sdx_init_wsa_dev(pdev, card); + if (ret) + goto err; + } ret = snd_soc_register_card(card); if (ret == -EPROBE_DEFER) { goto err; } else if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", - ret); + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); goto err; } @@ -3397,11 +3535,6 @@ static int sdx_asoc_machine_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id sdx_asoc_machine_of_match[] = { - { .compatible = "qcom,sdx-asoc-snd-tavil", }, - {}, -}; - static struct platform_driver sdx_asoc_machine_driver = { .driver = { .name = DRV_NAME, From 0ee480117f2fa16d27d9f1cc28f8032f8585ad4a Mon Sep 17 00:00:00 2001 From: Sujin Panicker Date: Thu, 26 Sep 2019 16:54:22 +0530 Subject: [PATCH 273/276] dsp: adm: Add error check to avoid memory overread For ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST adsp response, add additional check to make sure there is enough data for copy from adsp payload. Change-Id: Ib8fef116ca73ce68e872616db969f7112f289b69 Signed-off-by: Sujin Panicker --- dsp/q6adm.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dsp/q6adm.c b/dsp/q6adm.c index 372f2043eae6..676e5c9cf03f 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -1614,9 +1614,12 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) pr_err(":err = 0x%x\n", payload[0]); } else if (data->payload_size >= (2 * sizeof(uint32_t))) { - if (payload[1] > + if ((payload[1] > ((ADM_GET_TOPO_MODULE_LIST_LENGTH / - sizeof(uint32_t)) - 1)) { + sizeof(uint32_t)) - 1)) || + ((data->payload_size - + (2 * sizeof(uint32_t))) < + (payload[1] * sizeof(uint32_t)))) { pr_err("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST", __func__); pr_err(":size = %d\n", payload[1]); From cc37cbb842f1687a05a9e268ffd8b65ac9818acf Mon Sep 17 00:00:00 2001 From: Sujin Panicker Date: Thu, 26 Sep 2019 18:08:51 +0530 Subject: [PATCH 274/276] dsp: adm: Fix to avoid memory overread in adm callback For ADM_CMDRSP_GET_PP_PARAMS_V5 cmd response, the check for data payload_size is incorrect. Modify the check condition to make sure there is enough data to copy, size is contained in paylode. Change-Id: I2f155ad8b302e89131ee85cfc72e4009dda617d3 Signed-off-by: Sujin Panicker --- dsp/q6adm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dsp/q6adm.c b/dsp/q6adm.c index 372f2043eae6..d5fdaa079169 100644 --- a/dsp/q6adm.c +++ b/dsp/q6adm.c @@ -1573,7 +1573,8 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv) idx = ADM_GET_PARAMETER_LENGTH * copp_idx; if ((payload[0] == 0) && (data->payload_size > (4 * sizeof(*payload))) && - (data->payload_size - 4 >= + (data->payload_size - + (4 * sizeof(*payload)) >= payload[3]) && (ARRAY_SIZE(adm_get_parameters) > idx) && From 06785f382d8040b351efc3a847226f5a7f34f572 Mon Sep 17 00:00:00 2001 From: Ajit Pandey Date: Thu, 5 Sep 2019 16:26:01 +0530 Subject: [PATCH 275/276] asoc: msm-pcm: Added lock in controls _put() and _get() callback There is race condition around private data used in put() and get() of few mixer ctls with close() callback. Added global mutex lock and code changes to protect such critical section by accessing such lock. Change-Id: Ic160b0c7c29d0ea5d517b3bacc53828d8d61e085 Signed-off-by: Ajit Pandey --- asoc/msm-pcm-q6-v2.c | 86 ++++++++++++++++++++++++++++++++++++++++++-- asoc/msm-pcm-q6-v2.h | 1 + 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 559d91a2291d..6b9bb427e385 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -836,6 +837,14 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream) pr_debug("%s: cmd_pending 0x%lx\n", __func__, prtd->cmd_pending); + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: platform data is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&pdata->lock); if (prtd->audio_client) { dir = IN; @@ -843,8 +852,6 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream) * Unvote to downgrade the Rx thread priority from * RT Thread for Low-Latency use case. */ - pdata = (struct msm_plat_data *) - dev_get_drvdata(soc_prtd->platform->dev); if (pdata) { if (pdata->perf_mode == LOW_LATENCY_PCM_MODE) apr_end_rx_rt(prtd->audio_client->apr); @@ -878,6 +885,7 @@ static int msm_pcm_playback_close(struct snd_pcm_substream *substream) msm_adsp_clean_mixer_ctl_pp_event_queue(soc_prtd); kfree(prtd); runtime->private_data = NULL; + mutex_unlock(&pdata->lock); return 0; } @@ -971,9 +979,20 @@ 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; + struct msm_plat_data *pdata; int dir = OUT; pr_debug("%s\n", __func__); + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: platform data is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&pdata->lock); + if (prtd->audio_client) { q6asm_cmd(prtd->audio_client, CMD_CLOSE); q6asm_audio_client_buf_free_contiguous(dir, @@ -985,6 +1004,7 @@ static int msm_pcm_capture_close(struct snd_pcm_substream *substream) SNDRV_PCM_STREAM_CAPTURE); kfree(prtd); runtime->private_data = NULL; + mutex_unlock(&pdata->lock); return 0; } @@ -1154,6 +1174,7 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, goto done; } + mutex_lock(&pdata->lock); event_data = (struct msm_adsp_event_data *)ucontrol->value.bytes.data; if ((event_data->event_type < ADSP_STREAM_PP_EVENT) || (event_data->event_type >= ADSP_STREAM_EVENT_MAX)) { @@ -1185,6 +1206,7 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, pr_err("%s: failed to send stream event cmd, err = %d\n", __func__, ret); done: + mutex_unlock(&pdata->lock); return ret; } @@ -1348,6 +1370,8 @@ static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); struct snd_pcm_substream *substream = vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_soc_pcm_runtime *soc_prtd = NULL; + struct msm_plat_data *pdata = NULL; struct msm_audio *prtd; int volume = ucontrol->value.integer.value[0]; @@ -1360,11 +1384,27 @@ static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, pr_err("%s substream runtime not found\n", __func__); return 0; } + + soc_prtd = substream->private_data; + if (!soc_prtd) { + pr_err("%s: soc_prtd is NULL\n", __func__); + return -ENODEV; + } + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: platform data is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&pdata->lock); prtd = substream->runtime->private_data; if (prtd) { rc = msm_pcm_set_volume(prtd, volume); prtd->volume = volume; } + mutex_unlock(&pdata->lock); return rc; } @@ -1527,15 +1567,32 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol, 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 snd_soc_pcm_runtime *soc_prtd = NULL; + struct msm_plat_data *pdata = NULL; struct msm_audio *prtd; pr_debug("%s", __func__); substream = snd_pcm_chmap_substream(info, idx); if (!substream) return -ENODEV; + if (!substream->runtime) return 0; + soc_prtd = substream->private_data; + if (!soc_prtd) { + pr_err("%s: soc_prtd is NULL\n", __func__); + return -ENODEV; + } + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: platform data is NULL\n", __func__); + return -EINVAL; + } + + mutex_lock(&pdata->lock); prtd = substream->runtime->private_data; if (prtd) { prtd->set_channel_map = true; @@ -1543,6 +1600,7 @@ static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol, prtd->channel_map[i] = (char)(ucontrol->value.integer.value[i]); } + mutex_unlock(&pdata->lock); return 0; } @@ -1553,17 +1611,37 @@ static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol, 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 snd_soc_pcm_runtime *soc_prtd = NULL; + struct msm_plat_data *pdata = NULL; 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 */ + soc_prtd = substream->private_data; + if (!soc_prtd) { + pr_err("%s: soc_prtd is NULL\n", __func__); + return -ENODEV; + } + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: platform data is NULL\n", __func__); + return -EINVAL; + } + + memset(ucontrol->value.integer.value, 0, + sizeof(ucontrol->value.integer.value)); + + mutex_lock(&pdata->lock); prtd = substream->runtime->private_data; if (prtd && prtd->set_channel_map == true) { @@ -1575,6 +1653,7 @@ static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[i] = 0; } + mutex_unlock(&pdata->lock); return 0; } @@ -1975,7 +2054,7 @@ static int msm_pcm_probe(struct platform_device *pdev) } else { pdata->perf_mode = LEGACY_PCM_MODE; } - + mutex_init(&pdata->lock); dev_set_drvdata(&pdev->dev, pdata); @@ -1990,6 +2069,7 @@ static int msm_pcm_remove(struct platform_device *pdev) struct msm_plat_data *pdata; pdata = dev_get_drvdata(&pdev->dev); + mutex_destroy(&pdata->lock); kfree(pdata); snd_soc_unregister_platform(&pdev->dev); return 0; diff --git a/asoc/msm-pcm-q6-v2.h b/asoc/msm-pcm-q6-v2.h index 810a0082d5b3..43fee549709d 100644 --- a/asoc/msm-pcm-q6-v2.h +++ b/asoc/msm-pcm-q6-v2.h @@ -126,6 +126,7 @@ struct output_meta_data_st { struct msm_plat_data { int perf_mode; struct snd_pcm *pcm[MSM_FRONTEND_DAI_MAX]; + struct mutex lock; }; #endif /*_MSM_PCM_H*/ From 75e980c3a6a2fdec7e4619b091403b6120fa939e Mon Sep 17 00:00:00 2001 From: Soumya Managoli Date: Fri, 18 Oct 2019 14:52:36 +0530 Subject: [PATCH 276/276] asoc: msm-pcm: Add mutex lock to protect prvt data Add mutex lock to protect private data in _put() and get() calls. Change-Id: I92f5a6515b6d1c4ad650a7dcf22a0a231a84dd30 Signed-off-by: Prasad Kumpatla --- asoc/msm-pcm-loopback-v2.c | 12 ++++++++- asoc/msm-pcm-q6-noirq.c | 55 ++++++++++++++++++++++++++++++++++---- asoc/msm-pcm-q6-v2.c | 24 ++++++++++++++--- 3 files changed, 81 insertions(+), 10 deletions(-) diff --git a/asoc/msm-pcm-loopback-v2.c b/asoc/msm-pcm-loopback-v2.c index 3b6b3a840eab..61318050162f 100644 --- a/asoc/msm-pcm-loopback-v2.c +++ b/asoc/msm-pcm-loopback-v2.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2013-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2013-2019, 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 @@ -137,6 +137,7 @@ static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol, goto done; } + mutex_lock(&loopback_session_lock); pr_debug("%s: mute=%d\n", __func__, mute); hfp_tx_mute = mute; for (n = 0; n < LOOPBACK_SESSION_MAX; n++) { @@ -149,6 +150,7 @@ static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol, pr_err("%s: Send mute command failed rc=%d\n", __func__, ret); } + mutex_unlock(&loopback_session_lock); done: return ret; } @@ -351,6 +353,7 @@ static void stop_pcm(struct msm_pcm_loopback *pcm) if (pcm->audio_client == NULL) return; + mutex_lock(&loopback_session_lock); q6asm_cmd(pcm->audio_client, CMD_CLOSE); if (pcm->playback_substream != NULL) { @@ -365,6 +368,7 @@ static void stop_pcm(struct msm_pcm_loopback *pcm) } q6asm_audio_client_free(pcm->audio_client); pcm->audio_client = NULL; + mutex_unlock(&loopback_session_lock); } static int msm_pcm_close(struct snd_pcm_substream *substream) @@ -495,12 +499,15 @@ static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, rc = -ENODEV; goto exit; } + mutex_lock(&loopback_session_lock); prtd = substream->runtime->private_data; if (!prtd) { rc = -ENODEV; + mutex_unlock(&loopback_session_lock); goto exit; } rc = pcm_loopback_set_volume(prtd, volume); + mutex_unlock(&loopback_session_lock); exit: return rc; @@ -521,12 +528,15 @@ static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, rc = -ENODEV; goto exit; } + mutex_lock(&loopback_session_lock); prtd = substream->runtime->private_data; if (!prtd) { rc = -ENODEV; + mutex_unlock(&loopback_session_lock); goto exit; } ucontrol->value.integer.value[0] = prtd->volume; + mutex_unlock(&loopback_session_lock); exit: return rc; diff --git a/asoc/msm-pcm-q6-noirq.c b/asoc/msm-pcm-q6-noirq.c index 386b52a65fcc..9698c2e3864e 100644 --- a/asoc/msm-pcm-q6-noirq.c +++ b/asoc/msm-pcm-q6-noirq.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2019, 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 @@ -577,6 +577,7 @@ static int msm_pcm_prepare(struct snd_pcm_substream *substream) static int msm_pcm_close(struct snd_pcm_substream *substream) { + struct msm_plat_data *pdata = NULL; struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_prtd = substream->private_data; struct msm_audio *prtd = runtime->private_data; @@ -585,6 +586,21 @@ static int msm_pcm_close(struct snd_pcm_substream *substream) int dir = 0; int ret = 0; + if (!soc_prtd) { + pr_debug("%s private_data not found\n", + __func__); + return 0; + } + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: pdata not found\n", __func__); + return -ENODEV; + } + + mutex_lock(&pdata->lock); + if (ac) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dir = IN; @@ -619,6 +635,7 @@ static int msm_pcm_close(struct snd_pcm_substream *substream) SNDRV_PCM_STREAM_CAPTURE); kfree(prtd); runtime->private_data = NULL; + mutex_unlock(&pdata->lock); return 0; } @@ -643,8 +660,10 @@ 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 msm_plat_data *pdata = NULL; struct snd_pcm_substream *substream = vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_soc_pcm_runtime *soc_prtd = NULL; struct msm_audio *prtd; pr_debug("%s\n", __func__); @@ -652,13 +671,24 @@ static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, pr_err("%s substream not found\n", __func__); return -ENODEV; } - if (!substream->runtime) { - pr_debug("%s substream runtime not found\n", __func__); + soc_prtd = substream->private_data; + if (!substream->runtime || !soc_prtd) { + pr_debug("%s substream runtime or private_data not found\n", + __func__); return 0; } + + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: pdata not found\n", __func__); + return -ENODEV; + } + mutex_lock(&pdata->lock); prtd = substream->runtime->private_data; if (prtd) ucontrol->value.integer.value[0] = prtd->volume; + mutex_unlock(&pdata->lock); return 0; } @@ -667,8 +697,10 @@ static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, { int rc = 0; struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol); + struct msm_plat_data *pdata = NULL; struct snd_pcm_substream *substream = vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_soc_pcm_runtime *soc_prtd = NULL; struct msm_audio *prtd; int volume = ucontrol->value.integer.value[0]; @@ -677,15 +709,25 @@ static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol, pr_err("%s substream not found\n", __func__); return -ENODEV; } - if (!substream->runtime) { - pr_err("%s substream runtime not found\n", __func__); + soc_prtd = substream->private_data; + if (!substream->runtime || !soc_prtd) { + pr_debug("%s substream runtime or private_data not found\n", + __func__); return 0; } + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: pdata not found\n", __func__); + return -ENODEV; + } + mutex_lock(&pdata->lock); prtd = substream->runtime->private_data; if (prtd) { rc = msm_pcm_set_volume(prtd, volume); prtd->volume = volume; } + mutex_unlock(&pdata->lock); return rc; } @@ -1228,6 +1270,8 @@ static int msm_pcm_probe(struct platform_device *pdev) pdata->perf_mode = perf_mode; + mutex_init(&pdata->lock); + dev_set_drvdata(&pdev->dev, pdata); dev_dbg(&pdev->dev, "%s: dev name %s\n", @@ -1248,6 +1292,7 @@ static int msm_pcm_remove(struct platform_device *pdev) dev_dbg(&pdev->dev, "Pull mode remove\n"); pdata = dev_get_drvdata(&pdev->dev); + mutex_destroy(&pdata->lock); devm_kfree(&pdev->dev, pdata); snd_soc_unregister_platform(&pdev->dev); return 0; diff --git a/asoc/msm-pcm-q6-v2.c b/asoc/msm-pcm-q6-v2.c index 559d91a2291d..ecf1915fe205 100644 --- a/asoc/msm-pcm-q6-v2.c +++ b/asoc/msm-pcm-q6-v2.c @@ -1123,10 +1123,10 @@ static int msm_pcm_adsp_stream_cmd_put(struct snd_kcontrol *kcontrol, if (!pdata) { pr_err("%s pdata is NULL\n", __func__); - ret = -ENODEV; - goto done; + return -ENODEV; } + mutex_lock(&pdata->lock); substream = pdata->pcm[kcontrol->private_value]-> streams[SNDRV_PCM_STREAM_PLAYBACK].substream; if (!substream) { @@ -1322,8 +1322,10 @@ 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 msm_plat_data *pdata = NULL; struct snd_pcm_substream *substream = vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + struct snd_soc_pcm_runtime *soc_prtd = NULL; struct msm_audio *prtd; pr_debug("%s\n", __func__); @@ -1331,13 +1333,23 @@ static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol, pr_err("%s substream not found\n", __func__); return -ENODEV; } - if (!substream->runtime) { - pr_debug("%s substream runtime not found\n", __func__); + soc_prtd = substream->private_data; + if (!substream->runtime || !soc_prtd) { + pr_debug("%s substream runtime or private_data not found\n", + __func__); return 0; } + pdata = (struct msm_plat_data *) + dev_get_drvdata(soc_prtd->platform->dev); + if (!pdata) { + pr_err("%s: pdata not found\n", __func__); + return -ENODEV; + } + mutex_lock(&pdata->lock); prtd = substream->runtime->private_data; if (prtd) ucontrol->value.integer.value[0] = prtd->volume; + mutex_unlock(&pdata->lock); return 0; } @@ -1423,9 +1435,11 @@ static int msm_pcm_compress_ctl_get(struct snd_kcontrol *kcontrol, pr_debug("%s substream runtime not found\n", __func__); return 0; } + mutex_lock(&pdata->lock); prtd = substream->runtime->private_data; if (prtd) ucontrol->value.integer.value[0] = prtd->compress_enable; + mutex_unlock(&pdata->lock); return 0; } @@ -1455,12 +1469,14 @@ static int msm_pcm_compress_ctl_put(struct snd_kcontrol *kcontrol, pr_err("%s substream runtime not found\n", __func__); return 0; } + mutex_lock(&pdata->lock); prtd = substream->runtime->private_data; if (prtd) { pr_debug("%s: setting compress flag to 0x%x\n", __func__, compress); prtd->compress_enable = compress; } + mutex_unlock(&pdata->lock); return rc; }