From e4f33f63b8dcd4bb98bd93556a9d16a19aa9d94c Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Fri, 29 May 2026 12:53:22 +0530 Subject: [PATCH 1/5] dt-bindings: sound: qcom: add Shikra QAIF Document the QAIF CPU DAI controller binding used by Shikra audio. Signed-off-by: Mohammad Rafi Shaik --- .../bindings/sound/qcom,shikra-qaif.yaml | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/qcom,shikra-qaif.yaml diff --git a/Documentation/devicetree/bindings/sound/qcom,shikra-qaif.yaml b/Documentation/devicetree/bindings/sound/qcom,shikra-qaif.yaml new file mode 100644 index 0000000000000..640dc5f86b198 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/qcom,shikra-qaif.yaml @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/qcom,shikra-qaif.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Audio Interface on Shikra + +maintainers: + - Qualcomm Innovation Center, Inc. + +description: + Qualcomm Audio Interface CPU DAI controller used by the Shikra audio core. + +properties: + compatible: + const: qcom,shikra-qaif-cpu + + reg: + maxItems: 1 + + reg-names: + const: audio-qaif-core + + iommus: + maxItems: 1 + + clocks: + minItems: 15 + maxItems: 15 + + clock-names: + items: + - const: gcc_lpass_config_clk + - const: gcc_lpass_core_axim_clk + - const: audio_core_cc_aud_dma_clk + - const: audio_core_cc_aud_dma_mem_clk + - const: audio_core_cc_bus_clk + - const: audio_core_cc_aif_if0_ebit_clk + - const: audio_core_cc_aif_if0_ibit_clk + - const: audio_core_cc_aif_if1_ebit_clk + - const: audio_core_cc_aif_if1_ibit_clk + - const: audio_core_cc_aif_if2_ebit_clk + - const: audio_core_cc_aif_if2_ibit_clk + - const: audio_core_cc_aif_if3_ebit_clk + - const: audio_core_cc_aif_if3_ibit_clk + - const: audio_core_cc_ext_mclka_clk + - const: audio_core_cc_ext_mclkb_clk + + interrupts: + maxItems: 1 + + interrupt-names: + const: qaif-irq-audio-core + + '#sound-dai-cells': + const: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + aif-interface: + $ref: /schemas/types.yaml#/definitions/phandle + description: TDM/MI2S interface configuration referenced by this controller. + +required: + - compatible + - reg + - reg-names + - iommus + - clocks + - clock-names + - interrupts + - interrupt-names + - '#sound-dai-cells' + +additionalProperties: false + +examples: + - | + #include + #include + #include + + audio@a000000 { + compatible = "qcom,shikra-qaif-cpu"; + reg = <0x0a000000 0x20000>; + reg-names = "audio-qaif-core"; + iommus = <&apps_smmu 0x1c0 0x0>; + clocks = <&gcc 0>, <&gcc 1>, + <&audiocorecc AUDIO_CORE_CC_AUD_DMA_CLK>, + <&audiocorecc AUDIO_CORE_CC_AUD_DMA_MEM_CLK>, + <&audiocorecc AUDIO_CORE_CC_BUS_CLK>, + <&audiocorecc AUDIO_CORE_CC_AIF_IF0_EBIT_CLK>, + <&audiocorecc AUDIO_CORE_CC_AIF_IF0_IBIT_CLK>, + <&audiocorecc AUDIO_CORE_CC_AIF_IF1_EBIT_CLK>, + <&audiocorecc AUDIO_CORE_CC_AIF_IF1_IBIT_CLK>, + <&audiocorecc AUDIO_CORE_CC_AIF_IF2_EBIT_CLK>, + <&audiocorecc AUDIO_CORE_CC_AIF_IF2_IBIT_CLK>, + <&audiocorecc AUDIO_CORE_CC_AIF_IF3_EBIT_CLK>, + <&audiocorecc AUDIO_CORE_CC_AIF_IF3_IBIT_CLK>, + <&audiocorecc AUDIO_CORE_CC_EXT_MCLKA_OUT_CLK>, + <&audiocorecc AUDIO_CORE_CC_EXT_MCLKB_OUT_CLK>; + clock-names = "gcc_lpass_config_clk", "gcc_lpass_core_axim_clk", + "audio_core_cc_aud_dma_clk", + "audio_core_cc_aud_dma_mem_clk", + "audio_core_cc_bus_clk", + "audio_core_cc_aif_if0_ebit_clk", + "audio_core_cc_aif_if0_ibit_clk", + "audio_core_cc_aif_if1_ebit_clk", + "audio_core_cc_aif_if1_ibit_clk", + "audio_core_cc_aif_if2_ebit_clk", + "audio_core_cc_aif_if2_ibit_clk", + "audio_core_cc_aif_if3_ebit_clk", + "audio_core_cc_aif_if3_ibit_clk", + "audio_core_cc_ext_mclka_clk", + "audio_core_cc_ext_mclkb_clk"; + interrupts = ; + interrupt-names = "qaif-irq-audio-core"; + #sound-dai-cells = <1>; + }; + +... From f0459c8ad0e73ea7d90ca06c0aeb3888e705357a Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Fri, 29 May 2026 11:43:15 +0530 Subject: [PATCH 2/5] ASoC: qcom: add QAIF header files Add the QAIF header files for shikra SOC. This includes the register definitions for the shikra QAIF, and the structure definition for the driver data. Signed-off-by: Mohammad Rafi Shaik --- sound/soc/qcom/qaif-reg.h | 689 ++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/qaif.h | 576 +++++++++++++++++++++++++++++++ 2 files changed, 1265 insertions(+) create mode 100644 sound/soc/qcom/qaif-reg.h create mode 100644 sound/soc/qcom/qaif.h diff --git a/sound/soc/qcom/qaif-reg.h b/sound/soc/qcom/qaif-reg.h new file mode 100644 index 0000000000000..cccee76a74861 --- /dev/null +++ b/sound/soc/qcom/qaif-reg.h @@ -0,0 +1,689 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * qaif-reg.h -- ALSA SoC CPU-Platform DAI driver register header file for QTi QAIF + */ +#ifndef __QAIF_REG_H__ +#define __QAIF_REG_H__ + +#include "qaif.h" + +#define QAIF_REG_ADDR(offset) (offset) +/* Base for AUDIO_CORE_QAIF_HW_VERSION 0xA000000 */ + +#define QAIF_SUMMARY_IRQSTAT_REG(v) \ + (0x19188 + (0x1000 * ((v)->ee))) /* 0xA019188 */ +/* + * #define QAIF_RDDMA_UNDERFLOW_REG(v) + * QAIF_REG_ADDR(0x19188 + (0x1000 * (ee))) + */ + +/* Shikra core registers */ + +/* Core HW info */ +#define QAIF_HW_VERSION_REG() (0x0000) /* 0xA000000 */ +#define QAIF_HW_INFO_REG (0x0004) /* 0xA000004 */ +#define QAIF_HW_INFO2_REG (0x0008) /* 0xA000008 */ + +/* Interface lane and channel info */ +#define QAIF_AUD_INTF_LANE_INFO_REG (0x0020) /* 0xA000020 */ +#define QAIF_AUD_INTF_LANE_INFO2_REG (0x0024) /* 0xA000024 */ +/* 0xA000028 .. 0xA000044 (n:0..7) */ +#define QAIF_CODEC_TX_INTF_CH_INFO_REG(n) (0x0028 + (0x4 * (n))) +/* 0xA000068 .. 0xA000084 (n:0..7) */ +#define QAIF_CODEC_RX_INTF_CH_INFO_REG(n) (0x0068 + (0x4 * (n))) +#define QAIF_QXM1_SHRAM_LENGTH_INFO_REG (0x0088) /* 0xA000088 */ +#define QAIF_QXM0_SHRAM_LENGTH_INFO_REG (0x008C) /* 0xA00008C */ +#define QAIF_NUM_AUD_INTF_TO_RAIL_INFO_REG (0x0090) /* 0xA000090 */ + +/* Debug/control and status */ +#define QAIF_DEBUG_CTL_REG (0x0200) /* 0xA000200 */ +#define QAIF_WRDMA_LOOPBACK_EN_REG (0x0204) /* 0xA000204 */ +#define QAIF_WRDMA_LOOPBACK_SEL_REG (0x0208) /* 0xA000208 */ +#define QAIF_SHRAM_DYNAMIC_CLK_GATING_EN_REG (0x0300) /* 0xA000300 */ +#define QAIF_AXI_STATUS_REG (0x0304) /* 0xA000304 */ +#define QAIF_QSB_DYNAMIC_CLK_GATING_EN_REG (0x0308) /* 0xA000308 */ +#define QAIF_START_STOP_CTRL_BYPASS_EN_REG (0x030C) /* 0xA00030C */ +#define QAIF_QXM0_AXI_ATTR_CFG_REG (0x040C) /* 0xA00040C */ + +/* QXM request/grant debug */ +#define QAIF_QXM0_AUD_WR_REQ_GNT_DBG_STAT_REG (0x0500) /* 0xA000500 */ +#define QAIF_QXM1_AUD_WR_REQ_GNT_DBG_STAT_REG (0x0504) /* 0xA000504 */ +#define QAIF_QXM0_CODEC_RX_WR_REQ_DBG_STAT_REG (0x0508) /* 0xA000508 */ +#define QAIF_QXM0_CODEC_RX_WR_GNT_DBG_STAT_REG (0x050C) /* 0xA00050C */ +#define QAIF_QXM1_CODEC_RX_WR_REQ_DBG_STAT_REG (0x0510) /* 0xA000510 */ +#define QAIF_QXM1_CODEC_RX_WR_GNT_DBG_STAT_REG (0x0514) /* 0xA000514 */ +#define QAIF_QXM0_AUD_RD_REQ_GNT_DBG_STAT_REG (0x0518) /* 0xA000518 */ +#define QAIF_QXM1_AUD_RD_REQ_GNT_DBG_STAT_REG (0x051C) /* 0xA00051C */ +#define QAIF_QXM0_CODEC_TX_RD_REQ_DBG_STAT_REG (0x0520) /* 0xA000520 */ +#define QAIF_QXM0_CODEC_TX_RD_GNT_DBG_STAT_REG (0x0524) /* 0xA000524 */ +#define QAIF_QXM1_CODEC_TX_RD_REQ_DBG_STAT_REG (0x0528) /* 0xA000528 */ +#define QAIF_QXM1_CODEC_TX_RD_GNT_DBG_STAT_REG (0x052C) /* 0xA00052C */ +#define QAIF_QXM0_EXT_RDDMA_RD_REQ_GNT_DBG_STAT_REG (0x0530) /* 0xA000530 */ +#define QAIF_QXM1_EXT_RDDMA_RD_REQ_GNT_DBG_STAT_REG (0x0534) /* 0xA000534 */ + +/* QSB transaction debug */ +#define QAIF_QSB_AUD_WR_TXN_DBG_STAT_REG (0x0538) /* 0xA000538 */ +#define QAIF_QSB_CODEC_RX_WR_TXN_ERR_DBG_STAT_REG (0x053C) /* 0xA00053C */ +#define QAIF_QSB_CODEC_RX_WR_TXN_OKAY_DBG_STAT_REG (0x0540) /* 0xA000540 */ +#define QAIF_QSB_AUD_ADDR_SENT_DBG_STAT_REG (0x0544) /* 0xA000544 */ +#define QAIF_QSB_CODEC_TX_RD_ADDR_SENT_DBG_STAT_REG (0x0548) /* 0xA000548 */ +#define QAIF_QSB_EXT_RDDMA_RD_ADDR_SENT_DBG_STAT_REG (0x054C) /* 0xA00054C */ +#define QAIF_QSB_CODEC_RX_WR_ADDR_SENT_DBG_STAT_REG (0x0550) /* 0xA000550 */ +#define QAIF_QSB_AUD_RD_TXN_DBG_STAT_REG (0x0554) /* 0xA000554 */ +#define QAIF_QSB_CODEC_TX_RD_TXN_ERR_DBG_STAT_REG (0x0558) /* 0xA000558 */ +#define QAIF_QSB_CODEC_TX_RD_TXN_RCVD_DBG_STAT_REG (0x055C) /* 0xA00055C */ +#define QAIF_QSB_EXT_RDDMA_RD_TXN_DBG_STAT_REG (0x0560) /* 0xA000560 */ +#define QAIF_QSB_MISC_DBG_STATUS_REG (0x0564) /* 0xA000564 */ + +/* Global spare and HWE */ +#define QAIF_GLOBAL_SPARE_IN_REG (0x0B00) /* 0xA000B00 */ +#define QAIF_GLOBAL_SPARE_OUT_REG (0x0B04) /* 0xA000B04 */ +#define QAIF_HWE_CFG_REG (0x0B08) /* 0xA000B08 */ + +/* SID maps */ +#define QAIF_WRDMA_SID_MAP_REG (0x1B00) /* 0xA001B00 */ +#define QAIF_CODEC_WRDMA_SID_MAP_REG (0x1B40) /* 0xA001B40 */ +#define QAIF_RDDMA_SID_MAP_REG (0x1C00) /* 0xA001C00 */ +#define QAIF_CODEC_RDDMA_SID_MAP_REG (0x1C40) /* 0xA001C40 */ + +/* EE overlap interrupts */ +#define QAIF_EE_OVERLAP_IRQ_EN_REG (0x1D00) /* 0xA001D00 */ +#define QAIF_EE_OVERLAP_IRQ_RAW_STATUS_REG (0x1D04) /* 0xA001D04 */ +#define QAIF_EE_OVERLAP_IRQ_CLEAR_REG (0x1D08) /* 0xA001D08 */ +#define QAIF_EE_OVERLAP_IRQ_FORCE_REG (0x1D0C) /* 0xA001D0C */ + +/* EE assignments and maps */ +/* 0xA019148 */ +#define QAIF_EE_RDDMA_ASSIGNMENT_REG(v) (0x19148 + (0x1000 * ((v)->ee))) +/* 0xA019150 */ +#define QAIF_EE_WRDMA_ASSIGNMENT_REG(v) (0x19150 + (0x1000 * ((v)->ee))) +/* 0xA019158 */ +#define QAIF_EE_INTF_ASSIGNMENT_REG(v) (0x19158 + (0x1000 * ((v)->ee))) +/* 0xA019308 */ +#define QAIF_EE_CODEC_RDDMA_ASSIGNMENT_REG(v) (0x19308 + (0x1000 * ((v)->ee))) +/* 0xA019318 */ +#define QAIF_EE_CODEC_WRDMA_ASSIGNMENT_REG(v) (0x19318 + (0x1000 * ((v)->ee))) +/* 0xA001920 */ +#define QAIF_EE_RDDMA_MAP_REG(v) (0x1920 + (0x1000 * ((v)->ee))) +/* 0xA001940 */ +#define QAIF_EE_WRDMA_MAP_REG(v) (0x1940 + (0x1000 * ((v)->ee))) +/* 0xA001960 */ +#define QAIF_EE_INTF_MAP_REG(v) (0x1960 + (0x1000 * ((v)->ee))) +/* 0xA001980 */ +#define QAIF_EE_CODEC_RDDMA_MAP_REG(v) (0x1980 + (0x1000 * ((v)->ee))) +/* 0xA001A00 */ +#define QAIF_EE_CODEC_WRDMA_MAP_REG(v) (0x1A00 + (0x1000 * ((v)->ee))) + +/* EE rate-detection and VFR interrupts */ +/* 0xA0190F0 */ +#define QAIF_EE_RATE_DET_IRQ_EN_REG(v) (0x190F0 + (0x1000 * ((v)->ee))) +/* 0xA0190F4 */ +#define QAIF_EE_RATE_DET_IRQ_STATUS_REG(v) (0x190F4 + (0x1000 * ((v)->ee))) +/* 0xA0190F8 */ +#define QAIF_EE_RATE_DET_IRQ_RAW_STATUS_REG(v) (0x190F8 + (0x1000 * ((v)->ee))) +/* 0xA0190FC */ +#define QAIF_EE_RATE_DET_IRQ_CLEAR_REG(v) (0x190FC + (0x1000 * ((v)->ee))) +/* 0xA019100 */ +#define QAIF_EE_RATE_DET_IRQ_FORCE_REG(v) (0x19100 + (0x1000 * ((v)->ee))) + +/* 0xA019104 */ +#define QAIF_EE_VFR_IRQ_EN_REG(v) (0x19104 + (0x1000 * ((v)->ee))) +/* 0xA019108 */ +#define QAIF_EE_VFR_IRQ_STATUS_REG(v) (0x19108 + (0x1000 * ((v)->ee))) +/* 0xA01910C */ +#define QAIF_EE_VFR_IRQ_RAW_STATUS_REG(v) (0x1910C + (0x1000 * ((v)->ee))) +/* 0xA019110 */ +#define QAIF_EE_VFR_IRQ_CLEAR_REG(v) (0x19110 + (0x1000 * ((v)->ee))) +/* 0xA019114 */ +#define QAIF_EE_VFR_IRQ_FORCE_REG(v) (0x19114 + (0x1000 * ((v)->ee))) + +/* EE AUD_INTF underflow/overflow interrupts */ +/* 0xA019160 */ +#define QAIF_EE_AUD_INTF_UNDERFLOW_IRQ_EN_REG(v) \ + (0x19160 + (0x1000 * ((v)->ee))) +/* 0xA019164 */ +#define QAIF_EE_AUD_INTF_UNDERFLOW_IRQ_STATUS_REG(v) \ + (0x19164 + (0x1000 * ((v)->ee))) +/* 0xA019168 */ +#define QAIF_EE_AUD_INTF_UNDERFLOW_IRQ_RAW_STATUS_REG(v) \ + (0x19168 + (0x1000 * ((v)->ee))) +/* 0xA01916C */ +#define QAIF_EE_AUD_INTF_UNDERFLOW_IRQ_CLEAR_REG(v) \ + (0x1916C + (0x1000 * ((v)->ee))) +/* 0xA019170 */ +#define QAIF_EE_AUD_INTF_UNDERFLOW_IRQ_FORCE_REG(v) \ + (0x19170 + (0x1000 * ((v)->ee))) + +/* 0xA019174 */ +#define QAIF_EE_AUD_INTF_OVERFLOW_IRQ_EN_REG(v) \ + (0x19174 + (0x1000 * ((v)->ee))) +/* 0xA019178 */ +#define QAIF_EE_AUD_INTF_OVERFLOW_IRQ_STATUS_REG(v) \ + (0x19178 + (0x1000 * ((v)->ee))) +/* 0xA01917C */ +#define QAIF_EE_AUD_INTF_OVERFLOW_IRQ_RAW_STATUS_REG(v) \ + (0x1917C + (0x1000 * ((v)->ee))) +/* 0xA019180 */ +#define QAIF_EE_AUD_INTF_OVERFLOW_IRQ_CLEAR_REG(v) \ + (0x19180 + (0x1000 * ((v)->ee))) +/* 0xA019184 */ +#define QAIF_EE_AUD_INTF_OVERFLOW_IRQ_FORCE_REG(v) \ + (0x19184 + (0x1000 * ((v)->ee))) + +/* EE L2 Period IRQ mux selection */ +/* 0xA019F00 */ +#define QAIF_EE_L2_PERIOD_IRQ_0_3_MUX_SEL_REG(v) \ + (0x19F00 + (0x1000 * ((v)->ee))) +/* 0xA019F04 */ +#define QAIF_EE_L2_PERIOD_IRQ_4_7_MUX_SEL_REG(v) \ + (0x19F04 + (0x1000 * ((v)->ee))) + +/* AUD_INTF block (per interface, stride 0x1000 starting at 0x4000) */ +#define QAIF_AUD_INTF_REG_ADDR(offset, intf) \ + (0x4000 + (offset) + (0x1000 * (intf))) + +/* 0xA004000 */ +#define QAIF_AUD_INTF_CTL_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0000, (intf)) +/* 0xA004004 */ +#define QAIF_AUD_INTF_SYNC_CFG_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0004, (intf)) +/* 0xA004008 */ +#define QAIF_AUD_INTF_BIT_WIDTH_CFG_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0008, (intf)) +/* 0xA00400C */ +#define QAIF_AUD_INTF_FRAME_CFG_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x000C, (intf)) +/* 0xA004010 */ +#define QAIF_AUD_INTF_ACTV_SLOT_EN_TX_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0010, (intf)) +/* 0xA004030 */ +#define QAIF_AUD_INTF_ACTV_SLOT_EN_RX_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0030, (intf)) +/* 0xA004050 */ +#define QAIF_AUD_INTF_LANE_CFG_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0050, (intf)) +/* 0xA004054 */ +#define QAIF_AUD_INTF_MI2S_CFG_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0054, (intf)) +/* 0xA004058 */ +#define QAIF_AUD_INTF_CFG_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0058, (intf)) +/* 0xA00405C */ +#define QAIF_AUD_INTF_CHAR_CTL_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x005C, (intf)) +/* 0xA004060 */ +#define QAIF_AUD_INTF_CHAR_CFG_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0060, (intf)) +/* 0xA004064 */ +#define QAIF_AUD_INTF_CHAR_DATA_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0064, (intf)) +/* 0xA004068 */ +#define QAIF_AUD_INTF_CHAR_DATA_EXT_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0068, (intf)) +/* 0xA00406C */ +#define QAIF_AUD_INTF_CHAR_SYNC_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x006C, (intf)) +/* 0xA004FF0 */ +#define QAIF_AUD_INTF_INIT_DBG_STATUS_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0FF0, (intf)) +/* 0xA004FF4 */ +#define QAIF_AUD_INTF_TX_DBG_STATUS_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0FF4, (intf)) +/* 0xA004FF8 */ +#define QAIF_AUD_INTF_RX_DBG_STATUS_REG(intf) \ + QAIF_AUD_INTF_REG_ADDR(0x0FF8, (intf)) + +/* RATE_DET block (per detector, stride 0x1000 starting at 0x1E000) */ +#define QAIF_RATE_DET_REG_ADDR(offset, det) \ + (0x1E000 + (offset) + (0x1000 * (det))) + +/* 0xA01E000 */ +#define QAIF_RATE_DET_CONFIG_REG(det) \ + QAIF_RATE_DET_REG_ADDR(0x0000, (det)) +/* 0xA01E004 */ +#define QAIF_RATE_DET_TARGET1_CONFIG_REG(det) \ + QAIF_RATE_DET_REG_ADDR(0x0004, (det)) +/* 0xA01E008 */ +#define QAIF_RATE_DET_TARGET2_CONFIG_REG(det) \ + QAIF_RATE_DET_REG_ADDR(0x0008, (det)) +/* 0xA01E00C */ +#define QAIF_RATE_DET_BIN_REG(det) \ + QAIF_RATE_DET_REG_ADDR(0x000C, (det)) +/* 0xA01E010 */ +#define QAIF_RATE_DET_STC_DIFF_REG(det) \ + QAIF_RATE_DET_REG_ADDR(0x0010, (det)) +/* 0xA01E014 */ +#define QAIF_RATE_DET_SEL_REG(det) \ + QAIF_RATE_DET_REG_ADDR(0x0014, (det)) +/* 0xA01E018 */ +#define QAIF_RATE_DET_TIMEOUT_CFG_REG(det) \ + QAIF_RATE_DET_REG_ADDR(0x0018, (det)) + +#define QAIF_WRDMA_MAP_QXM (0x1000) +#define QAIF_CODEC_WRDMA_MAP_QXM (0x1004) +#define QAIF_RDDMA_MAP_QXM (0x1010) +#define QAIF_CODEC_RDDMA_MAP_QXM (0x1014) +#define QAIF_RDDMA_QXM1_SHRAM_ST_ADDR(i) (0x1100 + (0x4 * (i))) +#define QAIF_CODEC_RDDMA_QXM1_SHRAM_ST_ADDR(i) (0x1140 + (0x4 * (i))) +#define QAIF_RDDMA_QXM0_SHRAM_ST_ADDR(i) (0x1200 + (0x4 * (i))) +#define QAIF_CODEC_RDDMA_QXM0_SHRAM_ST_ADDR(i) (0x1240 + (0x4 * (i))) +#define QAIF_RDDMA_QXM1_SHRAM_LEN(i) (0x1300 + (0x4 * (i))) +#define QAIF_CODEC_RDDMA_QXM1_SHRAM_LEN(i) (0x1340 + (0x4 * (i))) +#define QAIF_RDDMA_QXM0_SHRAM_LEN(i) (0x1400 + (0x4 * (i))) +#define QAIF_CODEC_RDDMA_QXM0_SHRAM_LEN(i) (0x1440 + (0x4 * (i))) +#define QAIF_WRDMA_QXM1_SHRAM_ST_ADDR(i) (0x1500 + (0x4 * (i))) +#define QAIF_CODEC_WRDMA_QXM1_SHRAM_ST_ADDR(i) (0x1540 + (0x4 * (i))) +#define QAIF_WRDMA_QXM0_SHRAM_ST_ADDR(i) (0x1600 + (0x4 * (i))) +#define QAIF_CODEC_WRDMA_QXM0_SHRAM_ST_ADDR(i) (0x1640 + (0x4 * (i))) +#define QAIF_WRDMA_QXM1_SHRAM_LEN(i) (0x1700 + (0x4 * (i))) +#define QAIF_CODEC_WRDMA_QXM1_SHRAM_LEN(i) (0x1740 + (0x4 * (i))) +#define QAIF_WRDMA_QXM0_SHRAM_LEN(i) (0x1800 + (0x4 * (i))) +#define QAIF_CODEC_WRDMA_QXM0_SHRAM_LEN(i) (0x1840 + (0x4 * (i))) + +/* + * RDDMA + * v : ptr to qaif_variant + */ +static inline u32 QAIF_RDDMA_REG_ADDR(const struct qaif_variant *v, u32 offset, u32 chan) +{ + return (v)->rddma_reg_base + offset + (v)->rddma_stride * chan; +} + +#define QAIF_RDDMA_CTL_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x00, (chan)) +#define QAIF_RDDMA_CFG_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x04, (chan)) +#define QAIF_RDDMA_BASE_ADDR_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x08, (chan)) +#define QAIF_RDDMA_BUFF_LEN_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x10, (chan)) +#define QAIF_RDDMA_CURR_ADDR_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x14, (chan)) +#define QAIF_RDDMA_PERIOD_LEN_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x1C, (chan)) +#define QAIF_RDDMA_PERIOD_CNT_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x20, (chan)) +#define QAIF_RDDMA_SHRAM_WORDCNT_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x24, (chan)) +#define QAIF_RDDMA_FRAME_STATUS_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x28, (chan)) +#define QAIF_RDDMA_FRAME_STATUS_EXTN_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x2C, (chan)) +#define QAIF_RDDMA_FRAME_STATUS_CLR_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x30, (chan)) +#define QAIF_RDDMA_SET_BUFF_CNT_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x34, (chan)) +#define QAIF_RDDMA_SET_PERIOD_CNT_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x38, (chan)) +#define QAIF_RDDMA_STC_LSB_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x3C, (chan)) +#define QAIF_RDDMA_STC_MSB_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x40, (chan)) +#define QAIF_RDDMA_PERIOD_DET_STAT_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x44, (chan)) +#define QAIF_RDDMA_PERIOD_DET_CLR_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x48, (chan)) +#define QAIF_RDDMA_FORMAT_ERR_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x4C, (chan)) +#define QAIF_RDDMA_AHB_BYPASS_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x50, (chan)) +#define QAIF_RDDMA_SHUTDOWN_STAT_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x54, (chan)) +#define QAIF_RDDMA_PADDING_CFG_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x58, (chan)) +#define QAIF_RDDMA_STATUS_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0x60, (chan)) +#define QAIF_RDDMA_DBG_STATUS_REG(v, chan) \ + QAIF_RDDMA_REG_ADDR(v, 0xFF0, (chan)) + +static inline u32 QAIF_CODEC_RDDMA_REG_ADDR(const struct qaif_variant *v, u32 offset, u32 chan) +{ + return (v)->codec_rddma_reg_base + offset + (v)->codec_rddma_stride * chan; +} + +#define QAIF_CODEC_RDDMA_CTL_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x00, (chan)) +#define QAIF_CODEC_RDDMA_CFG_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x04, (chan)) +#define QAIF_CODEC_RDDMA_BASE_ADDR_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x08, (chan)) +#define QAIF_CODEC_RDDMA_BUFF_LEN_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x10, (chan)) +#define QAIF_CODEC_RDDMA_CURR_ADDR_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x14, (chan)) +#define QAIF_CODEC_RDDMA_PERIOD_LEN_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x1C, (chan)) +#define QAIF_CODEC_RDDMA_PERIOD_CNT_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x20, (chan)) +#define QAIF_CODEC_RDDMA_SHRAM_WORDCNT_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x24, (chan)) +#define QAIF_CODEC_RDDMA_FRAME_STATUS_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x28, (chan)) +#define QAIF_CODEC_RDDMA_FRAME_STATUS_EXTN_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x2C, (chan)) +#define QAIF_CODEC_RDDMA_FRAME_STATUS_CLR_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x30, (chan)) +#define QAIF_CODEC_RDDMA_SET_BUFF_CNT_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x34, (chan)) +#define QAIF_CODEC_RDDMA_SET_PERIOD_CNT_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x38, (chan)) +#define QAIF_CODEC_RDDMA_STC_LSB_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x3C, (chan)) +#define QAIF_CODEC_RDDMA_STC_MSB_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x40, (chan)) +#define QAIF_CODEC_RDDMA_PERIOD_DET_STAT_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x44, (chan)) +#define QAIF_CODEC_RDDMA_PERIOD_DET_CLR_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x48, (chan)) +#define QAIF_CODEC_RDDMA_FORMAT_ERR_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x4C, (chan)) +#define QAIF_CODEC_RDDMA_AHB_BYPASS_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x50, (chan)) +#define QAIF_CODEC_RDDMA_SHUTDOWN_STAT_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x54, (chan)) +#define QAIF_CODEC_RDDMA_PADDING_CFG_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x58, (chan)) +#define QAIF_CODEC_RDDMA_INTF_CFG_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x5C, (chan)) +#define QAIF_CODEC_RDDMA_STATUS_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0x60, (chan)) +#define QAIF_CODEC_RDDMA_DBG_STATUS_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0xFF0, (chan)) +#define QAIF_CODEC_RDDMA_INTF_DBG_STATUS_REG(v, chan) \ + QAIF_CODEC_RDDMA_REG_ADDR(v, 0xFF4, (chan)) + +/* + * WRDMA + * v : ptr to qaif_variant + */ +static inline u32 QAIF_WRDMA_REG_ADDR(const struct qaif_variant *v, u32 offset, u32 chan) +{ + return (v)->wrdma_reg_base + offset + (v)->wrdma_stride * chan; +} + +#define QAIF_WRDMA_CTL_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x00, (chan)) +#define QAIF_WRDMA_CFG_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x04, (chan)) +#define QAIF_WRDMA_BASE_ADDR_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x08, (chan)) +#define QAIF_WRDMA_BUFF_LEN_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x10, (chan)) +#define QAIF_WRDMA_CURR_ADDR_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x14, (chan)) +#define QAIF_WRDMA_PERIOD_LEN_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x1C, (chan)) +#define QAIF_WRDMA_PERIOD_CNT_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x20, (chan)) +#define QAIF_WRDMA_SHRAM_WORDCNT_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x24, (chan)) +#define QAIF_WRDMA_FRAME_STATUS_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x28, (chan)) +#define QAIF_WRDMA_FRAME_STATUS_EXTN_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x2C, (chan)) +#define QAIF_WRDMA_FRAME_STATUS_CLR_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x30, (chan)) +#define QAIF_WRDMA_SET_BUFF_CNT_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x34, (chan)) +#define QAIF_WRDMA_SET_PERIOD_CNT_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x38, (chan)) +#define QAIF_WRDMA_STC_LSB_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x3C, (chan)) +#define QAIF_WRDMA_STC_MSB_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x40, (chan)) +#define QAIF_WRDMA_PERIOD_DET_STAT_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x44, (chan)) +#define QAIF_WRDMA_PERIOD_DET_CLR_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x48, (chan)) +#define QAIF_WRDMA_FORMAT_ERR_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x4C, (chan)) +#define QAIF_WRDMA_AHB_BYPASS_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x50, (chan)) +#define QAIF_WRDMA_SHUTDOWN_STAT_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0x54, (chan)) +#define QAIF_WRDMA_DBG_STATUS_REG(v, chan) \ + QAIF_WRDMA_REG_ADDR(v, 0xFF0, (chan)) + +static inline u32 QAIF_CODEC_WRDMA_REG_ADDR(const struct qaif_variant *v, u32 offset, u32 chan) +{ + return (v)->codec_wrdma_reg_base + offset + (v)->codec_wrdma_stride * chan; +} + +#define QAIF_CODEC_WRDMA_CTL_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x00, (chan)) +#define QAIF_CODEC_WRDMA_CFG_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x04, (chan)) +#define QAIF_CODEC_WRDMA_BASE_ADDR_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x08, (chan)) +#define QAIF_CODEC_WRDMA_BUFF_LEN_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x10, (chan)) +#define QAIF_CODEC_WRDMA_CURR_ADDR_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x14, (chan)) +#define QAIF_CODEC_WRDMA_PERIOD_LEN_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x1C, (chan)) +#define QAIF_CODEC_WRDMA_PERIOD_CNT_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x20, (chan)) +#define QAIF_CODEC_WRDMA_SHRAM_WORDCNT_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x24, (chan)) +#define QAIF_CODEC_WRDMA_FRAME_STATUS_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x28, (chan)) +#define QAIF_CODEC_WRDMA_FRAME_STATUS_EXTN_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x2C, (chan)) +#define QAIF_CODEC_WRDMA_FRAME_STATUS_CLR_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x30, (chan)) +#define QAIF_CODEC_WRDMA_SET_BUFF_CNT_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x34, (chan)) +#define QAIF_CODEC_WRDMA_SET_PERIOD_CNT_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x38, (chan)) +#define QAIF_CODEC_WRDMA_STC_LSB_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x3C, (chan)) +#define QAIF_CODEC_WRDMA_STC_MSB_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x40, (chan)) +#define QAIF_CODEC_WRDMA_PERIOD_DET_STAT_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x44, (chan)) +#define QAIF_CODEC_WRDMA_PERIOD_DET_CLR_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x48, (chan)) +#define QAIF_CODEC_WRDMA_FORMAT_ERR_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x4C, (chan)) +#define QAIF_CODEC_WRDMA_AHB_BYPASS_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x50, (chan)) +#define QAIF_CODEC_WRDMA_SHUTDOWN_STAT_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x54, (chan)) +#define QAIF_CODEC_WRDMA_INTF_CFG_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0x58, (chan)) +#define QAIF_CODEC_WRDMA_DBG_STATUS_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0xFF0, (chan)) +#define QAIF_CODEC_WRDMA_INTF_DBG_STATUS_REG(v, chan) \ + QAIF_CODEC_WRDMA_REG_ADDR(v, 0xFF4, (chan)) + +static inline u32 QAIF_EE_RDDMA_IRQ_REG_ADDR(const struct qaif_variant *v, + enum qaif_irq_type_t dma_type, + u32 offset) +{ + if (dma_type == QAIF_AIF_IRQ) + return (v)->rddma_irq_reg_base + offset + + (v)->rddma_irq_stride * (v)->ee; + return (v)->codec_rddma_irq_reg_base + offset + + (v)->codec_rddma_irq_stride * (v)->ee; +} + +/* RDDMA Period Interrupts */ +#define QAIF_EE_RDDMA_PERIOD_IRQ_EN_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x00) +#define QAIF_EE_RDDMA_PERIOD_IRQ_STAT_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x08) +#define QAIF_EE_RDDMA_PERIOD_IRQ_RAW_STAT_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x10) +#define QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x18) +#define QAIF_EE_RDDMA_PERIOD_IRQ_FORCE_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x20) +/* RDDMA Underflow Interrupts */ +#define QAIF_EE_RDDMA_UNDERFLOW_IRQ_EN_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x28) +#define QAIF_EE_RDDMA_UNDERFLOW_IRQ_STAT_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x30) +#define QAIF_EE_RDDMA_UNDERFLOW_IRQ_RAW_STAT_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x38) +#define QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x40) +#define QAIF_EE_RDDMA_UNDERFLOW_IRQ_FORCE_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x48) +/* RDDMA Error Response Interrupts */ +#define QAIF_EE_RDDMA_ERR_RSP_IRQ_EN_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x50) +#define QAIF_EE_RDDMA_ERR_RSP_IRQ_STAT_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x58) +#define QAIF_EE_RDDMA_ERR_RSP_IRQ_RAW_STAT_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x60) +#define QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x68) +#define QAIF_EE_RDDMA_ERR_RSP_IRQ_FORCE_REG(v, i) \ + QAIF_EE_RDDMA_IRQ_REG_ADDR(v, i, 0x70) + +static inline u32 QAIF_EE_WRDMA_IRQ_REG_ADDR(const struct qaif_variant *v, + enum qaif_irq_type_t dma_type, + u32 offset) +{ + if (dma_type == QAIF_AIF_IRQ) + return (v)->wrdma_irq_reg_base + offset + + (v)->wrdma_irq_stride * (v)->ee; + return (v)->codec_wrdma_irq_reg_base + offset + + (v)->codec_wrdma_irq_stride * (v)->ee; +} + +/* WRDMA Period Interrupts */ +#define QAIF_EE_WRDMA_PERIOD_IRQ_EN_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x00) +#define QAIF_EE_WRDMA_PERIOD_IRQ_STAT_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x08) +#define QAIF_EE_WRDMA_PERIOD_IRQ_RAW_STAT_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x10) +#define QAIF_EE_WRDMA_PERIOD_IRQ_CLR_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x18) +#define QAIF_EE_WRDMA_PERIOD_IRQ_FORCE_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x20) +/* WRDMA Overflow Interrupts */ +#define QAIF_EE_WRDMA_OVERFLOW_IRQ_EN_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x28) +#define QAIF_EE_WRDMA_OVERFLOW_IRQ_STAT_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x30) +#define QAIF_EE_WRDMA_OVERFLOW_IRQ_RAW_STAT_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x38) +#define QAIF_EE_WRDMA_OVERFLOW_IRQ_CLR_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x40) +#define QAIF_EE_WRDMA_OVERFLOW_IRQ_FORCE_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x48) +/* WRDMA Error Response Interrupts */ +#define QAIF_EE_WRDMA_ERR_RSP_IRQ_EN_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x50) +#define QAIF_EE_WRDMA_ERR_RSP_IRQ_STAT_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x58) +#define QAIF_EE_WRDMA_ERR_RSP_IRQ_RAW_STAT_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x60) +#define QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x68) +#define QAIF_EE_WRDMA_ERR_RSP_IRQ_FORCE_REG(v, i) \ + QAIF_EE_WRDMA_IRQ_REG_ADDR(v, i, 0x70) + +static inline u32 QAIF_DMACFG_REG(const struct qaif_variant *v, u32 chan, int dir, int dai_id) +{ + if (is_cif_dma_port(dai_id)) { + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_CODEC_RDDMA_CFG_REG(v, chan); + return QAIF_CODEC_WRDMA_CFG_REG(v, chan); + } + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_RDDMA_CFG_REG(v, chan); + return QAIF_WRDMA_CFG_REG(v, chan); +} + +static inline u32 QAIF_DMACTL_REG(const struct qaif_variant *v, u32 chan, int dir, int dai_id) +{ + if (is_cif_dma_port(dai_id)) { + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_CODEC_RDDMA_CTL_REG(v, chan); + return QAIF_CODEC_WRDMA_CTL_REG(v, chan); + } + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_RDDMA_CTL_REG(v, chan); + return QAIF_WRDMA_CTL_REG(v, chan); +} + +static inline u32 QAIF_DMABUFF_REG(const struct qaif_variant *v, u32 chan, int dir, int dai_id) +{ + if (is_cif_dma_port(dai_id)) { + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_CODEC_RDDMA_BUFF_LEN_REG(v, chan); + return QAIF_CODEC_WRDMA_BUFF_LEN_REG(v, chan); + } + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_RDDMA_BUFF_LEN_REG(v, chan); + return QAIF_WRDMA_BUFF_LEN_REG(v, chan); +} + +static inline u32 QAIF_DMACURR_REG(const struct qaif_variant *v, u32 chan, int dir, int dai_id) +{ + if (is_cif_dma_port(dai_id)) { + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_CODEC_RDDMA_CURR_ADDR_REG(v, chan); + return QAIF_CODEC_WRDMA_CURR_ADDR_REG(v, chan); + } + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_RDDMA_CURR_ADDR_REG(v, chan); + return QAIF_WRDMA_CURR_ADDR_REG(v, chan); +} + +static inline u32 QAIF_DMAPER_REG(const struct qaif_variant *v, u32 chan, int dir, int dai_id) +{ + if (is_cif_dma_port(dai_id)) { + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_CODEC_RDDMA_PERIOD_CNT_REG(v, chan); + return QAIF_CODEC_WRDMA_PERIOD_CNT_REG(v, chan); + } + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_RDDMA_PERIOD_CNT_REG(v, chan); + return QAIF_WRDMA_PERIOD_CNT_REG(v, chan); +} + +static inline u32 QAIF_DMAPER_LEN_REG(const struct qaif_variant *v, u32 chan, int dir, int dai_id) +{ + if (is_cif_dma_port(dai_id)) { + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_CODEC_RDDMA_PERIOD_LEN_REG(v, chan); + return QAIF_CODEC_WRDMA_PERIOD_LEN_REG(v, chan); + } + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_RDDMA_PERIOD_LEN_REG(v, chan); + return QAIF_WRDMA_PERIOD_LEN_REG(v, chan); +} + +static inline u32 QAIF_DMABASE_REG(const struct qaif_variant *v, u32 chan, int dir, int dai_id) +{ + if (is_cif_dma_port(dai_id)) { + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_CODEC_RDDMA_BASE_ADDR_REG(v, chan); + return QAIF_CODEC_WRDMA_BASE_ADDR_REG(v, chan); + } + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_RDDMA_BASE_ADDR_REG(v, chan); + return QAIF_WRDMA_BASE_ADDR_REG(v, chan); +} + +static inline u32 QAIF_SID_MAP_REG(int dir, int dai_id) +{ + if (is_cif_dma_port(dai_id)) { + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_CODEC_RDDMA_SID_MAP_REG; + return QAIF_CODEC_WRDMA_SID_MAP_REG; + } + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + return QAIF_RDDMA_SID_MAP_REG; + return QAIF_WRDMA_SID_MAP_REG; +} + +#endif /* __QAIF_REG_H__ */ diff --git a/sound/soc/qcom/qaif.h b/sound/soc/qcom/qaif.h new file mode 100644 index 0000000000000..f52c92289813f --- /dev/null +++ b/sound/soc/qcom/qaif.h @@ -0,0 +1,576 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2010-2011,2013-2015,2020 The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * qaif.h -- ALSA SoC CPU-Platform DAI driver header file for QTi QAIF + */ +#ifndef __QAIF_H__ +#define __QAIF_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MI2S_SEPTENARY 35 + +#define SMMU_SID_OFFSET 32 +#define LPASS_MAX_MI2S_PORTS (8) +#define LPASS_MAX_AIF_DMA_IDX (8) +#define LPASS_MAX_CIF_DMA_IDX (8) +#define QAIF_CIF_DMA_INTF_ONE_CHANNEL (0x01) +#define QAIF_CIF_DMA_INTF_TWO_CHANNEL (0x03) +#define QAIF_CIF_DMA_INTF_FOUR_CHANNEL (0x0F) +#define QAIF_CIF_DMA_INTF_SIX_CHANNEL (0x3F) +#define QAIF_CIF_DMA_INTF_EIGHT_CHANNEL (0xFF) + +#define QAIF_DMACTL_ENABLE_ON 1 +#define QAIF_DMACTL_ENABLE_OFF 0 + +#define QAIF_DMACTL_DYNCLK_ON 1 +#define QAIF_DMACTL_DYNCLK_OFF 0 + +#define QAIF_MAX_AIF_CFG_CNT (LPASS_MAX_AIF_DMA_IDX / 2) + +/* TODO: confirm if only dma0...3 are active. */ +#define QAIF_IRQ_DMA_ALL (0xf) + +/* Individual bit masks (hex) */ +#define QAIF_AUD_INTF_CTL_ENABLE 0x00000001 /* bit 0 ENABLE RX and TX*/ +#define QAIF_AUD_INTF_CTL_ENABLE_TX 0x00000010 /* bit 4 */ +#define QAIF_AUD_INTF_CTL_ENABLE_RX 0x00000100 /* bit 8 */ +#define QAIF_AUD_INTF_CTL_RESET 0x00001000 /* bit 12 RESET RX and TX*/ +#define QAIF_AUD_INTF_CTL_RESET_TX 0x00010000 /* bit 16 */ +#define QAIF_AUD_INTF_CTL_RESET_RX 0x00100000 /* bit 20 */ + +/* Combined masks */ +#define QAIF_AUD_INTF_CTL_ENABLE_ALL 0x00000110 /* bits 4,8 */ +#define QAIF_AUD_INTF_CTL_RESET_ALL 0x00110000 /* bits 16,20 */ + +#define QAIF_AUD_INTF_CTL_MONO 1 /* Mono Mode True */ +#define QAIF_AUD_INTF_CTL_STEREO 0 /* Mono Mode False */ + +#define QAIF_AIF_SAMPLE_WIDTH(bits) ((bits) - 1) +#define QAIF_AIF_SLOT_WIDTH(bits) ((bits) - 1) + +#define QAIF_DMA_CLK_RATE_HZ 153600000 + +#define QAIF_DMACTL_WM_5 4 +#define QAIF_DMACTL_WM_8 7 +#define QAIF_DMACTL_BURSTEN 1 + +#define QAIF_MAX_LANES 8 + +/* QAIF_AUD_INTF_SYNC_CFG_REG bit masks and shifts */ +#define QAIF_AUD_INTF_SYNC_CFG_INV_SYNC_MASK BIT(12) +#define QAIF_AUD_INTF_SYNC_CFG_INV_SYNC_SHFT 12 + +#define QAIF_AUD_INTF_SYNC_CFG_SYNC_DELAY_MASK GENMASK(9, 8) +#define QAIF_AUD_INTF_SYNC_CFG_SYNC_DELAY_SHFT 8 + +#define QAIF_AUD_INTF_SYNC_CFG_SYNC_MODE_MASK GENMASK(5, 4) +#define QAIF_AUD_INTF_SYNC_CFG_SYNC_MODE_SHFT 4 + +#define QAIF_AUD_INTF_SYNC_CFG_SYNC_SRC_MASK BIT(0) +#define QAIF_AUD_INTF_SYNC_CFG_SYNC_SRC_SHFT 0 + +/* QAIF_AUD_INTF_LANE_CFG_REG bit masks and shifts */ +#define QAIF_AUD_INTF_LANE_CFG_LOOPBACK_MASK BIT(31) +#define QAIF_AUD_INTF_LANE_CFG_LOOPBACK_SHFT 31 + +#define QAIF_AUD_INTF_LANE_CFG_CTRL_DATA_OE_MASK BIT(16) +#define QAIF_AUD_INTF_LANE_CFG_CTRL_DATA_OE_SHFT 16 + +#define QAIF_AUD_INTF_LANE_CFG_LANE_EN_MASK GENMASK(15, 8) +#define QAIF_AUD_INTF_LANE_CFG_LANE_EN_SHFT 8 + +#define QAIF_AUD_INTF_LANE_CFG_LANE_DIR_MASK GENMASK(7, 0) +#define QAIF_AUD_INTF_LANE_CFG_LANE_DIR_SHFT 0 + +/* ========== QAIF_AUD_INTF_BIT_WIDTH_CFG_REG bit masks and shifts ========== */ +#define QAIF_AUD_INTF_BIT_WIDTH_CFG_SAMPLE_WIDTH_RX_MASK GENMASK(28, 24) +#define QAIF_AUD_INTF_BIT_WIDTH_CFG_SAMPLE_WIDTH_RX_SHFT 24 + +#define QAIF_AUD_INTF_BIT_WIDTH_CFG_SAMPLE_WIDTH_TX_MASK GENMASK(20, 16) +#define QAIF_AUD_INTF_BIT_WIDTH_CFG_SAMPLE_WIDTH_TX_SHFT 16 + +#define QAIF_AUD_INTF_BIT_WIDTH_CFG_SLOT_WIDTH_RX_MASK GENMASK(12, 8) +#define QAIF_AUD_INTF_BIT_WIDTH_CFG_SLOT_WIDTH_RX_SHFT 8 + +#define QAIF_AUD_INTF_BIT_WIDTH_CFG_SLOT_WIDTH_TX_MASK GENMASK(4, 0) +#define QAIF_AUD_INTF_BIT_WIDTH_CFG_SLOT_WIDTH_TX_SHFT 0 + +/* ========== QAIF_AUD_INTF_BIT_WIDTH_CFG_REG - Combined masks for RMW ========== */ +/* RX-only fields mask (for preserving TX fields) */ +#define QAIF_AUD_INTF_BIT_WIDTH_CFG_RX_FIELDS_MASK \ + (QAIF_AUD_INTF_BIT_WIDTH_CFG_SAMPLE_WIDTH_RX_MASK | \ + QAIF_AUD_INTF_BIT_WIDTH_CFG_SLOT_WIDTH_RX_MASK) + +/* TX-only fields mask (for preserving RX fields) */ +#define QAIF_AUD_INTF_BIT_WIDTH_CFG_TX_FIELDS_MASK \ + (QAIF_AUD_INTF_BIT_WIDTH_CFG_SAMPLE_WIDTH_TX_MASK | \ + QAIF_AUD_INTF_BIT_WIDTH_CFG_SLOT_WIDTH_TX_MASK) + +/* ========== QAIF_AUD_INTF_MI2S_CFG_REG bit masks and shifts ========== */ +#define QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_RX_MASK BIT(1) +#define QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_RX_SHFT 1 + +#define QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_TX_MASK BIT(0) +#define QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_TX_SHFT 0 + +/* Combined masks for Read-Modify-Write operations */ +#define QAIF_AUD_INTF_MI2S_CFG_RX_FIELDS_MASK \ + (QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_RX_MASK) + +#define QAIF_AUD_INTF_MI2S_CFG_TX_FIELDS_MASK \ + (QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_TX_MASK) + +enum qxm_sel { + QXM0 = 0, + QXM1 = 1, + MAX_QXM_TYPE, +}; + +/* Enum list to define the interface direction */ +enum aud_dma_util_direction { + AUD_DMA_SINK = 0, + AUD_DMA_SOURCE = 1, +}; + +static inline bool is_cif_dma_port(int dai_id) +{ + switch (dai_id) { + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + return true; + } + return false; +} + +/* Enum list to define the list of HW interfaces DMA Util is used for + * example, Display port can be part of the below list in future + */ +enum qaif_type_t { + QAIF_INVALID = -1, + QAIF = 0, + QAIF_VA, + QAIF_MAX_TYPES +}; + +enum qaif_irq_type_t { + QAIF_AIF_IRQ = 0, + QAIF_CIF_IRQ = 1, + QAIF_AUD_INTF_IRQ = 2, + QAIF_IRQ_MAX = 3 +}; + +enum qaif_dma_type { + QAIF_AIF_DMA = 0, + QAIF_CIF_DMA = 1, + DMA_TYPE_MAX +}; + +struct qaif_dmactl { + //AUDIO_CORE_QAIF_CODEC_xDMAa_CTL + struct regmap_field *enable; + struct regmap_field *reset; + + //AUDIO_CORE_QAIF_CODEC_xDMAa_CFG + struct regmap_field *num_ot; //outstanding transaction + struct regmap_field *dma_dyncclk; + struct regmap_field *burst16; + struct regmap_field *burst8; + struct regmap_field *burst4; + struct regmap_field *burst2; + struct regmap_field *burst1; + struct regmap_field *shram_wm; //SHRAM_WATERMRK + +}; + +struct qaif_cdc_intfctl { + //AUDIO_CORE_QAIF_CODEC_xDMAa_INTF_CFG + struct regmap_field *active_ch_en; + struct regmap_field *fs_sel; + struct regmap_field *fs_delay; + struct regmap_field *fs_out_gate; + struct regmap_field *intf_dyncclk; + struct regmap_field *en_16bit_unpack; +}; + +struct qaif_aud_intfctl { + /* AUDIO_CORE_QAIF_AUD_INTFa_SYNC_CFG */ + struct regmap_field *inv_sync; /* qcom,qaif-aif-invert-sync */ + struct regmap_field *sync_delay; /* qcom,qaif-aif-sync-delay */ + struct regmap_field *sync_mode; /* qcom,qaif-aif-sync-mode */ + struct regmap_field *sync_src; /* qcom,qaif-aif-sync-src */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_BIT_WIDTH_CFG */ + struct regmap_field *slot_width_rx; /* qcom,qaif-aif-slot-width-rx (MIC/RX Path) */ + struct regmap_field *slot_width_tx; /* qcom,qaif-aif-slot-width-tx (SPKR/TX Path) */ + struct regmap_field *sample_width_rx; /* qcom,qaif-aif-sample-width-rx (MIC/RX Path) */ + struct regmap_field *sample_width_tx; /* qcom,qaif-aif-sample-width-tx (SPKR/TX Path) */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_MI2S_CFG */ + struct regmap_field *mono_mode_rx; /* qcom,qaif-aif-mono-mode-rx (SPKR/TX Path) */ + struct regmap_field *mono_mode_tx; /* qcom,qaif-aif-mono-mode-tx (MIC/RX Path) */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_LANE_CFG */ + struct regmap_field *lane_en; /* Lane enable mask (bits 8-15) */ + struct regmap_field *lane_dir; /* Lane direction mask (bits 0-7, 0=TX, 1=RX) */ + struct regmap_field *loopback_en; /* qcom,qaif-aif-loopback-en (bit 31) */ + struct regmap_field *ctrl_data_oe; /* qcom,qaif-aif-ctrl-data-oe (bit 16) */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_ACTV_SLOT_EN_RX */ + struct regmap_field *slot_en_rx_mask; /* qcom,qaif-aif-slot-en-rx-mask (32-bit mask) */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_ACTV_SLOT_EN_TX */ + struct regmap_field *slot_en_tx_mask; /* qcom,qaif-aif-slot-en-tx-mask (32-bit mask) */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_CFG */ + struct regmap_field *full_cycle_en; /* qcom,qaif-aif-full-cycle-en */ + /* AUDIO_CORE_QAIF_AUD_INTFa_FRAME_CFG */ + struct regmap_field *bits_per_lane; /* qcom,qaif-aif-bits-per-lane */ +}; + +/* Lane configuration structure */ +struct qaif_lane_config { + u32 enable; /* 1 = enabled, 0 = disabled */ + u32 direction; /* 0 = TX_SPKR, 1 = RX_MIC */ +}; + +/* QAIF Audio Interface Configuration Structure */ +struct qaif_aif_config { + /* Sync configuration */ + u32 sync_mode; /* qcom,qaif-aif-sync-mode */ + u32 sync_src; /* qcom,qaif-aif-sync-src */ + u32 invert_sync; /* qcom,qaif-aif-invert-sync */ + u32 sync_delay; /* qcom,qaif-aif-sync-delay */ + /* Slot and sample width configuration */ + u32 slot_width_rx; /* qcom,qaif-aif-slot-width-rx (MIC/RX Path) */ + u32 slot_width_tx; /* qcom,qaif-aif-slot-width-tx (SPKR/TX Path) */ + u32 sample_width_rx; /* qcom,qaif-aif-sample-width-rx (MIC/RX Path) */ + u32 sample_width_tx; /* qcom,qaif-aif-sample-width-tx (SPKR/TX Path) */ + /* Slot enable masks (32-bit masks for 32 slots) */ + u32 slot_en_rx_mask; /* qcom,qaif-aif-slot-en-rx-mask (MIC/RX Path) */ + u32 slot_en_tx_mask; /* qcom,qaif-aif-slot-en-tx-mask (SPKR/TX Path) */ + /* Control configuration */ + u32 loopback_en; /* qcom,qaif-aif-loopback-en */ + u32 ctrl_data_oe; /* qcom,qaif-aif-ctrl-data-oe */ + /* Lane configuration */ + u32 num_lanes; /* Number of lanes configured */ + struct qaif_lane_config lane_cfg[QAIF_MAX_LANES]; /* qcom,qaif-aif-lane-config */ + u32 lane_en_mask; + u32 lane_dir_mask; + /* Mono/Stereo mode */ + u32 mono_mode_tx; /* qcom,qaif-aif-mono-mode-tx (MIC/RX Path) */ + u32 mono_mode_rx; /* qcom,qaif-aif-mono-mode-rx (SPKR/TX Path) */ + /* Frame configuration */ + u32 full_cycle_en; /* qcom,qaif-aif-full-cycle-en */ + u32 bits_per_lane; /* qcom,qaif-aif-bits-per-lane (FRAME_CFG) */ +}; + +struct qaif_pcm_data { + int stream_dma_idx; + //int i2s_port; +}; + +struct qaif_dma_mem_info { + dma_addr_t dma_addr; + size_t alloc_size; + void *vaddr; +}; + +struct qaif_dmaidx_dai_map { + unsigned int dai_id; +}; + +/* Both the CPU DAI and platform drivers will access this data */ +struct qaif_drv_data { + /* MI2S system clock */ + struct clk *mi2s_osr_clk[LPASS_MAX_MI2S_PORTS]; + + /* MI2S bit clock (derived from system clock by a divider */ + struct clk *mi2s_bit_clk[LPASS_MAX_MI2S_PORTS]; + + /* The state of MI2S prepare dai_ops was called */ + bool mi2s_was_prepared[LPASS_MAX_MI2S_PORTS]; + + /* SOC specific clock list */ + struct clk_bulk_data *clks; + int num_clks; + + struct clk *aud_dma_clk; + struct clk *aud_dma_mem_clk; + + /* Qualcomm audio interface (QAIF) registers */ + void __iomem *audio_qaif; + + /* regmap backed by the Qualcomm audio interface (QAIF) registers */ + struct regmap *audio_qaif_map; + + /* interrupts from the Qualcomm audio interface (QAIF) */ + int audio_qaif_irq; + + /* QAIF init config refcount*/ + unsigned int qaif_init_ref_cnt; + + /* SOC specific variations in the QAIF IP integration */ + struct qaif_variant *variant; + + /* bit map to keep track of dma idx allocations */ + unsigned long aif_dma_idx_bit_map; + unsigned long cif_dma_idx_bit_map; + + /* used it for handling interrupt per dma channel */ + struct snd_pcm_substream *aif_substream[LPASS_MAX_AIF_DMA_IDX]; + struct snd_pcm_substream *cif_substream[LPASS_MAX_CIF_DMA_IDX]; + + u64 smmu_csid_bits; + u64 smmu_sid_bits; + + /* DMA Heap handle*/ + struct dma_heap *dma_heap; + /* DMA Heap name*/ + const char *dma_heap_name; + /* DMA info handle per stream/dma idx*/ + struct qaif_dma_mem_info *aif_dma_heap[LPASS_MAX_AIF_DMA_IDX]; + struct qaif_dma_mem_info *cif_dma_heap[LPASS_MAX_CIF_DMA_IDX]; + +}; + +enum qaif_summary_irq_bitmask { + QAIF_SUMMARY_BITMASK_AIF_PERIOD_RDDMA = BIT(0), + QAIF_SUMMARY_BITMASK_AIF_UNDERFLOW_RDDMA = BIT(1), + QAIF_SUMMARY_BITMASK_AIF_ERR_RSP_RDDMA = BIT(2), + QAIF_SUMMARY_BITMASK_AIF_PERIOD_WRDMA = BIT(3), + QAIF_SUMMARY_BITMASK_AIF_OVERFLOW_WRDMA = BIT(4), + QAIF_SUMMARY_BITMASK_AIF_ERR_RSP_WRDMA = BIT(5), + + QAIF_SUMMARY_BITMASK_AUD_OVERFLOW = BIT(6), + QAIF_SUMMARY_BITMASK_AUD_UNDERFLOW = BIT(7), + + QAIF_SUMMARY_BITMASK_RATE_DET = BIT(8), + QAIF_SUMMARY_BITMASK_VFR = BIT(9), + QAIF_SUMMARY_BITMASK_GRP = BIT(10), + QAIF_SUMMARY_BITMASK_RDDMA_OVERLAP = BIT(11), + QAIF_SUMMARY_BITMASK_WRDMA_OVERLAP = BIT(12), + QAIF_SUMMARY_BITMASK_INTF_OVERLAP = BIT(13), + QAIF_SUMMARY_BITMASK_GRP_OVERLAP = BIT(14), + + QAIF_SUMMARY_BITMASK_CIF_OVERLAP_RDDMA = BIT(15), + QAIF_SUMMARY_BITMASK_CIF_OVERLAP_WRDMA = BIT(17), + QAIF_SUMMARY_BITMASK_CIF_PERIOD_RDDMA = BIT(18), + QAIF_SUMMARY_BITMASK_CIF_UNDERFLOW_RDDMA = BIT(19), + QAIF_SUMMARY_BITMASK_CIF_ERR_RSP = BIT(20), + QAIF_SUMMARY_BITMASK_CIF_PERIOD_WRDMA = BIT(24), + QAIF_SUMMARY_BITMASK_CIF_OVERFLOW_WRDMA = BIT(25), + QAIF_SUMMARY_BITMASK_CIF_ERR_RSP_WRDMA = BIT(26) + +}; + +/* defines the bitmask in the status register for each of the clients */ +enum qaif_client_status_register_bitmask_info { + QAIF_BITMASK_GROUP_INF = 0x400, + QAIF_BITMASK_AIF_RDDMA_WRDMA = 0x3F, + QAIF_BITMASK_CIF_RDDMA_WRDMA = 0x71c0000, + QAIF_BITMASK_DP_RDDMA = 0xe00000, + QAIF_BITMASK_AUD_INF = 0xC0, +}; + +struct qaif_irq_map { + int client_id; + u32 mask; + irqreturn_t (*client_irq_handler)(struct qaif_drv_data *drvdata, u32 irq_status); +}; + +enum dma_type { + DMA_TYPE_RDDMA, + DMA_TYPE_WRDMA +}; + +enum qaif_irq { + QAIF_IRQ_PERIOD, + QAIF_IRQ_OVERFLOW, + QAIF_IRQ_UNDERFLOW, + QAIF_IRQ_ERROR +}; + +/* list of clients for IRQ Util */ +enum qaif_client_info { + QAIF_CLIENT_ID_GROUP_INF = 0, + QAIF_CLIENT_ID_AIF_DMA = 1, + QAIF_CLIENT_ID_CIF_DMA = 2, + QAIF_CLIENT_ID_DP_DMA = 3, + QAIF_CLIENT_ID_AUD_INF = 4, + QAIF_CLIENT_ID_MAX +}; + +struct qaif_variant { + u32 ee; + u32 qaif_type; + + u32 num_rddma; + u32 num_wrdma; + u32 wrdma_start; + + u32 num_codec_rddma; //RX + u32 num_codec_wrdma; //TX + u32 codec_wrdma_start; + u32 num_intf; + + u32 rddma_reg_base; + u32 rddma_stride; + u32 codec_rddma_reg_base; + u32 codec_rddma_stride; + + u32 wrdma_reg_base; + u32 wrdma_stride; + u32 codec_wrdma_reg_base; + u32 codec_wrdma_stride; + + u32 rddma_irq_reg_base; + u32 rddma_irq_stride; + u32 codec_rddma_irq_reg_base; + u32 codec_rddma_irq_stride; + + u32 wrdma_irq_reg_base; + u32 wrdma_irq_stride; + u32 codec_wrdma_irq_reg_base; + u32 codec_wrdma_irq_stride; + + u32 qxm_type; + u32 rd_len; + u32 rddma_shram_len; + u32 rddma_shram_start_addr[DMA_TYPE_MAX]; + u32 wr_len; + u32 wrdma_shram_len; + u32 wrdma_shram_start_addr[DMA_TYPE_MAX]; + + /* AIF RDDMA register fields */ + const struct reg_field rddma_enable; + const struct reg_field rddma_reset; + const struct reg_field rddma_num_ot; + const struct reg_field rddma_dma_dyncclk; + const struct reg_field rddma_burst16; + const struct reg_field rddma_burst8; + const struct reg_field rddma_burst4; + const struct reg_field rddma_burst2; + const struct reg_field rddma_burst1; + const struct reg_field rddma_shram_wm; + + /* AIF WRDMA register fields */ + const struct reg_field wrdma_enable; + const struct reg_field wrdma_reset; + const struct reg_field wrdma_num_ot; + const struct reg_field wrdma_dma_dyncclk; + const struct reg_field wrdma_burst16; + const struct reg_field wrdma_burst8; + const struct reg_field wrdma_burst4; + const struct reg_field wrdma_burst2; + const struct reg_field wrdma_burst1; + const struct reg_field wrdma_shram_wm; + + /* CODEC RDDMA register fields */ + const struct reg_field cif_rddma_enable; + const struct reg_field cif_rddma_reset; + const struct reg_field cif_rddma_num_ot; + const struct reg_field cif_rddma_dma_dyncclk; + const struct reg_field cif_rddma_burst16; + const struct reg_field cif_rddma_burst8; + const struct reg_field cif_rddma_burst4; + const struct reg_field cif_rddma_burst2; + const struct reg_field cif_rddma_burst1; + const struct reg_field cif_rddma_shram_wm; + const struct reg_field cif_rddma_active_ch_en; + const struct reg_field cif_rddma_fs_sel; + const struct reg_field cif_rddma_fs_delay; + const struct reg_field cif_rddma_fs_out_gate; + const struct reg_field cif_rddma_intf_dyncclk; + const struct reg_field cif_rddma_en_16bit_unpack; + + /* CODEC WRDMA register fields */ + const struct reg_field cif_wrdma_enable; + const struct reg_field cif_wrdma_reset; + const struct reg_field cif_wrdma_num_ot; + const struct reg_field cif_wrdma_dma_dyncclk; + const struct reg_field cif_wrdma_burst16; + const struct reg_field cif_wrdma_burst8; + const struct reg_field cif_wrdma_burst4; + const struct reg_field cif_wrdma_burst2; + const struct reg_field cif_wrdma_burst1; + const struct reg_field cif_wrdma_shram_wm; + const struct reg_field cif_wrdma_active_ch_en; + const struct reg_field cif_wrdma_fs_sel; + const struct reg_field cif_wrdma_fs_delay; + const struct reg_field cif_wrdma_fs_out_gate; + const struct reg_field cif_wrdma_intf_dyncclk; + const struct reg_field cif_wrdma_en_16bit_unpack; + + /* Regmap fields of AIF interface registers bitfields */ + const struct reg_field aif_inv_sync; + const struct reg_field aif_sync_delay; + const struct reg_field aif_sync_mode; + const struct reg_field aif_sync_src; + const struct reg_field aif_sample_width_rx; + const struct reg_field aif_sample_width_tx; + const struct reg_field aif_slot_width_rx; + const struct reg_field aif_slot_width_tx; + const struct reg_field aif_bits_per_lane; + const struct reg_field aif_slot_en_tx_mask; + const struct reg_field aif_slot_en_rx_mask; + const struct reg_field aif_loopback_en; + const struct reg_field aif_ctrl_data_oe; + const struct reg_field aif_lane_en; + const struct reg_field aif_lane_dir; + const struct reg_field aif_mono_mode_rx; + const struct reg_field aif_mono_mode_tx; + const struct reg_field aif_full_cycle_en; + + /* Regmap fields of DMACTL registers bitfields */ + struct qaif_dmactl *aif_rd_dmactl; + struct qaif_dmactl *aif_wr_dmactl; + + /* Regmap fields of CODEC DMA CTRL registers */ + struct qaif_dmactl *cif_rd_dmactl; + struct qaif_dmactl *cif_wr_dmactl; + + struct qaif_aif_config aif_intf_cfg[QAIF_MAX_AIF_CFG_CNT]; + struct qaif_aud_intfctl *aif_intfctl; + + struct qaif_cdc_intfctl *cif_rddma_intfctl; + struct qaif_cdc_intfctl *cif_wrdma_intfctl; + + /* Platform-specific data */ + const char **clk_name; + int num_clks; + struct snd_soc_dai_driver *dai_driver; + int num_dai; + const char **dai_osr_clk_names; + const char **dai_bit_clk_names; + + /* Platform-specific function pointers */ + int (*init)(struct platform_device *pdev); + int (*exit)(struct platform_device *pdev); + int (*alloc_stream_dma_idx)(struct qaif_drv_data *data, int direction, unsigned int dai_id); + int (*free_stream_dma_idx)(struct qaif_drv_data *data, int chan, unsigned int dai_id); + int (*get_dma_idx)(unsigned int dai_id); + +}; + +/* External DAI ops structures defined in qaif-cpu.c */ +extern const struct snd_soc_dai_ops asoc_qcom_qaif_cif_dai_ops; +extern const struct snd_soc_dai_ops asoc_qcom_qaif_aif_cpu_dai_ops; + +/* Platform driver functions defined in qaif-cpu.c */ +int asoc_qcom_qaif_cpu_platform_probe(struct platform_device *pdev); +int asoc_qcom_qaif_platform_register(struct platform_device *pdev); +void asoc_qcom_qaif_cpu_platform_remove(struct platform_device *pdev); +void asoc_qcom_qaif_cpu_platform_shutdown(struct platform_device *pdev); + +#endif /* __QAIF_H__ */ From 85158df63123d89c7f877b170998e237653fd8c5 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Fri, 29 May 2026 11:44:54 +0530 Subject: [PATCH 3/5] ASoC: qcom: Add QAIF CPU DAI driver Add the CPU DAI driver for the Qualcomm Technologies Qualcomm Audio Interface (QAIF). Signed-off-by: Mohammad Rafi Shaik --- sound/soc/qcom/qaif-cpu.c | 1585 +++++++++++++++++++++++++++++++++++++ 1 file changed, 1585 insertions(+) create mode 100644 sound/soc/qcom/qaif-cpu.c diff --git a/sound/soc/qcom/qaif-cpu.c b/sound/soc/qcom/qaif-cpu.c new file mode 100644 index 0000000000000..0a3bd9e5879a0 --- /dev/null +++ b/sound/soc/qcom/qaif-cpu.c @@ -0,0 +1,1585 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * qaif-cpu.c -- ALSA SoC CPU-Platform DAI driver for QTi QAIF + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qaif-reg.h" +#include "qaif.h" + +#define QAIF_AIF_REG_READ 1 +#define QAIF_AIF_REG_WRITE 0 + +static int qaif_cif_cpu_init_bitfields(struct device *dev, + struct regmap *map) +{ + struct qaif_drv_data *drvdata = dev_get_drvdata(dev); + struct qaif_variant *v = drvdata->variant; + struct qaif_dmactl *rd_dmactl; + struct qaif_dmactl *wr_dmactl; + struct qaif_cdc_intfctl *rd_intfctl; + struct qaif_cdc_intfctl *wr_intfctl; + + /* Allocate RDDMA control structure */ + rd_dmactl = devm_kzalloc(dev, sizeof(struct qaif_dmactl), GFP_KERNEL); + if (!rd_dmactl) + return -ENOMEM; + + /* Allocate WRDMA control structure */ + wr_dmactl = devm_kzalloc(dev, sizeof(struct qaif_dmactl), GFP_KERNEL); + if (!wr_dmactl) + return -ENOMEM; + + /* Allocate RDDMA INTF control structure */ + rd_intfctl = devm_kzalloc(dev, sizeof(struct qaif_cdc_intfctl), GFP_KERNEL); + if (!rd_intfctl) + return -ENOMEM; + + /* Allocate WRDMA INTF control structure */ + wr_intfctl = devm_kzalloc(dev, sizeof(struct qaif_cdc_intfctl), GFP_KERNEL); + if (!wr_intfctl) + return -ENOMEM; + + /* =================================================================== + * Allocate RDDMA (RX/Playback) regmap fields for all 4 channels + * =================================================================== + */ + + /* CTL register fields */ + rd_dmactl->enable = devm_regmap_field_alloc(dev, map, v->cif_rddma_enable); + rd_dmactl->reset = devm_regmap_field_alloc(dev, map, v->cif_rddma_reset); + + /* CFG register fields */ + rd_dmactl->shram_wm = devm_regmap_field_alloc(dev, map, v->cif_rddma_shram_wm); + rd_dmactl->burst1 = devm_regmap_field_alloc(dev, map, v->cif_rddma_burst1); + rd_dmactl->burst2 = devm_regmap_field_alloc(dev, map, v->cif_rddma_burst2); + rd_dmactl->burst4 = devm_regmap_field_alloc(dev, map, v->cif_rddma_burst4); + rd_dmactl->burst8 = devm_regmap_field_alloc(dev, map, v->cif_rddma_burst8); + rd_dmactl->burst16 = devm_regmap_field_alloc(dev, map, v->cif_rddma_burst16); + rd_dmactl->dma_dyncclk = devm_regmap_field_alloc(dev, map, v->cif_rddma_dma_dyncclk); + rd_dmactl->num_ot = devm_regmap_field_alloc(dev, map, v->cif_rddma_num_ot); + + /* INTF_CFG register fields */ + rd_intfctl->en_16bit_unpack = + devm_regmap_field_alloc(dev, map, v->cif_rddma_en_16bit_unpack); + rd_intfctl->intf_dyncclk = devm_regmap_field_alloc(dev, map, v->cif_rddma_intf_dyncclk); + rd_intfctl->fs_out_gate = devm_regmap_field_alloc(dev, map, v->cif_rddma_fs_out_gate); + rd_intfctl->fs_sel = devm_regmap_field_alloc(dev, map, v->cif_rddma_fs_sel); + rd_intfctl->fs_delay = devm_regmap_field_alloc(dev, map, v->cif_rddma_fs_delay); + rd_intfctl->active_ch_en = devm_regmap_field_alloc(dev, map, v->cif_rddma_active_ch_en); + + /* =================================================================== + * Allocate WRDMA (TX/Capture) regmap fields for all 4 channels + * =================================================================== + */ + + /* CTL register fields */ + wr_dmactl->enable = devm_regmap_field_alloc(dev, map, v->cif_wrdma_enable); + wr_dmactl->reset = devm_regmap_field_alloc(dev, map, v->cif_wrdma_reset); + + /* CFG register fields */ + wr_dmactl->shram_wm = devm_regmap_field_alloc(dev, map, v->cif_wrdma_shram_wm); + wr_dmactl->burst1 = devm_regmap_field_alloc(dev, map, v->cif_wrdma_burst1); + wr_dmactl->burst2 = devm_regmap_field_alloc(dev, map, v->cif_wrdma_burst2); + wr_dmactl->burst4 = devm_regmap_field_alloc(dev, map, v->cif_wrdma_burst4); + wr_dmactl->burst8 = devm_regmap_field_alloc(dev, map, v->cif_wrdma_burst8); + wr_dmactl->burst16 = devm_regmap_field_alloc(dev, map, v->cif_wrdma_burst16); + wr_dmactl->dma_dyncclk = devm_regmap_field_alloc(dev, map, v->cif_wrdma_dma_dyncclk); + wr_dmactl->num_ot = devm_regmap_field_alloc(dev, map, v->cif_wrdma_num_ot); + + /* INTF_CFG register fields */ + wr_intfctl->en_16bit_unpack = + devm_regmap_field_alloc(dev, map, v->cif_wrdma_en_16bit_unpack); + wr_intfctl->intf_dyncclk = devm_regmap_field_alloc(dev, map, v->cif_wrdma_intf_dyncclk); + wr_intfctl->fs_out_gate = devm_regmap_field_alloc(dev, map, v->cif_wrdma_fs_out_gate); + wr_intfctl->fs_sel = devm_regmap_field_alloc(dev, map, v->cif_wrdma_fs_sel); + wr_intfctl->fs_delay = devm_regmap_field_alloc(dev, map, v->cif_wrdma_fs_delay); + wr_intfctl->active_ch_en = devm_regmap_field_alloc(dev, map, v->cif_wrdma_active_ch_en); + + /* =================================================================== + * Check for allocation errors + * =================================================================== + */ + if (IS_ERR(rd_dmactl->enable) || IS_ERR(wr_dmactl->enable) || + IS_ERR(rd_dmactl->reset) || IS_ERR(wr_dmactl->reset) || + IS_ERR(rd_dmactl->num_ot) || IS_ERR(wr_dmactl->num_ot) || + IS_ERR(rd_dmactl->dma_dyncclk) || IS_ERR(wr_dmactl->dma_dyncclk) || + IS_ERR(rd_dmactl->burst16) || IS_ERR(wr_dmactl->burst16) || + IS_ERR(rd_dmactl->burst8) || IS_ERR(wr_dmactl->burst8) || + IS_ERR(rd_dmactl->burst4) || IS_ERR(wr_dmactl->burst4) || + IS_ERR(rd_dmactl->burst2) || IS_ERR(wr_dmactl->burst2) || + IS_ERR(rd_dmactl->burst1) || IS_ERR(wr_dmactl->burst1) || + IS_ERR(rd_dmactl->shram_wm) || IS_ERR(wr_dmactl->shram_wm) || + IS_ERR(rd_intfctl->active_ch_en) || IS_ERR(wr_intfctl->active_ch_en) || + IS_ERR(rd_intfctl->fs_sel) || IS_ERR(wr_intfctl->fs_sel) || + IS_ERR(rd_intfctl->fs_delay) || IS_ERR(wr_intfctl->fs_delay) || + IS_ERR(rd_intfctl->fs_out_gate) || IS_ERR(wr_intfctl->fs_out_gate) || + IS_ERR(rd_intfctl->intf_dyncclk) || IS_ERR(wr_intfctl->intf_dyncclk) || + IS_ERR(rd_intfctl->en_16bit_unpack) || IS_ERR(wr_intfctl->en_16bit_unpack)) { + dev_err(dev, "error allocating codec dma regmap fields\n"); + return -EINVAL; + } + + /* Store in variant data */ + v->cif_rd_dmactl = rd_dmactl; + v->cif_wr_dmactl = wr_dmactl; + v->cif_rddma_intfctl = rd_intfctl; + v->cif_wrdma_intfctl = wr_intfctl; + + return 0; +} + +static struct qaif_cdc_intfctl * +qaif_get_cif_intfctl_handle(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai); + struct qaif_variant *v = drvdata->variant; + unsigned int dai_id = cpu_dai->driver->id; + struct qaif_cdc_intfctl *intfctl = NULL; + + if (!v) { + dev_err(soc_runtime->dev, "No variant data\n"); + return intfctl; + } + + switch (dai_id) { + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + intfctl = v->cif_rddma_intfctl; + break; + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + intfctl = v->cif_wrdma_intfctl; + break; + default: + dev_err(soc_runtime->dev, "invalid dai id for dma ctl: %d\n", dai_id); + break; + } + return intfctl; +} + +static int qaif_cif_daiops_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai); + struct qaif_variant *v = drvdata->variant; + struct qaif_cdc_intfctl *intfctl = NULL; + unsigned int dai_id = cpu_dai->driver->id; + unsigned int ret, regval; + unsigned int channels = params_channels(params); + int idx; + + pr_err("%s:%d: dai_id=%u stream=%d channels=%u rate=%u\n", + __func__, __LINE__, dai_id, substream->stream, channels, + params_rate(params)); + + switch (channels) { + case 1: + regval = QAIF_CIF_DMA_INTF_ONE_CHANNEL; + break; + case 2: + regval = QAIF_CIF_DMA_INTF_TWO_CHANNEL; + break; + case 4: + regval = QAIF_CIF_DMA_INTF_FOUR_CHANNEL; + break; + case 6: + regval = QAIF_CIF_DMA_INTF_SIX_CHANNEL; + break; + case 8: + regval = QAIF_CIF_DMA_INTF_EIGHT_CHANNEL; + break; + default: + dev_err(soc_runtime->dev, "invalid PCM config\n"); + return -EINVAL; + } + + intfctl = qaif_get_cif_intfctl_handle(substream, dai); + if (!intfctl) { + dev_err(soc_runtime->dev, "Invalid intfctl: %d\n", dai_id); + return -EINVAL; + } + idx = v->get_dma_idx(dai_id); + if (idx < 0) { + dev_err(soc_runtime->dev, "Invalid DMA index: %d\n", idx); + return -EINVAL; + } + //active channel mask + ret = regmap_fields_write(intfctl->active_ch_en, idx, regval); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to intfctl active_ch_en reg field: %d\n", ret); + return ret; + } + pr_err("%s:%d: configured active_ch_en idx=%d val=0x%x\n", + __func__, __LINE__, idx, regval); + + return 0; +} + +static int qaif_cif_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai); + struct qaif_variant *v = drvdata->variant; + unsigned int dai_id = cpu_dai->driver->id; + struct qaif_cdc_intfctl *intfctl = NULL; + int ret = 0, idx; + + pr_err("%s:%d: dai_id=%u stream=%d cmd=%d\n", + __func__, __LINE__, dai_id, substream->stream, cmd); + + intfctl = qaif_get_cif_intfctl_handle(substream, dai); + if (!intfctl) { + dev_err(soc_runtime->dev, "Invalid intfctl: %d\n", dai_id); + return -EINVAL; + } + idx = v->get_dma_idx(dai_id); + if (idx < 0) { + dev_err(soc_runtime->dev, "Invalid DMA index: %d\n", idx); + return -EINVAL; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = regmap_fields_write(intfctl->intf_dyncclk, idx, 1); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to dmactl intf_dyncclk reg field: %d\n", + ret); + return ret; + } + /* ToDo: Hardcoded for now, Later to modify dynamically */ + ret = regmap_fields_write(intfctl->fs_sel, idx, 0x0); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to dmactl codec_fs_sel reg field: %d\n", + ret); + return ret; + } + + ret = regmap_fields_write(intfctl->en_16bit_unpack, idx, 0x1); + /* ToDo: based on bw and packing enable flag */ + if (ret) { + dev_err(soc_runtime->dev, + "error writing to dmactl en_16bit_unpack reg field: %d\n", + ret); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = regmap_fields_write(intfctl->intf_dyncclk, idx, 0); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to dmactl intf_dyncclk reg field: %d\n", + ret); + return ret; + } + ret = regmap_fields_write(intfctl->en_16bit_unpack, idx, 0); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to dmactl en_16bit_unpack reg field: %d\n", + ret); + return ret; + } + break; + default: + ret = -EINVAL; + dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, cmd); + break; + } + pr_err("%s:%d: cmd=%d ret=%d idx=%d\n", + __func__, __LINE__, cmd, ret, idx); + return ret; +} + +const struct snd_soc_dai_ops asoc_qcom_qaif_cif_dai_ops = { + .hw_params = qaif_cif_daiops_hw_params, + .trigger = qaif_cif_daiops_trigger, +}; +EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cif_dai_ops); + +static int qaif_aif_cfg_cpu_init_bitfields(struct device *dev, + struct regmap *map) +{ + struct qaif_drv_data *drvdata = dev_get_drvdata(dev); + struct qaif_variant *v = drvdata->variant; + struct qaif_aud_intfctl *aif_intfctl; + + /* Allocate AIF interface control structure */ + aif_intfctl = devm_kzalloc(dev, sizeof(struct qaif_aud_intfctl), GFP_KERNEL); + if (!aif_intfctl) + return -ENOMEM; + + /* =================================================================== + * Allocate regmap fields for AUD_INTF SYNC_CFG register + * =================================================================== + */ + aif_intfctl->inv_sync = devm_regmap_field_alloc(dev, map, v->aif_inv_sync); + aif_intfctl->sync_delay = devm_regmap_field_alloc(dev, map, v->aif_sync_delay); + aif_intfctl->sync_mode = devm_regmap_field_alloc(dev, map, v->aif_sync_mode); + aif_intfctl->sync_src = devm_regmap_field_alloc(dev, map, v->aif_sync_src); + + /* =================================================================== + * Allocate regmap fields for AUD_INTF BIT_WIDTH_CFG register + * =================================================================== + */ + aif_intfctl->slot_width_rx = devm_regmap_field_alloc(dev, map, v->aif_slot_width_rx); + aif_intfctl->slot_width_tx = devm_regmap_field_alloc(dev, map, v->aif_slot_width_tx); + aif_intfctl->sample_width_rx = devm_regmap_field_alloc(dev, map, v->aif_sample_width_rx); + aif_intfctl->sample_width_tx = devm_regmap_field_alloc(dev, map, v->aif_sample_width_tx); + + /* =================================================================== + * Allocate regmap fields for AUD_INTF MI2S_CFG register + * =================================================================== + */ + aif_intfctl->mono_mode_rx = devm_regmap_field_alloc(dev, map, v->aif_mono_mode_rx); + aif_intfctl->mono_mode_tx = devm_regmap_field_alloc(dev, map, v->aif_mono_mode_tx); + + /* =================================================================== + * Allocate regmap fields for AUD_INTF LANE_CFG register + * =================================================================== + */ + aif_intfctl->lane_en = devm_regmap_field_alloc(dev, map, v->aif_lane_en); + aif_intfctl->lane_dir = devm_regmap_field_alloc(dev, map, v->aif_lane_dir); + aif_intfctl->loopback_en = devm_regmap_field_alloc(dev, map, v->aif_loopback_en); + aif_intfctl->ctrl_data_oe = devm_regmap_field_alloc(dev, map, v->aif_ctrl_data_oe); + + /* =================================================================== + * Allocate regmap fields for AUD_INTF SLOT_EN registers + * =================================================================== + */ + aif_intfctl->slot_en_rx_mask = devm_regmap_field_alloc(dev, map, v->aif_slot_en_rx_mask); + aif_intfctl->slot_en_tx_mask = devm_regmap_field_alloc(dev, map, v->aif_slot_en_tx_mask); + + /* =================================================================== + * Allocate regmap fields for AUD_INTF FRAME_CFG register + * =================================================================== + */ + aif_intfctl->bits_per_lane = devm_regmap_field_alloc(dev, map, v->aif_bits_per_lane); + + /* =================================================================== + * Allocate regmap fields for AUD_INTF CFG register + * =================================================================== + */ + aif_intfctl->full_cycle_en = devm_regmap_field_alloc(dev, map, v->aif_full_cycle_en); + + /* =================================================================== + * Check for allocation errors + * =================================================================== + */ + if (IS_ERR(aif_intfctl->inv_sync) || IS_ERR(aif_intfctl->sync_delay) || + IS_ERR(aif_intfctl->sync_mode) || IS_ERR(aif_intfctl->sync_src) || + IS_ERR(aif_intfctl->slot_width_rx) || IS_ERR(aif_intfctl->slot_width_tx) || + IS_ERR(aif_intfctl->sample_width_rx) || IS_ERR(aif_intfctl->sample_width_tx) || + IS_ERR(aif_intfctl->mono_mode_rx) || IS_ERR(aif_intfctl->mono_mode_tx) || + IS_ERR(aif_intfctl->lane_en) || IS_ERR(aif_intfctl->lane_dir) || + IS_ERR(aif_intfctl->loopback_en) || IS_ERR(aif_intfctl->ctrl_data_oe) || + IS_ERR(aif_intfctl->slot_en_rx_mask) || IS_ERR(aif_intfctl->slot_en_tx_mask) || + IS_ERR(aif_intfctl->bits_per_lane) || IS_ERR(aif_intfctl->full_cycle_en)) { + dev_err(dev, "error allocating AIF interface regmap fields\n"); + return -EINVAL; + } + + /* Store in variant data */ + v->aif_intfctl = aif_intfctl; + + dev_info(dev, "Successfully initialized AIF interface control bitfields\n"); + return 0; +} + +static int qaif_aif_cpu_init_bitfields(struct device *dev, + struct regmap *map) +{ + struct qaif_drv_data *drvdata = dev_get_drvdata(dev); + struct qaif_variant *v = drvdata->variant; + struct qaif_dmactl *rd_dmactl; + struct qaif_dmactl *wr_dmactl; + + /* Allocate RDDMA control structure */ + rd_dmactl = devm_kzalloc(dev, sizeof(struct qaif_dmactl), GFP_KERNEL); + if (!rd_dmactl) + return -ENOMEM; + + /* Allocate WRDMA control structure */ + wr_dmactl = devm_kzalloc(dev, sizeof(struct qaif_dmactl), GFP_KERNEL); + if (!wr_dmactl) + return -ENOMEM; + + /* =================================================================== + * Allocate RDDMA (RX/Playback) regmap fields for all 4 channels + * =================================================================== + */ + + /* CTL register fields */ + rd_dmactl->enable = devm_regmap_field_alloc(dev, map, v->rddma_enable); + rd_dmactl->reset = devm_regmap_field_alloc(dev, map, v->rddma_reset); + + /* CFG register fields */ + rd_dmactl->shram_wm = devm_regmap_field_alloc(dev, map, v->rddma_shram_wm); + rd_dmactl->burst1 = devm_regmap_field_alloc(dev, map, v->rddma_burst1); + rd_dmactl->burst2 = devm_regmap_field_alloc(dev, map, v->rddma_burst2); + rd_dmactl->burst4 = devm_regmap_field_alloc(dev, map, v->rddma_burst4); + rd_dmactl->burst8 = devm_regmap_field_alloc(dev, map, v->rddma_burst8); + rd_dmactl->burst16 = devm_regmap_field_alloc(dev, map, v->rddma_burst16); + rd_dmactl->dma_dyncclk = devm_regmap_field_alloc(dev, map, v->rddma_dma_dyncclk); + rd_dmactl->num_ot = devm_regmap_field_alloc(dev, map, v->rddma_num_ot); + + /* =================================================================== + * Allocate WRDMA (TX/Capture) regmap fields for all 4 channels + * =================================================================== + */ + + /* CTL register fields */ + wr_dmactl->enable = devm_regmap_field_alloc(dev, map, v->wrdma_enable); + wr_dmactl->reset = devm_regmap_field_alloc(dev, map, v->wrdma_reset); + + /* CFG register fields */ + wr_dmactl->shram_wm = devm_regmap_field_alloc(dev, map, v->wrdma_shram_wm); + wr_dmactl->burst1 = devm_regmap_field_alloc(dev, map, v->wrdma_burst1); + wr_dmactl->burst2 = devm_regmap_field_alloc(dev, map, v->wrdma_burst2); + wr_dmactl->burst4 = devm_regmap_field_alloc(dev, map, v->wrdma_burst4); + wr_dmactl->burst8 = devm_regmap_field_alloc(dev, map, v->wrdma_burst8); + wr_dmactl->burst16 = devm_regmap_field_alloc(dev, map, v->wrdma_burst16); + wr_dmactl->dma_dyncclk = devm_regmap_field_alloc(dev, map, v->wrdma_dma_dyncclk); + wr_dmactl->num_ot = devm_regmap_field_alloc(dev, map, v->wrdma_num_ot); + + /* =================================================================== + * Check for allocation errors + * =================================================================== + */ + if (IS_ERR(rd_dmactl->enable) || IS_ERR(wr_dmactl->enable) || + IS_ERR(rd_dmactl->reset) || IS_ERR(wr_dmactl->reset) || + IS_ERR(rd_dmactl->num_ot) || IS_ERR(wr_dmactl->num_ot) || + IS_ERR(rd_dmactl->dma_dyncclk) || IS_ERR(wr_dmactl->dma_dyncclk) || + IS_ERR(rd_dmactl->burst16) || IS_ERR(wr_dmactl->burst16) || + IS_ERR(rd_dmactl->burst8) || IS_ERR(wr_dmactl->burst8) || + IS_ERR(rd_dmactl->burst4) || IS_ERR(wr_dmactl->burst4) || + IS_ERR(rd_dmactl->burst2) || IS_ERR(wr_dmactl->burst2) || + IS_ERR(rd_dmactl->burst1) || IS_ERR(wr_dmactl->burst1) || + IS_ERR(rd_dmactl->shram_wm) || IS_ERR(wr_dmactl->shram_wm)) { + dev_err(dev, "error allocating AIF dma regmap fields\n"); + return -EINVAL; + } + + /* Store in variant data */ + v->aif_rd_dmactl = rd_dmactl; + v->aif_wr_dmactl = wr_dmactl; + + return 0; +} + +static int qaif_aif_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + return 0; +} + +static int qaif_aif_cpu_daiops_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai); + const struct qaif_variant *v = drvdata->variant; + int idx, ret = 0; + + pr_err("%s:%d: dai_id=%d stream=%d\n", + __func__, __LINE__, dai->driver->id, substream->stream); + + idx = v->get_dma_idx(dai->driver->id); + if (idx < 0) { + dev_err(dai->dev, "%s: Invalid DMA index: %d\n", __func__, idx); + return -EINVAL; + } + + ret = clk_prepare(drvdata->mi2s_bit_clk[idx]); + if (ret) { + dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); + return ret; + } + pr_err("%s:%d: prepared bit_clk idx=%d\n", + __func__, __LINE__, idx); + return 0; +} + +static void qaif_aif_cpu_daiops_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai); + const struct qaif_variant *v = drvdata->variant; + int idx = v->get_dma_idx(dai->driver->id); + + pr_err("%s:%d: dai_id=%d stream=%d\n", + __func__, __LINE__, dai->driver->id, substream->stream); + + if (idx < 0) { + dev_err(dai->dev, "%s: Invalid DMA index: %d\n", __func__, idx); + return; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_CTL_REG(idx), 0); + else + regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_CTL_REG(idx), 0); + + if (drvdata->mi2s_was_prepared[idx]) { + drvdata->mi2s_was_prepared[idx] = false; + clk_disable(drvdata->mi2s_bit_clk[idx]); + } + + clk_unprepare(drvdata->mi2s_bit_clk[idx]); +} + +static int qaif_aif_cpu_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai); + const struct qaif_variant *v = drvdata->variant; + int idx, ret = -EINVAL; + + idx = v->get_dma_idx(dai->driver->id); + + if (idx < 0) { + dev_err(dai->dev, "%s: Invalid DMA index: %d\n", __func__, idx); + return -EINVAL; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_CTL_REG(idx), QAIF_AUD_INTF_CTL_ENABLE_TX); + } else { + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_CTL_REG(idx), QAIF_AUD_INTF_CTL_ENABLE_RX); + } + if (ret) + dev_err(dai->dev, "error writing to AIF CTL reg: %d\n", ret); + + ret = clk_enable(drvdata->mi2s_bit_clk[idx]); + if (ret) { + dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); + return ret; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_CTL_REG(idx), 0); + } else { + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_CTL_REG(idx), 0); + } + if (ret) + dev_err(dai->dev, "error writing to AIF CTL reg: %d\n", ret); + + clk_disable(drvdata->mi2s_bit_clk[idx]); + + break; + } + + return ret; +} + +static int qaif_aif_cpu_daiops_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return 0; +} + +static int qaif_aif_cpu_daiops_probe(struct snd_soc_dai *dai) +{ + return 0; +} + +static int qaif_aif_cpu_daiops_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct qaif_drv_data *drvdata = snd_soc_dai_get_drvdata(dai); + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); + struct qaif_variant *v = drvdata->variant; + unsigned int idx; + struct qaif_aif_config *aif_intf_cfg = NULL; + snd_pcm_format_t format = params_format(params); + unsigned int channels = params_channels(params); + unsigned int rate = params_rate(params); + unsigned int slot_width = 32; + int bitwidth, ret; + u32 sync_cfg_val, lane_cfg_val, mi2s_cfg_val, frame_cfg_val; + u32 tx_bw_fields, rx_bw_fields, bit_width_cfg_val; + + idx = v->get_dma_idx(dai->driver->id); + + if (idx < 0) { + dev_err(dai->dev, "%s: Invalid DMA index: %d\n", __func__, idx); + return -EINVAL; + } + + aif_intf_cfg = &v->aif_intf_cfg[idx]; + + if (!aif_intf_cfg) { + dev_err(dai->dev, "AIF interface config not found\n"); + return -EINVAL; + } + bitwidth = snd_pcm_format_width(format); + if (bitwidth < 0) { + dev_err(dai->dev, "invalid bit width given: %d\n", bitwidth); + return bitwidth; + } + + /* Combine all fields into single value */ + sync_cfg_val = ((aif_intf_cfg->invert_sync << QAIF_AUD_INTF_SYNC_CFG_INV_SYNC_SHFT) & + QAIF_AUD_INTF_SYNC_CFG_INV_SYNC_MASK) | + ((aif_intf_cfg->sync_delay << + QAIF_AUD_INTF_SYNC_CFG_SYNC_DELAY_SHFT) & + QAIF_AUD_INTF_SYNC_CFG_SYNC_DELAY_MASK) | + ((aif_intf_cfg->sync_mode << + QAIF_AUD_INTF_SYNC_CFG_SYNC_MODE_SHFT) & + QAIF_AUD_INTF_SYNC_CFG_SYNC_MODE_MASK) | + ((aif_intf_cfg->sync_src << QAIF_AUD_INTF_SYNC_CFG_SYNC_SRC_SHFT) & + QAIF_AUD_INTF_SYNC_CFG_SYNC_SRC_MASK); + + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_SYNC_CFG_REG(idx), sync_cfg_val); + if (ret) { + dev_err(dai->dev, "Failed to write QAIF_AUD_INTF_SYNC_CFG_REG: %d\n", ret); + return ret; + } + + lane_cfg_val = ((aif_intf_cfg->loopback_en << QAIF_AUD_INTF_LANE_CFG_LOOPBACK_SHFT) & + QAIF_AUD_INTF_LANE_CFG_LOOPBACK_MASK) | + ((aif_intf_cfg->ctrl_data_oe << + QAIF_AUD_INTF_LANE_CFG_CTRL_DATA_OE_SHFT) & + QAIF_AUD_INTF_LANE_CFG_CTRL_DATA_OE_MASK) | + ((aif_intf_cfg->lane_en_mask << + QAIF_AUD_INTF_LANE_CFG_LANE_EN_SHFT) & + QAIF_AUD_INTF_LANE_CFG_LANE_EN_MASK) | + ((aif_intf_cfg->lane_dir_mask << + QAIF_AUD_INTF_LANE_CFG_LANE_DIR_SHFT) & + QAIF_AUD_INTF_LANE_CFG_LANE_DIR_MASK); + + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_LANE_CFG_REG(idx), lane_cfg_val); + if (ret) { + dev_err(dai->dev, "Failed to write QAIF_AUD_INTF_LANE_CFG_REG: %d\n", ret); + return ret; + } + + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_CFG_REG(idx), aif_intf_cfg->full_cycle_en); + if (ret) { + dev_err(dai->dev, "Failed to write QAIF_AUD_INTF_CFG_REG: %d\n", ret); + return ret; + } + dev_dbg(dai->dev, "%s: sync_cfg_val: %x, lane_cfg_val: %x, full_cycle_en: %x\n", + __func__, sync_cfg_val, lane_cfg_val, aif_intf_cfg->full_cycle_en); + + ret = regmap_read(drvdata->audio_qaif_map, + QAIF_AUD_INTF_BIT_WIDTH_CFG_REG(idx), + &bit_width_cfg_val); + if (ret) { + dev_err(dai->dev, "Failed to read QAIF_AUD_INTF_BIT_WIDTH_CFG_REG: %d\n", ret); + return ret; + } + + ret = regmap_read(drvdata->audio_qaif_map, + QAIF_AUD_INTF_MI2S_CFG_REG(idx), &mi2s_cfg_val); + if (ret) { + dev_err(dai->dev, "Failed to read QAIF_AUD_INTF_MI2S_CFG_REG: %d\n", ret); + return ret; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slot_width = aif_intf_cfg->slot_width_tx; + /* Prepare TX field values */ + tx_bw_fields = + ((QAIF_AIF_SAMPLE_WIDTH(bitwidth) << + QAIF_AUD_INTF_BIT_WIDTH_CFG_SAMPLE_WIDTH_TX_SHFT) & + QAIF_AUD_INTF_BIT_WIDTH_CFG_SAMPLE_WIDTH_TX_MASK) | + ((QAIF_AIF_SLOT_WIDTH(slot_width) << + QAIF_AUD_INTF_BIT_WIDTH_CFG_SLOT_WIDTH_TX_SHFT) & + QAIF_AUD_INTF_BIT_WIDTH_CFG_SLOT_WIDTH_TX_MASK); + + /* Clear TX fields, preserve RX fields, write new TX values */ + bit_width_cfg_val = + (bit_width_cfg_val & + ~QAIF_AUD_INTF_BIT_WIDTH_CFG_TX_FIELDS_MASK) | + tx_bw_fields; + + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_BIT_WIDTH_CFG_REG(idx), bit_width_cfg_val); + if (ret) { + dev_err(dai->dev, + "Write to read QAIF_AUD_INTF_BIT_WIDTH_CFG_REG: %d\n", + ret); + return ret; + } + + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_ACTV_SLOT_EN_TX_REG(idx), + aif_intf_cfg->slot_en_tx_mask); + if (ret) { + dev_err(dai->dev, + "Write to read QAIF_AUD_INTF_ACTV_SLOT_EN_TX_REG: %d\n", + ret); + return ret; + } + + frame_cfg_val = (slot_width * aif_intf_cfg->bits_per_lane) - 1; + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_FRAME_CFG_REG(idx), frame_cfg_val); + if (ret) { + dev_err(dai->dev, "Failed to write QAIF_AUD_INTF_FRAME_CFG_REG: %d\n", ret); + return ret; + } + + /* Clear TX field, preserve RX field */ + mi2s_cfg_val &= ~QAIF_AUD_INTF_MI2S_CFG_TX_FIELDS_MASK; + if (channels >= 2) { + /* Set new TX mono mode value */ + mi2s_cfg_val |= + ((QAIF_AUD_INTF_CTL_STEREO << + QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_TX_SHFT) & + QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_TX_MASK); + } else { + /* Set new TX mono mode value */ + mi2s_cfg_val |= + ((QAIF_AUD_INTF_CTL_MONO << + QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_TX_SHFT) & + QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_TX_MASK); + } + + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_MI2S_CFG_REG(idx), + mi2s_cfg_val); + if (ret) { + dev_err(dai->dev, "Write to read QAIF_AUD_INTF_MI2S_CFG_REG: %d\n", ret); + return ret; + } + dev_err(dai->dev, "%s: TX bw_cfg=%x slot_en=%x frame=%x mi2s=%x\n", + __func__, bit_width_cfg_val, + aif_intf_cfg->slot_en_tx_mask, + frame_cfg_val, mi2s_cfg_val); + } else { + slot_width = aif_intf_cfg->slot_width_tx; + /* Prepare RX field values */ + rx_bw_fields = + ((QAIF_AIF_SAMPLE_WIDTH(bitwidth) << + QAIF_AUD_INTF_BIT_WIDTH_CFG_SAMPLE_WIDTH_RX_SHFT) & + QAIF_AUD_INTF_BIT_WIDTH_CFG_SAMPLE_WIDTH_RX_MASK) | + ((QAIF_AIF_SLOT_WIDTH(slot_width) << + QAIF_AUD_INTF_BIT_WIDTH_CFG_SLOT_WIDTH_RX_SHFT) & + QAIF_AUD_INTF_BIT_WIDTH_CFG_SLOT_WIDTH_RX_MASK); + + /* Clear RX fields, preserve TX fields, write new RX values */ + bit_width_cfg_val = + (bit_width_cfg_val & + ~QAIF_AUD_INTF_BIT_WIDTH_CFG_RX_FIELDS_MASK) | + rx_bw_fields; + + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_BIT_WIDTH_CFG_REG(idx), bit_width_cfg_val); + if (ret) { + dev_err(dai->dev, + "Write to read QAIF_AUD_INTF_BIT_WIDTH_CFG_REG: %d\n", + ret); + return ret; + } + + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_ACTV_SLOT_EN_RX_REG(idx), + aif_intf_cfg->slot_en_rx_mask); + if (ret) { + dev_err(dai->dev, + "Write to read QAIF_AUD_INTF_ACTV_SLOT_EN_RX_REG: %d\n", + ret); + return ret; + } + + frame_cfg_val = (slot_width * aif_intf_cfg->bits_per_lane) - 1; + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_FRAME_CFG_REG(idx), frame_cfg_val); + if (ret) { + dev_err(dai->dev, "Failed to write QAIF_AUD_INTF_FRAME_CFG_REG: %d\n", ret); + return ret; + } + + /* Clear RX field, preserve TX field */ + mi2s_cfg_val &= ~QAIF_AUD_INTF_MI2S_CFG_RX_FIELDS_MASK; + if (channels >= 2) { + /* Set new RX mono mode value */ + mi2s_cfg_val |= + ((QAIF_AUD_INTF_CTL_STEREO << + QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_RX_SHFT) & + QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_RX_MASK); + } else { + /* Set new RX mono mode value */ + mi2s_cfg_val |= + ((QAIF_AUD_INTF_CTL_MONO << + QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_RX_SHFT) & + QAIF_AUD_INTF_MI2S_CFG_MONO_MODE_RX_MASK); + } + ret = regmap_write(drvdata->audio_qaif_map, + QAIF_AUD_INTF_MI2S_CFG_REG(idx), + mi2s_cfg_val); + if (ret) { + dev_err(dai->dev, "Write to read QAIF_AUD_INTF_MI2S_CFG_REG: %d\n", ret); + return ret; + } + dev_err(dai->dev, "%s: RX bw_cfg=%x slot_en=%x frame=%x mi2s=%x\n", + __func__, bit_width_cfg_val, + aif_intf_cfg->slot_en_rx_mask, + frame_cfg_val, mi2s_cfg_val); + } + + if (ret) { + dev_err(dai->dev, "error writing to aif_intfctl channels mode: %d\n", + ret); + return ret; + } + + ret = clk_set_rate(drvdata->mi2s_bit_clk[idx], + rate * slot_width * aif_intf_cfg->bits_per_lane); + if (ret) { + dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n", + rate * slot_width * aif_intf_cfg->bits_per_lane, ret); + return ret; + } + dev_dbg(dai->dev, "setting IBIT clock to %u\n", + rate * slot_width * aif_intf_cfg->bits_per_lane); + + if (!drvdata->mi2s_was_prepared[idx]) { + ret = clk_enable(drvdata->mi2s_bit_clk[idx]); + if (ret) { + dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); + return ret; + } + drvdata->mi2s_was_prepared[idx] = true; + + dev_dbg(rtd->card->dev, "%s: substream = %s stream = %d\n", + __func__, substream->name, substream->stream); + snd_soc_dai_set_tdm_slot(codec_dai, 0x0f, 0b11, + aif_intf_cfg->bits_per_lane, slot_width); + snd_soc_dai_set_sysclk(codec_dai, 0, + rate * aif_intf_cfg->bits_per_lane * slot_width, + 0); + } + + return 0; +} + +const struct snd_soc_dai_ops asoc_qcom_qaif_aif_cpu_dai_ops = { + .probe = qaif_aif_cpu_daiops_probe, + .set_sysclk = qaif_aif_cpu_daiops_set_sysclk, + .startup = qaif_aif_cpu_daiops_startup, + .shutdown = qaif_aif_cpu_daiops_shutdown, + .hw_params = qaif_aif_cpu_daiops_hw_params, + .trigger = qaif_aif_cpu_daiops_trigger, + .prepare = qaif_aif_cpu_daiops_prepare, +}; +EXPORT_SYMBOL_GPL(asoc_qcom_qaif_aif_cpu_dai_ops); + +static int asoc_qcom_of_xlate_dai_name(struct snd_soc_component *component, + const struct of_phandle_args *args, + const char **dai_name) +{ + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + struct qaif_variant *v = drvdata->variant; + int id = args->args[0]; + int ret = -EINVAL; + int i; + + for (i = 0; i < v->num_dai; i++) { + if (v->dai_driver[i].id == id) { + *dai_name = v->dai_driver[i].name; + ret = 0; + break; + } + } + + return ret; +} + +static const struct snd_soc_component_driver qaif_cpu_comp_driver = { + .name = "qaif-cpu", + .of_xlate_dai_name = asoc_qcom_of_xlate_dai_name, + .legacy_dai_naming = 1, +}; + +static bool __audio_qaif_regmap_accessible(struct device *dev, unsigned int reg, bool rw) +{ + struct qaif_drv_data *drvdata = dev_get_drvdata(dev); + struct qaif_variant *v = drvdata->variant; + int i; + + if (reg == QAIF_EE_OVERLAP_IRQ_EN_REG) + return true; + if (reg == QAIF_EE_OVERLAP_IRQ_RAW_STATUS_REG) + return true; + if (reg == QAIF_EE_OVERLAP_IRQ_CLEAR_REG) + return true; + if (reg == QAIF_EE_OVERLAP_IRQ_FORCE_REG) + return true; + + for (i = 0; i < DMA_TYPE_MAX; i++) { + //RDDMA IRQ + if (reg == QAIF_EE_RDDMA_PERIOD_IRQ_EN_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_PERIOD_IRQ_FORCE_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_UNDERFLOW_IRQ_EN_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_UNDERFLOW_IRQ_FORCE_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_ERR_RSP_IRQ_EN_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_ERR_RSP_IRQ_FORCE_REG(v, i)) + return true; + + //WRDMA IRQ + if (reg == QAIF_EE_WRDMA_PERIOD_IRQ_EN_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_PERIOD_IRQ_FORCE_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_OVERFLOW_IRQ_EN_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_OVERFLOW_IRQ_FORCE_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_ERR_RSP_IRQ_EN_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_ERR_RSP_IRQ_FORCE_REG(v, i)) + return true; + } + + for (i = 0; i < v->num_rddma; i++) { + if (reg == QAIF_RDDMA_CTL_REG(v, i)) + return true; + if (reg == QAIF_RDDMA_CFG_REG(v, i)) + return true; + if (reg == QAIF_RDDMA_BASE_ADDR_REG(v, i)) + return true; + if (reg == QAIF_RDDMA_BUFF_LEN_REG(v, i)) + return true; + if (reg == QAIF_RDDMA_PERIOD_LEN_REG(v, i)) + return true; + if (rw == QAIF_AIF_REG_READ) { + if (reg == QAIF_RDDMA_CURR_ADDR_REG(v, i)) + return true; + if (reg == QAIF_RDDMA_PERIOD_CNT_REG(v, i)) + return true; + } + } + + for (i = 0; i < v->num_wrdma; i++) { + if (reg == QAIF_WRDMA_CTL_REG(v, i)) + return true; + if (reg == QAIF_WRDMA_CFG_REG(v, i)) + return true; + if (reg == QAIF_WRDMA_BASE_ADDR_REG(v, i)) + return true; + if (reg == QAIF_WRDMA_BUFF_LEN_REG(v, i)) + return true; + if (reg == QAIF_WRDMA_PERIOD_LEN_REG(v, i)) + return true; + if (rw == QAIF_AIF_REG_READ) { + if (reg == QAIF_WRDMA_CURR_ADDR_REG(v, i)) + return true; + if (reg == QAIF_WRDMA_PERIOD_CNT_REG(v, i)) + return true; + } + } + + for (i = 0; i < v->num_codec_rddma; i++) { + if (reg == QAIF_CODEC_RDDMA_CTL_REG(v, i)) + return true; + if (reg == QAIF_CODEC_RDDMA_CFG_REG(v, i)) + return true; + if (reg == QAIF_CODEC_RDDMA_BASE_ADDR_REG(v, i)) + return true; + if (reg == QAIF_CODEC_RDDMA_BUFF_LEN_REG(v, i)) + return true; + if (reg == QAIF_CODEC_RDDMA_PERIOD_LEN_REG(v, i)) + return true; + if (rw == QAIF_AIF_REG_READ) { + if (reg == QAIF_CODEC_RDDMA_CURR_ADDR_REG(v, i)) + return true; + if (reg == QAIF_CODEC_RDDMA_PERIOD_CNT_REG(v, i)) + return true; + } + } + + for (i = 0; i < v->num_codec_wrdma; i++) { + if (reg == QAIF_CODEC_WRDMA_CTL_REG(v, i)) + return true; + if (reg == QAIF_CODEC_WRDMA_CFG_REG(v, i)) + return true; + if (reg == QAIF_CODEC_WRDMA_BASE_ADDR_REG(v, i)) + return true; + if (reg == QAIF_CODEC_WRDMA_BUFF_LEN_REG(v, i)) + return true; + if (reg == QAIF_CODEC_WRDMA_PERIOD_LEN_REG(v, i)) + return true; + if (rw == QAIF_AIF_REG_READ) { + if (reg == QAIF_CODEC_WRDMA_CURR_ADDR_REG(v, i)) + return true; + if (reg == QAIF_CODEC_WRDMA_PERIOD_CNT_REG(v, i)) + return true; + } + } + return true; +} + +static bool audio_qaif_regmap_writeable(struct device *dev, unsigned int reg) +{ + return __audio_qaif_regmap_accessible(dev, reg, QAIF_AIF_REG_WRITE); +} + +static bool audio_qaif_regmap_readable(struct device *dev, unsigned int reg) +{ + return __audio_qaif_regmap_accessible(dev, reg, QAIF_AIF_REG_READ); +} + +static bool audio_qaif_regmap_volatile(struct device *dev, unsigned int reg) +{ + struct qaif_drv_data *drvdata = dev_get_drvdata(dev); + struct qaif_variant *v = drvdata->variant; + int i; + + for (i = 0; i < DMA_TYPE_MAX; i++) { + //RDDMA IRQ + if (reg == QAIF_EE_RDDMA_PERIOD_IRQ_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_PERIOD_IRQ_RAW_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_UNDERFLOW_IRQ_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_UNDERFLOW_IRQ_RAW_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_ERR_RSP_IRQ_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_ERR_RSP_IRQ_RAW_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, i)) + return true; + + //WRDMA IRQ + if (reg == QAIF_EE_WRDMA_PERIOD_IRQ_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_PERIOD_IRQ_RAW_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_PERIOD_IRQ_CLR_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_OVERFLOW_IRQ_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_OVERFLOW_IRQ_RAW_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_OVERFLOW_IRQ_CLR_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_ERR_RSP_IRQ_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_ERR_RSP_IRQ_RAW_STAT_REG(v, i)) + return true; + if (reg == QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, i)) + return true; + } + + for (i = 0; i < v->num_rddma; i++) + if (reg == QAIF_RDDMA_CURR_ADDR_REG(v, i)) + return true; + + for (i = 0; i < v->num_wrdma; i++) + if (reg == QAIF_WRDMA_CURR_ADDR_REG(v, i)) + return true; + + for (i = 0; i < v->num_codec_rddma; i++) + if (reg == QAIF_CODEC_RDDMA_CURR_ADDR_REG(v, i)) + return true; + + for (i = 0; i < v->num_codec_wrdma; i++) + if (reg == QAIF_CODEC_WRDMA_CURR_ADDR_REG(v, i)) + return true; + + return true; +} + +static struct regmap_config audio_qaif_regmap_config = { + .name = "audio_qaif_cpu", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .writeable_reg = audio_qaif_regmap_writeable, + .readable_reg = audio_qaif_regmap_readable, + .volatile_reg = audio_qaif_regmap_volatile, + .cache_type = REGCACHE_FLAT, +}; + +static int of_qaif_parse_aif_intf_cfg(struct device *dev, + struct qaif_drv_data *data) +{ + struct qaif_variant *v = data->variant; + struct device_node *np = dev->of_node; + struct device_node *intf_np; + struct qaif_aif_config *cfg; + const __be32 *lane_cfg_prop; + int num_interfaces, ret, i, j; + int lane_cfg_len; + int dai_id, intf_idx; + + if (!v) { + dev_err(dev, "No variant data\n"); + return -EINVAL; + } + /* Get count of interface phandles from aif-interface property */ + num_interfaces = of_count_phandle_with_args(np, "aif-interface", NULL); + if (num_interfaces <= 0) { + dev_err(dev, "No aif-interface property found or invalid: %d\n", num_interfaces); + return -EINVAL; + } + + if (num_interfaces > QAIF_MAX_AIF_CFG_CNT) { + dev_warn(dev, "Too many interfaces (%d), limiting to %d\n", + num_interfaces, QAIF_MAX_AIF_CFG_CNT); + num_interfaces = QAIF_MAX_AIF_CFG_CNT; + } + + dev_info(dev, "Found %d AIF interfaces to parse\n", num_interfaces); + + /* Parse each interface node */ + for (i = 0; i < num_interfaces; i++) { + intf_np = of_parse_phandle(np, "aif-interface", i); + if (!intf_np) { + dev_err(dev, "Failed to get interface node %d\n", i); + continue; + } + + dev_dbg(dev, "Parsing interface %d: %s\n", i, intf_np->name); + + ret = of_property_read_u32(intf_np, "qcom,qaif-intf-dai-id", &dai_id); + if (ret) { + dev_err(dev, "Missing dai-id for interface %d: %s ===\n", i, intf_np->name); + continue; + } + + if (v->get_dma_idx) { + intf_idx = v->get_dma_idx(dai_id); + if (intf_idx < 0) { + dev_err(dev, + "invalid intf idx for : %d: %s ===\n", + i, intf_np->name); + continue; + } + } else { + dev_err(dev, "can not get intf idx for : %d: %s ===\n", i, intf_np->name); + return -EINVAL; + } + cfg = &v->aif_intf_cfg[intf_idx]; + + /* Parse sync configuration */ + ret = of_property_read_u32(intf_np, "qcom,qaif-aif-sync-mode", &cfg->sync_mode); + if (ret) { + dev_warn(dev, "Missing sync-mode for interface %d\n", i); + cfg->sync_mode = 0; + } + + ret = of_property_read_u32(intf_np, "qcom,qaif-aif-sync-src", &cfg->sync_src); + if (ret) { + dev_warn(dev, "Missing sync-src for interface %d\n", i); + cfg->sync_src = 0; + } + + ret = of_property_read_u32(intf_np, "qcom,qaif-aif-invert-sync", &cfg->invert_sync); + if (ret) { + dev_warn(dev, "Missing invert-sync for interface %d\n", i); + cfg->invert_sync = 0; + } + + ret = of_property_read_u32(intf_np, "qcom,qaif-aif-sync-delay", &cfg->sync_delay); + if (ret) { + dev_warn(dev, "Missing sync-delay for interface %d\n", i); + cfg->sync_delay = 0; + } + + /* Parse slot and sample width configuration */ + ret = of_property_read_u32(intf_np, + "qcom,qaif-aif-slot-width-rx", + &cfg->slot_width_rx); + if (ret) { + dev_warn(dev, "Missing slot-width-rx for interface %d\n", i); + cfg->slot_width_rx = 0; + } + + ret = of_property_read_u32(intf_np, + "qcom,qaif-aif-slot-width-tx", + &cfg->slot_width_tx); + if (ret) { + dev_warn(dev, "Missing slot-width-tx for interface %d\n", i); + cfg->slot_width_tx = 0; + } + + ret = of_property_read_u32(intf_np, + "qcom,qaif-aif-sample-width-rx", + &cfg->sample_width_rx); + if (ret) { + dev_warn(dev, "Missing sample-width-rx for interface %d\n", i); + cfg->sample_width_rx = 0; + } + + ret = of_property_read_u32(intf_np, + "qcom,qaif-aif-sample-width-tx", + &cfg->sample_width_tx); + if (ret) { + dev_warn(dev, "Missing sample-width-tx for interface %d\n", i); + cfg->sample_width_tx = 0; + } + + /* Parse slot enable masks */ + ret = of_property_read_u32(intf_np, + "qcom,qaif-aif-slot-en-rx-mask", + &cfg->slot_en_rx_mask); + if (ret) { + dev_warn(dev, "Missing slot-en-rx-mask for interface %d\n", i); + cfg->slot_en_rx_mask = 0; + } + + ret = of_property_read_u32(intf_np, + "qcom,qaif-aif-slot-en-tx-mask", + &cfg->slot_en_tx_mask); + if (ret) { + dev_warn(dev, "Missing slot-en-tx-mask for interface %d\n", i); + cfg->slot_en_tx_mask = 0; + } + + /* Parse control configuration */ + ret = of_property_read_u32(intf_np, "qcom,qaif-aif-loopback-en", &cfg->loopback_en); + if (ret) { + dev_warn(dev, "Missing loopback-en for interface %d\n", i); + cfg->loopback_en = 0; + } + + ret = of_property_read_u32(intf_np, + "qcom,qaif-aif-ctrl-data-oe", + &cfg->ctrl_data_oe); + if (ret) { + dev_warn(dev, "Missing ctrl-data-oe for interface %d\n", i); + cfg->ctrl_data_oe = 0; + } + + /* Parse lane configuration */ + lane_cfg_prop = of_get_property(intf_np, + "qcom,qaif-aif-lane-config", + &lane_cfg_len); + if (lane_cfg_prop) { + /* Each lane config has 2 u32 values: enable and direction */ + cfg->num_lanes = lane_cfg_len / (2 * sizeof(u32)); + if (cfg->num_lanes > QAIF_MAX_LANES) { + dev_warn(dev, "Too many lanes (%d), limiting to %d\n", + cfg->num_lanes, QAIF_MAX_LANES); + cfg->num_lanes = QAIF_MAX_LANES; + } + + for (j = 0; j < cfg->num_lanes; j++) { + cfg->lane_cfg[j].enable = + be32_to_cpup(lane_cfg_prop + (j * 2)); + if (cfg->lane_cfg[j].enable) + cfg->lane_en_mask |= BIT(j); + /* Set bit j for lane enable */ + + cfg->lane_cfg[j].direction = + be32_to_cpup(lane_cfg_prop + (j * 2 + 1)); + if (cfg->lane_cfg[j].direction) + cfg->lane_dir_mask |= BIT(j); + /* Set bit j for RX direction */ + } + + } else { + dev_warn(dev, "Missing lane-config for interface %d\n", i); + cfg->num_lanes = 0; + } + + /* Parse mono/stereo mode */ + ret = of_property_read_u32(intf_np, + "qcom,qaif-aif-mono-mode-tx", + &cfg->mono_mode_tx); + if (ret) { + dev_warn(dev, "Missing mono-mode-tx for interface %d\n", i); + cfg->mono_mode_tx = 0; + } + + ret = of_property_read_u32(intf_np, + "qcom,qaif-aif-mono-mode-rx", + &cfg->mono_mode_rx); + if (ret) { + dev_warn(dev, "Missing mono-mode-rx for interface %d\n", i); + cfg->mono_mode_rx = 0; + } + + /* Parse frame configuration */ + ret = of_property_read_u32(intf_np, + "qcom,qaif-aif-full-cycle-en", + &cfg->full_cycle_en); + if (ret) { + dev_warn(dev, "Missing full-cycle-en for interface %d\n", i); + cfg->full_cycle_en = 0; + } + + ret = of_property_read_u32(intf_np, + "qcom,qaif-aif-bits-per-lane", + &cfg->bits_per_lane); + if (ret) { + dev_warn(dev, "Missing bits-per-lane for interface %d\n", i); + cfg->bits_per_lane = 0; + } + + /* Debug dump of parsed properties. */ + dev_dbg(dev, "Interface %d configuration:\n", i); + dev_dbg(dev, " Node name: %s\n", intf_np->name); + + /* Sync configuration */ + dev_dbg(dev, " Sync Configuration:\n"); + dev_dbg(dev, " sync_mode = %u\n", cfg->sync_mode); + dev_dbg(dev, " sync_src = %u\n", cfg->sync_src); + dev_dbg(dev, " invert_sync = %u\n", cfg->invert_sync); + dev_dbg(dev, " sync_delay = %u\n", cfg->sync_delay); + + /* Slot and sample width */ + dev_dbg(dev, " Width Configuration:\n"); + dev_dbg(dev, " slot_width_rx = %u\n", cfg->slot_width_rx); + dev_dbg(dev, " slot_width_tx = %u\n", cfg->slot_width_tx); + dev_dbg(dev, " sample_width_rx= %u\n", cfg->sample_width_rx); + dev_dbg(dev, " sample_width_tx= %u\n", cfg->sample_width_tx); + + /* Slot enable masks */ + dev_dbg(dev, " Slot Enable Masks:\n"); + dev_dbg(dev, " slot_en_rx_mask= 0x%08X\n", cfg->slot_en_rx_mask); + dev_dbg(dev, " slot_en_tx_mask= 0x%08X\n", cfg->slot_en_tx_mask); + + /* Control configuration */ + dev_dbg(dev, " Control Configuration:\n"); + dev_dbg(dev, " loopback_en = %u\n", cfg->loopback_en); + dev_dbg(dev, " ctrl_data_oe = %u\n", cfg->ctrl_data_oe); + + /* Lane configuration */ + dev_dbg(dev, " Lane Configuration (num_lanes=%u):\n", cfg->num_lanes); + for (j = 0; j < cfg->num_lanes; j++) { + dev_dbg(dev, " Lane %d: enable=%u, direction=%u (%s)\n", + j, cfg->lane_cfg[j].enable, cfg->lane_cfg[j].direction, + cfg->lane_cfg[j].direction ? "RX_MIC" : "TX_SPKR"); + } + dev_dbg(dev, " Lane Configuration (lane_en_mask=0x%x):\n", cfg->lane_en_mask); + dev_dbg(dev, " Lane Configuration (lane_dir_mask=0x%x):\n", cfg->lane_dir_mask); + + /* Mono/Stereo mode */ + dev_dbg(dev, " Mono/Stereo Mode:\n"); + dev_dbg(dev, " mono_mode_tx = %u (%s)\n", + cfg->mono_mode_tx, cfg->mono_mode_tx ? "MONO" : "STEREO"); + dev_dbg(dev, " mono_mode_rx = %u (%s)\n", + cfg->mono_mode_rx, cfg->mono_mode_rx ? "MONO" : "STEREO"); + + /* Frame configuration */ + dev_dbg(dev, " Frame Configuration:\n"); + dev_dbg(dev, " full_cycle_en = %u\n", cfg->full_cycle_en); + dev_dbg(dev, " bits_per_lane = %u\n", cfg->bits_per_lane); + + dev_dbg(dev, "End interface %d\n", i); + + of_node_put(intf_np); + } + + dev_info(dev, "Successfully parsed %d AIF interfaces\n", num_interfaces); + return 0; +} + +static int of_qaif_cdc_dma_clks_parse(struct device *dev, + struct qaif_drv_data *data) +{ + data->aud_dma_clk = devm_clk_get(dev, "audio_core_cc_aud_dma_clk"); + if (IS_ERR(data->aud_dma_clk)) + return PTR_ERR(data->aud_dma_clk); + + data->aud_dma_mem_clk = devm_clk_get(dev, "audio_core_cc_aud_dma_mem_clk"); + if (IS_ERR(data->aud_dma_mem_clk)) + return PTR_ERR(data->aud_dma_mem_clk); + + return 0; +} + +int asoc_qcom_qaif_cpu_platform_probe(struct platform_device *pdev) +{ + struct qaif_drv_data *drvdata; + struct resource *res; + struct qaif_variant *variant; + struct device *dev = &pdev->dev; + const struct of_device_id *match; + int ret, i, dai_id, idx; + bool variant_init_done = false; + + drvdata = devm_kzalloc(dev, sizeof(struct qaif_drv_data), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + platform_set_drvdata(pdev, drvdata); + + match = of_match_device(dev->driver->of_match_table, dev); + if (!match || !match->data) + return -EINVAL; + + drvdata->variant = (struct qaif_variant *)match->data; + variant = drvdata->variant; + if (!variant) { + dev_err(dev, "No variant data\n"); + return -EINVAL; + } + + ret = of_qaif_parse_aif_intf_cfg(dev, drvdata); + if (ret) { + dev_err(dev, "Failed to parse aif interfaces: %d\n", ret); + return -EINVAL; + } + + drvdata->audio_qaif = + devm_platform_ioremap_resource_byname(pdev, "audio-qaif-core"); + if (IS_ERR(drvdata->audio_qaif)) + return PTR_ERR(drvdata->audio_qaif); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-qaif-core"); + if (!res) + return -EINVAL; + + audio_qaif_regmap_config.max_register = resource_size(res); + + drvdata->audio_qaif_map = devm_regmap_init_mmio(dev, drvdata->audio_qaif, + &audio_qaif_regmap_config); + if (IS_ERR(drvdata->audio_qaif_map)) + return PTR_ERR(drvdata->audio_qaif_map); + + ret = of_qaif_cdc_dma_clks_parse(dev, drvdata); + if (ret) { + dev_err(dev, "failed to get cdc dma clocks %d\n", ret); + return ret; + } + + if (variant->init) { + ret = variant->init(pdev); + if (ret) { + dev_err(dev, "error initializing variant: %d\n", ret); + return ret; + } + variant_init_done = true; + } + + for (i = 0; i < variant->num_dai; i++) { + dai_id = variant->dai_driver[i].id; + if (is_cif_dma_port(dai_id)) + continue; + idx = variant->get_dma_idx(dai_id); + if (idx < 0) + continue; + + drvdata->mi2s_bit_clk[idx] = devm_clk_get(dev, + variant->dai_bit_clk_names[idx]); + if (IS_ERR(drvdata->mi2s_bit_clk[idx])) { + dev_err(dev, + "error getting %s: %ld\n", + variant->dai_bit_clk_names[idx], + PTR_ERR(drvdata->mi2s_bit_clk[idx])); + ret = PTR_ERR(drvdata->mi2s_bit_clk[idx]); + goto err; + } + } + + ret = qaif_aif_cpu_init_bitfields(dev, drvdata->audio_qaif_map); + if (ret) { + dev_err(dev, "error init cif bitfield: %d\n", ret); + goto err; + } + + /* Initialize bitfields for dai AIF CFG register */ + ret = qaif_aif_cfg_cpu_init_bitfields(dev, drvdata->audio_qaif_map); + if (ret) { + dev_err(dev, "error init aif_intfctl field: %d\n", ret); + goto err; + } + + ret = qaif_cif_cpu_init_bitfields(dev, drvdata->audio_qaif_map); + if (ret) { + dev_err(dev, "error init cif bitfield: %d\n", ret); + goto err; + } + + ret = devm_snd_soc_register_component(dev, &qaif_cpu_comp_driver, + variant->dai_driver, + variant->num_dai); + if (ret) { + dev_err(dev, "error registering cpu driver: %d\n", ret); + goto err; + } + + ret = asoc_qcom_qaif_platform_register(pdev); + if (ret) { + dev_err(dev, "error registering platform driver: %d\n", ret); + goto err; + } + dev_info(&pdev->dev, "%s: QAIF CPU-Platform Driver Registered Successfully\n", __func__); +err: + if (ret && variant_init_done && variant->exit) + variant->exit(pdev); + return ret; +} +EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_probe); + +void asoc_qcom_qaif_cpu_platform_remove(struct platform_device *pdev) +{ + struct qaif_drv_data *drvdata = platform_get_drvdata(pdev); + + if (drvdata->variant->exit) + drvdata->variant->exit(pdev); +} +EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_remove); + +void asoc_qcom_qaif_cpu_platform_shutdown(struct platform_device *pdev) +{ + struct qaif_drv_data *drvdata = platform_get_drvdata(pdev); + + if (drvdata->variant->exit) + drvdata->variant->exit(pdev); +} +EXPORT_SYMBOL_GPL(asoc_qcom_qaif_cpu_platform_shutdown); + +MODULE_DESCRIPTION("QTi QAIF CPU Driver"); +MODULE_LICENSE("GPL"); From 276eab8ba7f458ea779a507bb9ce7a6c86292a40 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Fri, 29 May 2026 11:46:24 +0530 Subject: [PATCH 4/5] ASoC: qcom: Add QAIF platform driver Add platform driver for the Qualcomm Technologies Qualcomm Audio Interface (QAIF) ports. Signed-off-by: Mohammad Rafi Shaik --- sound/soc/qcom/qaif-platform.c | 1219 ++++++++++++++++++++++++++++++++ 1 file changed, 1219 insertions(+) create mode 100644 sound/soc/qcom/qaif-platform.c diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c new file mode 100644 index 0000000000000..545c2fd857d20 --- /dev/null +++ b/sound/soc/qcom/qaif-platform.c @@ -0,0 +1,1219 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * qaif-platform.c -- ALSA SoC CPU-Platform DAI driver for QTi QAIF + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qaif-reg.h" +#include "qaif.h" + +#define DRV_NAME "qaif-platform" + +/* 20 ms @ 48kHz S16 stereo = 3840 bytes */ +#define QAIF_PLATFORM_BUFFER_MIN_SIZE (960 * 2 * 2) +/* min period = 960 frames @ S16 stereo = 3840 bytes */ +#define QAIF_PLATFORM_PERIOD_BYTES_MIN (960 * 2 * 2) +/* 80 ms = 15360 bytes */ +#define QAIF_PLATFORM_BUFFER_SIZE (4 * QAIF_PLATFORM_BUFFER_MIN_SIZE) +#define QAIF_PLATFORM_PERIODS_MIN 2 +#define QAIF_PLATFORM_PERIODS_MAX 4 // 4 × 3840 = 15360 = buffer_bytes_max + +#define QAIF_SMMU_SID_OFFSET 32 + +static irqreturn_t qaif_aif_irq_handler(struct qaif_drv_data *drvdata, u32 summary_irq_status); +static irqreturn_t qaif_cif_irq_handler(struct qaif_drv_data *drvdata, u32 summary_irq_status); +//static irqreturn_t qaif_aud_inf_handler(struct qaif_drv_data *drvdata, u32 summary_irq_status); + +static int qaif_init(struct snd_soc_component *component); + +static const struct snd_pcm_hardware qaif_platform_aif_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = QAIF_PLATFORM_BUFFER_SIZE, + .period_bytes_min = QAIF_PLATFORM_PERIOD_BYTES_MIN, + .period_bytes_max = QAIF_PLATFORM_BUFFER_SIZE / QAIF_PLATFORM_PERIODS_MIN, + .periods_min = QAIF_PLATFORM_PERIODS_MIN, + .periods_max = QAIF_PLATFORM_PERIODS_MAX, + .fifo_size = 0, +}; + +static const struct snd_pcm_hardware qaif_platform_cif_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S24 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_8000_192000, + .rate_min = 8000, + .rate_max = 192000, + .channels_min = 1, + .channels_max = 8, + .buffer_bytes_max = QAIF_PLATFORM_BUFFER_SIZE, + .period_bytes_min = QAIF_PLATFORM_PERIOD_BYTES_MIN, + .period_bytes_max = QAIF_PLATFORM_BUFFER_SIZE / QAIF_PLATFORM_PERIODS_MIN, + .periods_min = QAIF_PLATFORM_PERIODS_MIN, + .periods_max = QAIF_PLATFORM_PERIODS_MAX, + .fifo_size = 0, +}; + +static const struct qaif_irq_map qaif_irq_clients[] = { + { QAIF_CLIENT_ID_AIF_DMA, QAIF_BITMASK_AIF_RDDMA_WRDMA, qaif_aif_irq_handler}, + { QAIF_CLIENT_ID_CIF_DMA, QAIF_BITMASK_CIF_RDDMA_WRDMA, qaif_cif_irq_handler}, + { QAIF_CLIENT_ID_AUD_INF, QAIF_BITMASK_AUD_INF, NULL}, +}; + +static const u32 QAIF_ALL_CLIENTS_MASK = + QAIF_BITMASK_AIF_RDDMA_WRDMA | + QAIF_BITMASK_CIF_RDDMA_WRDMA | + QAIF_BITMASK_AUD_INF; + +static struct qaif_dma_mem_info *qaif_mem_alloc_attach(struct snd_soc_component *component, + size_t alloc_size) +{ + struct device *dev = component->dev; + struct qaif_dma_mem_info *dma_mem_info; + + dma_mem_info = kzalloc_obj(*dma_mem_info, GFP_KERNEL); + if (!dma_mem_info) + return NULL; + + dma_mem_info->alloc_size = alloc_size; + + /* + * dma_alloc_coherent: allocates cache-coherent memory, returns + * CPU virtual address and fills dma_addr with the DMA/IOVA address. + */ + dma_mem_info->vaddr = dma_alloc_coherent(dev, alloc_size, + &dma_mem_info->dma_addr, + GFP_KERNEL); + if (!dma_mem_info->vaddr) { + dev_err(dev, "dma_alloc_coherent failed for %zu bytes\n", alloc_size); + kfree(dma_mem_info); + return NULL; + } + + dev_dbg(dev, "%s: dma_addr=%llx vaddr=%p\n", __func__, + dma_mem_info->dma_addr, dma_mem_info->vaddr); + return dma_mem_info; +} + +static void qaif_mem_dealloc_detach(struct device *dev, + struct qaif_dma_mem_info *dma_info) +{ + if (!dma_info) + return; + + if (dma_info->vaddr) + dma_free_coherent(dev, dma_info->alloc_size, + dma_info->vaddr, dma_info->dma_addr); + + kfree(dma_info); +} + +static struct qaif_dmactl *qaif_get_dmactl_handle(const struct snd_pcm_substream *substream, + struct snd_soc_component *component) +{ + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + struct qaif_dmactl *dmactl = NULL; + struct qaif_variant *v = drvdata->variant; + + switch (cpu_dai->driver->id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + case MI2S_SENARY: + case MI2S_SEPTENARY: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dmactl = v->aif_rd_dmactl; + else + dmactl = v->aif_wr_dmactl; + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + dmactl = v->cif_rd_dmactl; + break; + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + dmactl = v->cif_wr_dmactl; + break; + } + + return dmactl; +} + +static int qaif_platform_pcmops_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + struct snd_dma_buffer *buf; + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + const struct qaif_variant *v = drvdata->variant; + int ret, stream_dma_idx, dir = substream->stream; + struct qaif_pcm_data *data; + struct qaif_dmactl *dmactl; + struct qaif_dma_mem_info *dma_mem_info; + struct regmap *map; + unsigned int dai_id = cpu_dai->driver->id; + + pr_err("%s:%d: dai_id=%u stream=%d\n", + __func__, __LINE__, dai_id, dir); + + if (v->alloc_stream_dma_idx) + stream_dma_idx = v->alloc_stream_dma_idx(drvdata, dir, dai_id); + else + return -EINVAL; + + if (stream_dma_idx < 0) + return stream_dma_idx; + data = kzalloc_obj(*data, GFP_KERNEL); + if (!data) + return -ENOMEM; + + //data->i2s_port = cpu_dai->driver->id; + data->stream_dma_idx = stream_dma_idx; + + runtime->private_data = data; + map = drvdata->audio_qaif_map; + dmactl = qaif_get_dmactl_handle(substream, component); + if (!dmactl) { + kfree(data); + return -EINVAL; + } + buf = &substream->dma_buffer; + buf->dev.dev = component->dev; + buf->private_data = NULL; + /* Assign DMA buffer pointers */ + buf->dev.type = SNDRV_DMA_TYPE_CONTINUOUS; + + dma_mem_info = qaif_mem_alloc_attach(component, + qaif_platform_aif_hardware.buffer_bytes_max); + if (!dma_mem_info) + return -ENOMEM; + + clk_prepare_enable(drvdata->aud_dma_clk); + clk_prepare_enable(drvdata->aud_dma_mem_clk); + + ret = qaif_init(component); + if (ret) { + dev_err(soc_runtime->dev, "qaif_init failed: %d\n", ret); + return -EINVAL; + } + drvdata->qaif_init_ref_cnt++; + + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + case MI2S_SENARY: + case MI2S_SEPTENARY: + drvdata->aif_substream[stream_dma_idx] = substream; + drvdata->aif_dma_heap[stream_dma_idx] = dma_mem_info; + buf->bytes = qaif_platform_aif_hardware.buffer_bytes_max; + buf->addr = drvdata->aif_dma_heap[stream_dma_idx]->dma_addr; + buf->area = (unsigned char *)drvdata->aif_dma_heap[stream_dma_idx]->vaddr; + + snd_soc_set_runtime_hwparams(substream, &qaif_platform_aif_hardware); + runtime->dma_bytes = qaif_platform_aif_hardware.buffer_bytes_max; + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + drvdata->cif_substream[stream_dma_idx] = substream; + drvdata->cif_dma_heap[stream_dma_idx] = dma_mem_info; + buf->bytes = qaif_platform_cif_hardware.buffer_bytes_max; + buf->addr = drvdata->cif_dma_heap[stream_dma_idx]->dma_addr; + buf->area = (unsigned char *)drvdata->cif_dma_heap[stream_dma_idx]->vaddr; + + snd_soc_set_runtime_hwparams(substream, &qaif_platform_cif_hardware); + runtime->dma_bytes = qaif_platform_cif_hardware.buffer_bytes_max; + break; + default: + break; + } + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + kfree(data); + dev_err(soc_runtime->dev, "setting constraints failed: %d\n", + ret); + return -EINVAL; + } + dev_dbg(soc_runtime->dev, + "%s: runtime info - dma_area=%p, dma_addr=0x%llx, dma_bytes=%zu\n", + __func__, + runtime->dma_area, + (unsigned long long)runtime->dma_addr, + runtime->dma_bytes); + pr_err("%s:%d: stream_dma_idx=%d qaif_init_ref_cnt=%d\n", + __func__, __LINE__, stream_dma_idx, drvdata->qaif_init_ref_cnt); + + return 0; +} + +static int qaif_platform_pcmops_close(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + const struct qaif_variant *v = drvdata->variant; + struct qaif_pcm_data *data; + unsigned int dai_id = cpu_dai->driver->id; + + data = runtime->private_data; + pr_err("%s:%d: dai_id=%u stream=%d stream_dma_idx=%d\n", + __func__, __LINE__, dai_id, substream->stream, + data ? data->stream_dma_idx : -1); + + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + case MI2S_SENARY: + case MI2S_SEPTENARY: + drvdata->aif_substream[data->stream_dma_idx] = NULL; + qaif_mem_dealloc_detach(component->dev, + drvdata->aif_dma_heap[data->stream_dma_idx]); + drvdata->aif_dma_heap[data->stream_dma_idx] = NULL; + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + drvdata->cif_substream[data->stream_dma_idx] = NULL; + qaif_mem_dealloc_detach(component->dev, + drvdata->cif_dma_heap[data->stream_dma_idx]); + drvdata->cif_dma_heap[data->stream_dma_idx] = NULL; + break; + default: + break; + } + + if (drvdata->qaif_init_ref_cnt > 0) + drvdata->qaif_init_ref_cnt--; + else + dev_dbg(component->dev, "%s: QAIF init ref cnt: %d, skipping decrement\n", + __func__, drvdata->qaif_init_ref_cnt); + + if (v->free_stream_dma_idx) + v->free_stream_dma_idx(drvdata, data->stream_dma_idx, dai_id); + clk_disable_unprepare(drvdata->aud_dma_clk); + clk_disable_unprepare(drvdata->aud_dma_mem_clk); + kfree(data); + pr_err("%s:%d: done qaif_init_ref_cnt=%d\n", + __func__, __LINE__, drvdata->qaif_init_ref_cnt); + return 0; +} + +static int qaif_platform_pcmops_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + const struct qaif_variant *v = drvdata->variant; + struct qaif_dmactl *dmactl; + unsigned int dai_id = cpu_dai->driver->id; + int idx; + int ret; + + pr_err("%s:%d: dai_id=%u stream=%d channels=%u rate=%u\n", + __func__, __LINE__, dai_id, substream->stream, + params_channels(params), params_rate(params)); + + dmactl = qaif_get_dmactl_handle(substream, component); + if (!dmactl) + return -EINVAL; + idx = v->get_dma_idx(dai_id); + + if (idx < 0) { + dev_err(soc_runtime->dev, "%s: Invalid DMA index: %d\n", __func__, idx); + return -EINVAL; + } + + ret = regmap_fields_write(dmactl->burst4, idx, QAIF_DMACTL_BURSTEN); + if (ret) { + dev_err(soc_runtime->dev, "error updating burst4 field: %d\n", ret); + return ret; + } + + ret = regmap_fields_write(dmactl->shram_wm, idx, QAIF_DMACTL_WM_5); + if (ret) { + dev_err(soc_runtime->dev, "error updating shram_wm field: %d\n", ret); + return ret; + } + + pr_err("%s:%d: configured idx=%d\n", __func__, __LINE__, idx); + return 0; +} + +static int qaif_platform_pcmops_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + const struct qaif_variant *v = drvdata->variant; + unsigned int reg; + int ret, idx; + unsigned int dai_id = cpu_dai->driver->id; + struct regmap *map = drvdata->audio_qaif_map; + struct qaif_dmactl *dmactl; + + pr_err("%s:%d: dai_id=%u stream=%d\n", + __func__, __LINE__, dai_id, substream->stream); + + dmactl = qaif_get_dmactl_handle(substream, component); + if (!dmactl) + return -EINVAL; + idx = v->get_dma_idx(dai_id); + + if (idx < 0) { + dev_err(soc_runtime->dev, "%s: Invalid DMA index: %d\n", __func__, idx); + return -EINVAL; + } + + ret = regmap_fields_write(dmactl->enable, idx, QAIF_DMACTL_ENABLE_OFF); + if (ret) + dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); + + reg = QAIF_DMACFG_REG(v, idx, substream->stream, dai_id); + ret = regmap_write(map, reg, 0); + if (ret) + dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); + + pr_err("%s:%d: idx=%d ret=%d\n", __func__, __LINE__, idx, ret); + return ret; +} + +static int qaif_platform_pcmops_prepare(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + const struct qaif_variant *v = drvdata->variant; + struct qaif_dmactl *dmactl; + struct regmap *map; + int bitwidth = 32;//snd_pcm_format_width(runtime->format); + unsigned int channels = runtime->channels; + unsigned int rate = runtime->rate; + int ret, idx, dir = substream->stream; + unsigned int dai_id = cpu_dai->driver->id; + + pr_err("%s:%d: dai_id=%u stream=%d rate=%u channels=%u\n", + __func__, __LINE__, dai_id, dir, rate, channels); + + dmactl = qaif_get_dmactl_handle(substream, component); + if (!dmactl) + return -EINVAL; + idx = v->get_dma_idx(dai_id); + map = drvdata->audio_qaif_map; + + if (idx < 0) { + dev_err(soc_runtime->dev, "%s: Invalid DMA index: %d\n", __func__, idx); + return -EINVAL; + } + + clk_set_rate(drvdata->aud_dma_clk, rate * bitwidth * channels * 100); + clk_set_rate(drvdata->aud_dma_mem_clk, rate * bitwidth * channels * 100); + dev_dbg(soc_runtime->dev, "setting aud_dma_clk & aud_dma_mem_clk to %u\n", + rate * bitwidth * channels * 100); + + ret = regmap_write(map, QAIF_SID_MAP_REG(dir, dai_id), + drvdata->smmu_csid_bits); + if (ret) { + dev_err(soc_runtime->dev, "error writing to SID MAP reg: %d\n", + ret); + return ret; + } + + ret = regmap_write(map, QAIF_DMABASE_REG(v, idx, dir, dai_id), + runtime->dma_addr); + if (ret) { + dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n", + ret); + return ret; + } + + ret = regmap_write(map, QAIF_DMABUFF_REG(v, idx, dir, dai_id), + (snd_pcm_lib_buffer_bytes(substream) >> 3) - 1); + if (ret) { + dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n", + ret); + return ret; + } + + ret = regmap_write(map, QAIF_DMAPER_LEN_REG(v, idx, dir, dai_id), + (snd_pcm_lib_period_bytes(substream) >> 3) - 1); + if (ret) { + dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n", + ret); + return ret; + } + + pr_err("%s:%d: idx=%d dma_addr=0x%llx buf_bytes=%zu period_bytes=%zu\n", + __func__, __LINE__, idx, (unsigned long long)runtime->dma_addr, + snd_pcm_lib_buffer_bytes(substream), + snd_pcm_lib_period_bytes(substream)); + return 0; +} + +static int qaif_platform_irq_clear(struct qaif_drv_data *drvdata, + int dir, enum qaif_irq_type_t irq_type, int idx) +{ + int ret = 0; + const struct qaif_variant *v = drvdata->variant; + struct regmap *map = drvdata->audio_qaif_map; + unsigned int val_irqclr = BIT(idx); + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + ret |= regmap_write(map, QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, irq_type), val_irqclr); + ret |= regmap_write(map, + QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, irq_type), + val_irqclr); + ret |= regmap_write(map, + QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, irq_type), + val_irqclr); + } else { + ret |= regmap_write(map, QAIF_EE_WRDMA_PERIOD_IRQ_CLR_REG(v, irq_type), val_irqclr); + ret |= regmap_write(map, + QAIF_EE_WRDMA_OVERFLOW_IRQ_CLR_REG(v, irq_type), + val_irqclr); + ret |= regmap_write(map, + QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, irq_type), + val_irqclr); + } + return ret; +} + +static int qaif_platform_irq_enable(struct qaif_drv_data *drvdata, + int dir, enum qaif_irq_type_t irq_type, int idx) +{ + int ret = 0; + const struct qaif_variant *v = drvdata->variant; + struct regmap *map = drvdata->audio_qaif_map; + unsigned int val_irqen = BIT(idx); + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + ret |= regmap_write_bits(map, + QAIF_EE_RDDMA_PERIOD_IRQ_EN_REG(v, irq_type), + val_irqen, val_irqen); + ret |= regmap_write_bits(map, + QAIF_EE_RDDMA_UNDERFLOW_IRQ_EN_REG(v, irq_type), + val_irqen, val_irqen); + ret |= regmap_write_bits(map, + QAIF_EE_RDDMA_ERR_RSP_IRQ_EN_REG(v, irq_type), + val_irqen, val_irqen); + } else { + ret |= regmap_write_bits(map, + QAIF_EE_WRDMA_PERIOD_IRQ_EN_REG(v, irq_type), + val_irqen, val_irqen); + ret |= regmap_write_bits(map, + QAIF_EE_WRDMA_OVERFLOW_IRQ_EN_REG(v, irq_type), + val_irqen, val_irqen); + ret |= regmap_write_bits(map, + QAIF_EE_WRDMA_ERR_RSP_IRQ_EN_REG(v, irq_type), + val_irqen, val_irqen); + } + return ret; +} + +static int qaif_platform_irq_disable(struct qaif_drv_data *drvdata, + int dir, enum qaif_irq_type_t irq_type, int idx) +{ + int ret = 0; + const struct qaif_variant *v = drvdata->variant; + struct regmap *map = drvdata->audio_qaif_map; + unsigned int val_irq_disable = BIT(idx); + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + ret |= regmap_write_bits(map, + QAIF_EE_RDDMA_PERIOD_IRQ_EN_REG(v, irq_type), + val_irq_disable, 0); + ret |= regmap_write_bits(map, + QAIF_EE_RDDMA_UNDERFLOW_IRQ_EN_REG(v, irq_type), + val_irq_disable, 0); + ret |= regmap_write_bits(map, + QAIF_EE_RDDMA_ERR_RSP_IRQ_EN_REG(v, irq_type), + val_irq_disable, 0); + } else { + ret |= regmap_write_bits(map, + QAIF_EE_WRDMA_PERIOD_IRQ_EN_REG(v, irq_type), + val_irq_disable, 0); + ret |= regmap_write_bits(map, + QAIF_EE_WRDMA_OVERFLOW_IRQ_EN_REG(v, irq_type), + val_irq_disable, 0); + ret |= regmap_write_bits(map, + QAIF_EE_WRDMA_ERR_RSP_IRQ_EN_REG(v, irq_type), + val_irq_disable, 0); + } + return ret; +} + +static int qaif_platform_pcmops_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + int cmd) +{ + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + const struct qaif_variant *v = drvdata->variant; + struct qaif_dmactl *dmactl; + struct regmap *map; + int ret, idx; + unsigned int dai_id = cpu_dai->driver->id; + + //pr_err("%s:%d: dai_id=%u stream=%d cmd=%d\n", + // __func__, __LINE__, dai_id, substream->stream, cmd); + + dmactl = qaif_get_dmactl_handle(substream, component); + if (!dmactl) + return -EINVAL; + idx = v->get_dma_idx(dai_id); + map = drvdata->audio_qaif_map; + + if (idx < 0) { + dev_err(soc_runtime->dev, "%s: Invalid DMA index: %d\n", __func__, idx); + return -EINVAL; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = regmap_fields_write(dmactl->dma_dyncclk, idx, QAIF_DMACTL_DYNCLK_ON); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to dma_dyncclk reg field: %d\n", ret); + return ret; + } + ret = regmap_fields_write(dmactl->enable, idx, QAIF_DMACTL_ENABLE_ON); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to dma enable reg: %d\n", ret); + return ret; + } + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + case MI2S_SENARY: + case MI2S_SEPTENARY: + ret = qaif_platform_irq_clear(drvdata, + substream->stream, QAIF_AIF_IRQ, idx); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to clear irq reg: %d\n", ret); + return ret; + } + ret = qaif_platform_irq_enable(drvdata, + substream->stream, QAIF_AIF_IRQ, idx); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to enable irq reg: %d\n", ret); + return ret; + } + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + ret = qaif_platform_irq_clear(drvdata, + substream->stream, QAIF_CIF_IRQ, idx); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to clear irq reg: %d\n", ret); + return ret; + } + ret = qaif_platform_irq_enable(drvdata, + substream->stream, QAIF_CIF_IRQ, idx); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to enable irq reg: %d\n", ret); + return ret; + } + break; + default: + dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id); + return -EINVAL; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = regmap_fields_write(dmactl->dma_dyncclk, idx, QAIF_DMACTL_DYNCLK_OFF); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to dma_dyncclk reg field: %d\n", ret); + return ret; + } + ret = regmap_fields_write(dmactl->enable, idx, QAIF_DMACTL_ENABLE_OFF); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to dma enable reg: %d\n", ret); + return ret; + } + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + case MI2S_SENARY: + case MI2S_SEPTENARY: + ret = qaif_platform_irq_disable(drvdata, + substream->stream, QAIF_AIF_IRQ, idx); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to enable irq reg: %d\n", ret); + return ret; + } + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + ret = qaif_platform_irq_disable(drvdata, + substream->stream, QAIF_CIF_IRQ, idx); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to enable irq reg: %d\n", ret); + return ret; + } + break; + default: + dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id); + return -EINVAL; + } + break; + } + pr_err("%s:%d: cmd=%d idx=%d ret=%d\n", + __func__, __LINE__, cmd, idx, ret); + return 0; +} + +static snd_pcm_uframes_t qaif_platform_pcmops_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + const struct qaif_variant *v = drvdata->variant; + unsigned int base_addr, curr_addr; + int ret, idx, dir = substream->stream; + struct regmap *map; + unsigned int dai_id = cpu_dai->driver->id; + + //pr_err("%s:%d: dai_id=%u stream=%d\n", + // __func__, __LINE__, dai_id, dir); + + map = drvdata->audio_qaif_map; + idx = v->get_dma_idx(dai_id); + + if (idx < 0) { + dev_err(soc_runtime->dev, "%s: Invalid DMA index: %d\n", __func__, idx); + return -EINVAL; + } + + ret = regmap_read(map, + QAIF_DMABASE_REG(v, idx, dir, dai_id), &base_addr); + if (ret) { + dev_err(soc_runtime->dev, + "error reading from rdmabase reg: %d\n", ret); + return ret; + } + + ret = regmap_read(map, + QAIF_DMACURR_REG(v, idx, dir, dai_id), &curr_addr); + if (ret) { + dev_err(soc_runtime->dev, + "error reading from rdmacurr reg: %d\n", ret); + return ret; + } + + return bytes_to_frames(substream->runtime, curr_addr - base_addr); +} + +static int qaif_platform_cdc_dma_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned long size, offset; + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + size = vma->vm_end - vma->vm_start; + offset = vma->vm_pgoff << PAGE_SHIFT; + return io_remap_pfn_range(vma, vma->vm_start, + (runtime->dma_addr + offset) >> PAGE_SHIFT, + size, vma->vm_page_prot); +} + +static int qaif_platform_pcmops_mmap(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0); + unsigned int dai_id = cpu_dai->driver->id; + + if (is_cif_dma_port(dai_id)) + return qaif_platform_cdc_dma_mmap(substream, vma); + + return snd_pcm_lib_default_mmap(substream, vma); +} + +static irqreturn_t qaif_process_dma_irq(struct qaif_drv_data *drvdata, + u32 stat_reg_addr, + u32 clr_reg_addr, + enum qaif_irq_type_t irq_type, + enum dma_type dma_type, + enum qaif_irq irq, + struct snd_pcm_substream **substream) +{ + const struct qaif_variant *v = drvdata->variant; + struct snd_pcm_substream *stream = NULL; + unsigned int reg = 0; + int dma_idx, stream_dma_idx, rv, num_dma = 0; + int stream_offset = (dma_type == DMA_TYPE_WRDMA) ? v->wrdma_start : 0; + irqreturn_t ret = IRQ_NONE; + u32 mask = 0; + + num_dma = (irq_type == QAIF_AIF_IRQ) ? v->num_rddma : v->num_codec_rddma; + mask = GENMASK(num_dma - 1, 0); + // Read Status + rv = regmap_read(drvdata->audio_qaif_map, stat_reg_addr, ®); + if (rv) { + pr_err("QAIF IRQ: error reading stat reg 0x%x: %d\n", stat_reg_addr, rv); + return IRQ_NONE; + } + + /* Writing the same reg that we just read from the status register, + * SPF also clears before handling. + */ + regmap_write(drvdata->audio_qaif_map, clr_reg_addr, reg & mask); + + for (dma_idx = 0; dma_idx < num_dma; dma_idx++) { + stream_dma_idx = dma_idx + stream_offset; + // Check if bit is set AND substream exists + if ((reg & BIT(dma_idx)) && substream[stream_dma_idx]) { + stream = substream[stream_dma_idx]; + switch (irq) { + case QAIF_IRQ_PERIOD: + snd_pcm_period_elapsed(stream); + ret = IRQ_HANDLED; + break; + + case QAIF_IRQ_OVERFLOW: + case QAIF_IRQ_UNDERFLOW: + // snd_pcm_stop_xrun(stream); + pr_warn_ratelimited("QAIF DMA xRun warning\n"); + ret = IRQ_HANDLED; + break; + + case QAIF_IRQ_ERROR: + snd_pcm_stop(stream, SNDRV_PCM_STATE_DISCONNECTED); + pr_err("QAIF Bus error\n"); + ret = IRQ_HANDLED; + break; + } + } + } + return ret; +} + +static irqreturn_t qaif_aif_irq_handler(struct qaif_drv_data *drvdata, u32 summary_irq_status) +{ + const struct qaif_variant *v = drvdata->variant; + irqreturn_t ret = IRQ_NONE; + struct snd_pcm_substream **substream = drvdata->aif_substream; + + // period_irq handling. + if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_PERIOD_RDDMA) { + ret |= qaif_process_dma_irq(drvdata, + QAIF_EE_RDDMA_PERIOD_IRQ_STAT_REG(v, QAIF_AIF_IRQ), + QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, QAIF_AIF_IRQ), + QAIF_AIF_IRQ, DMA_TYPE_RDDMA, QAIF_IRQ_PERIOD, substream); + } + if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_PERIOD_WRDMA) { + ret |= qaif_process_dma_irq(drvdata, + QAIF_EE_WRDMA_PERIOD_IRQ_STAT_REG(v, QAIF_AIF_IRQ), + QAIF_EE_WRDMA_PERIOD_IRQ_CLR_REG(v, QAIF_AIF_IRQ), + QAIF_AIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_PERIOD, substream); + } + // OVERFLOQW & underflow handling. + if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_OVERFLOW_WRDMA) { + ret |= qaif_process_dma_irq(drvdata, + QAIF_EE_WRDMA_OVERFLOW_IRQ_STAT_REG(v, QAIF_AIF_IRQ), + QAIF_EE_WRDMA_OVERFLOW_IRQ_CLR_REG(v, QAIF_AIF_IRQ), + QAIF_AIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_OVERFLOW, substream); + } + if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_UNDERFLOW_RDDMA) { + ret |= qaif_process_dma_irq(drvdata, + QAIF_EE_RDDMA_UNDERFLOW_IRQ_STAT_REG(v, QAIF_AIF_IRQ), + QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, QAIF_AIF_IRQ), + QAIF_AIF_IRQ, DMA_TYPE_RDDMA, QAIF_IRQ_UNDERFLOW, substream); + } + // Bus error handling. + if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_ERR_RSP_RDDMA) { + ret |= qaif_process_dma_irq(drvdata, + QAIF_EE_WRDMA_ERR_RSP_IRQ_STAT_REG(v, QAIF_AIF_IRQ), + QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, QAIF_AIF_IRQ), + QAIF_AIF_IRQ, DMA_TYPE_RDDMA, QAIF_IRQ_ERROR, substream); + } + if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_ERR_RSP_WRDMA) { + ret |= qaif_process_dma_irq(drvdata, + QAIF_EE_WRDMA_ERR_RSP_IRQ_STAT_REG(v, QAIF_AIF_IRQ), + QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, QAIF_AIF_IRQ), + QAIF_AIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_ERROR, substream); + } + return ret; +} + +static irqreturn_t qaif_cif_irq_handler(struct qaif_drv_data *drvdata, u32 summary_irq_status) +{ + const struct qaif_variant *v = drvdata->variant; + irqreturn_t ret = IRQ_NONE; + struct snd_pcm_substream **substream = drvdata->cif_substream; + + // period_irq handling. + if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_PERIOD_RDDMA) { + ret |= qaif_process_dma_irq(drvdata, + QAIF_EE_RDDMA_PERIOD_IRQ_STAT_REG(v, QAIF_CIF_IRQ), + QAIF_EE_RDDMA_PERIOD_IRQ_CLR_REG(v, QAIF_CIF_IRQ), + QAIF_CIF_IRQ, DMA_TYPE_RDDMA, QAIF_IRQ_PERIOD, substream); + } + if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_PERIOD_WRDMA) { + ret |= qaif_process_dma_irq(drvdata, + QAIF_EE_WRDMA_PERIOD_IRQ_STAT_REG(v, QAIF_CIF_IRQ), + QAIF_EE_WRDMA_PERIOD_IRQ_CLR_REG(v, QAIF_CIF_IRQ), + QAIF_CIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_PERIOD, substream); + } + + if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_OVERFLOW_WRDMA) { + ret |= qaif_process_dma_irq(drvdata, + QAIF_EE_WRDMA_OVERFLOW_IRQ_STAT_REG(v, QAIF_CIF_IRQ), + QAIF_EE_WRDMA_OVERFLOW_IRQ_CLR_REG(v, QAIF_CIF_IRQ), + QAIF_CIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_OVERFLOW, substream); + } + if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_UNDERFLOW_RDDMA) { + ret |= qaif_process_dma_irq(drvdata, + QAIF_EE_RDDMA_UNDERFLOW_IRQ_STAT_REG(v, QAIF_CIF_IRQ), + QAIF_EE_RDDMA_UNDERFLOW_IRQ_CLR_REG(v, QAIF_CIF_IRQ), + QAIF_CIF_IRQ, DMA_TYPE_RDDMA, QAIF_IRQ_UNDERFLOW, substream); + } + + if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_ERR_RSP) { + ret |= qaif_process_dma_irq(drvdata, + QAIF_EE_WRDMA_ERR_RSP_IRQ_STAT_REG(v, QAIF_CIF_IRQ), + QAIF_EE_WRDMA_ERR_RSP_IRQ_CLR_REG(v, QAIF_CIF_IRQ), + QAIF_CIF_IRQ, DMA_TYPE_WRDMA, QAIF_IRQ_ERROR, substream); + } + + return ret; +} + +static irqreturn_t asoc_platform_qaif_irq(int irq, void *data) +{ + struct qaif_drv_data *drvdata = data; + const struct qaif_variant *v = drvdata->variant; + u32 summary_irq_status; + int rv, client; + irqreturn_t ret = IRQ_NONE; + + rv = regmap_read(drvdata->audio_qaif_map, + QAIF_SUMMARY_IRQSTAT_REG(v), &summary_irq_status); + if (rv) { + pr_err("error reading from irqstat reg: %d\n", rv); + return IRQ_NONE; + } + pr_debug("%s: summary_irq_status =0x%08x\n", __func__, summary_irq_status); + if (!(summary_irq_status & QAIF_ALL_CLIENTS_MASK)) + return IRQ_NONE; + for (client = 0; client < ARRAY_SIZE(qaif_irq_clients); client++) { + /* Check if the bits for this specific client_id are set in the register */ + if (summary_irq_status & qaif_irq_clients[client].mask) + ret = qaif_irq_clients[client].client_irq_handler(drvdata, + summary_irq_status); + } + return ret; +} + +static int qaif_platform_pcmops_suspend(struct snd_soc_component *component) +{ + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + struct regmap *map; + + map = drvdata->audio_qaif_map; + pr_err("%s:%d: suspend\n", __func__, __LINE__); + + regcache_cache_only(map, true); + regcache_mark_dirty(map); + clk_disable(drvdata->aud_dma_clk); + clk_disable(drvdata->aud_dma_mem_clk); + return 0; +} + +static int qaif_platform_pcmops_resume(struct snd_soc_component *component) +{ + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + struct regmap *map; + int ret; + + pr_err("%s:%d: resume\n", __func__, __LINE__); + clk_enable(drvdata->aud_dma_clk); + clk_enable(drvdata->aud_dma_mem_clk); + map = drvdata->audio_qaif_map; + + regcache_cache_only(map, false); + ret = regcache_sync(map); + if (ret) + dev_err(component->dev, "%s: regcache_sync failed: %d\n", + __func__, ret); + return ret; +} + +static int qaif_platform_copy(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int channel, + unsigned long pos, struct iov_iter *buf, + unsigned long bytes) +{ + struct snd_pcm_runtime *rt = substream->runtime; + size_t copied; + void *dma_buf; + + // rt->dma_area is the vaddr from iosys_vmap - regular kernel memory + dma_buf = (void *)(rt->dma_area + pos + + channel * (rt->dma_bytes / rt->channels)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + copied = copy_from_iter(dma_buf, bytes, buf); + if (copied != bytes) { + pr_err("DEBUG:%s:%d:Copy failed\n", __func__, __LINE__); + return -EFAULT; + } + } else { + copied = copy_to_iter(dma_buf, bytes, buf); + if (copied != bytes) { + pr_err("DEBUG:%s:%d:Copy failed\n", __func__, __LINE__); + return -EFAULT; + } + } + + return 0; +} + +static const struct snd_soc_component_driver qaif_component_driver = { + .name = DRV_NAME, + .open = qaif_platform_pcmops_open, + .close = qaif_platform_pcmops_close, + .hw_params = qaif_platform_pcmops_hw_params, + .hw_free = qaif_platform_pcmops_hw_free, + .prepare = qaif_platform_pcmops_prepare, + .trigger = qaif_platform_pcmops_trigger, + .pointer = qaif_platform_pcmops_pointer, + .mmap = qaif_platform_pcmops_mmap, + .suspend = qaif_platform_pcmops_suspend, + .resume = qaif_platform_pcmops_resume, + .copy = qaif_platform_copy, +}; + +/** + * qaif_map_ee_resource - Implements Steps 1-6 of Restrictions + * Maps GRP, INTF, RDDMA, WRDMA to the current EE. + */ +static int qaif_map_ee_resource(struct qaif_drv_data *drvdata) +{ + struct qaif_variant *v = drvdata->variant; + struct regmap *map = drvdata->audio_qaif_map; + int ret = 0; + u32 mask; + + mask = GENMASK(v->num_rddma - 1, 0); + ret |= regmap_write(map, QAIF_EE_RDDMA_MAP_REG(v), mask); + + mask = GENMASK(v->num_wrdma - 1, 0); + ret |= regmap_write(map, QAIF_EE_WRDMA_MAP_REG(v), mask); + + mask = GENMASK(v->num_intf - 1, 0); + ret |= regmap_write(map, QAIF_EE_INTF_MAP_REG(v), mask); + + mask = GENMASK(v->num_codec_rddma - 1, 0); + ret |= regmap_write(map, QAIF_EE_CODEC_RDDMA_MAP_REG(v), mask); + + mask = GENMASK(v->num_codec_wrdma - 1, 0); + ret |= regmap_write(map, QAIF_EE_CODEC_WRDMA_MAP_REG(v), mask); + + if (ret) + return ret; + return 0; +} + +static int qaif_map_dma_path(struct qaif_drv_data *drvdata) +{ + struct regmap *map = drvdata->audio_qaif_map; + struct qaif_variant *v = drvdata->variant; + int ret = 0; + int qxm_sel = v->qxm_type; + + if (qxm_sel != QXM0 && qxm_sel != QXM1) + return -EINVAL; + + ret |= regmap_write(map, QAIF_RDDMA_MAP_QXM, qxm_sel); + ret |= regmap_write(map, QAIF_WRDMA_MAP_QXM, qxm_sel); + ret |= regmap_write(map, QAIF_CODEC_RDDMA_MAP_QXM, qxm_sel); + ret |= regmap_write(map, QAIF_CODEC_WRDMA_MAP_QXM, qxm_sel); + + if (ret) + return ret; + + return 0; +} + +static int qaif_config_shram(struct qaif_drv_data *drvdata) +{ + struct qaif_variant *v = drvdata->variant; + u32 start_addr, shram_len; + int ret = 0, i = 0; + struct regmap *map = drvdata->audio_qaif_map; + + if (v->qxm_type != QXM0) + return -EINVAL; + //AIF RDDMA + start_addr = v->rddma_shram_start_addr[QAIF_AIF_DMA]; + shram_len = v->rddma_shram_len; + for (i = 0; i < v->num_rddma; i++) { + ret = regmap_write(map, + QAIF_RDDMA_QXM0_SHRAM_ST_ADDR(i), + start_addr + (shram_len * i)); + if (ret) + return ret; + ret = regmap_write(map, QAIF_RDDMA_QXM0_SHRAM_LEN(i), shram_len); + if (ret) + return ret; + } + //AIF WRDMA + start_addr = v->wrdma_shram_start_addr[QAIF_AIF_DMA]; + shram_len = v->wrdma_shram_len; + for (i = 0; i < v->num_wrdma; i++) { + ret = regmap_write(map, + QAIF_WRDMA_QXM0_SHRAM_ST_ADDR(i), + start_addr + (shram_len * i)); + if (ret) + return ret; + ret = regmap_write(map, QAIF_WRDMA_QXM0_SHRAM_LEN(i), shram_len); + if (ret) + return ret; + } + //CIF RDDMA + start_addr = v->rddma_shram_start_addr[QAIF_CIF_DMA]; + shram_len = v->rddma_shram_len; + for (i = 0; i < v->num_codec_rddma; i++) { + ret = regmap_write(map, + QAIF_CODEC_RDDMA_QXM0_SHRAM_ST_ADDR(i), + start_addr + (shram_len * i)); + if (ret) + return ret; + ret = regmap_write(map, QAIF_CODEC_RDDMA_QXM0_SHRAM_LEN(i), shram_len); + if (ret) + return ret; + } + //CIF wrDMA + start_addr = v->wrdma_shram_start_addr[QAIF_CIF_DMA]; + shram_len = v->wrdma_shram_len; + for (i = 0; i < v->num_codec_wrdma; i++) { + ret = regmap_write(map, + QAIF_CODEC_WRDMA_QXM0_SHRAM_ST_ADDR(i), + start_addr + (shram_len * i)); + if (ret) + return ret; + ret = regmap_write(map, QAIF_CODEC_WRDMA_QXM0_SHRAM_LEN(i), shram_len); + + if (ret) + return ret; + } + return 0; +} + +static int qaif_init(struct snd_soc_component *component) +{ + struct qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component); + int ret = 0; + + if (drvdata->qaif_init_ref_cnt) { + dev_info(component->dev, "%s: QAIF init is done already: ref cnt: %d\n", + __func__, drvdata->qaif_init_ref_cnt); + return 0; + } + + ret = qaif_config_shram(drvdata); + if (ret) { + dev_err(component->dev, "QAIF: Failed to config shram: %d\n", ret); + return ret; + } + + ret = qaif_map_ee_resource(drvdata); + if (ret) { + dev_err(component->dev, "QAIF: Failed to map EE resources: %d\n", ret); + return ret; + } + + ret = qaif_map_dma_path(drvdata); + if (ret) { + dev_err(component->dev, "QAIF: Failed to map EE resources: %d\n", ret); + return ret; + } + dev_dbg(component->dev, "%s: QAIF init is done ref cnt: %d\n", + __func__, drvdata->qaif_init_ref_cnt); + return 0; +} + +int asoc_qcom_qaif_platform_register(struct platform_device *pdev) +{ + struct qaif_drv_data *drvdata = platform_get_drvdata(pdev); + int ret = 0; + + if (!drvdata || !drvdata->variant) { + dev_err(&pdev->dev, "Invalid drvdata or variant\n"); + return -EINVAL; + } + + drvdata->smmu_csid_bits = 0; + + drvdata->audio_qaif_irq = platform_get_irq_byname(pdev, "qaif-irq-audio-core"); + if (drvdata->audio_qaif_irq < 0) + return -ENODEV; + + ret = devm_request_irq(&pdev->dev, drvdata->audio_qaif_irq, + asoc_platform_qaif_irq, IRQF_TRIGGER_HIGH, + "qaif-irq-audio-core", drvdata); + if (ret) { + dev_err(&pdev->dev, "irq request failed: %d\n", ret); + return ret; + } + drvdata->qaif_init_ref_cnt = 0; + dev_dbg(&pdev->dev, "%s: Register QAIF Platform\n", __func__); + return devm_snd_soc_register_component(&pdev->dev, + &qaif_component_driver, NULL, 0); +} +EXPORT_SYMBOL_GPL(asoc_qcom_qaif_platform_register); + +MODULE_DESCRIPTION("QTi QAIF Platform Driver"); +MODULE_LICENSE("GPL"); From ed75614e09bee845167abe54bcf35f9d88cee565 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Fri, 29 May 2026 11:48:09 +0530 Subject: [PATCH 5/5] ASoC: qcom: qaif-shikra: Add platform driver for qaif audio Add platform driver for configuring shikra qaif core I2S and DMA configuration to support playback & capture to external codecs connected over MI2S interface and soundwire interface. Signed-off-by: Mohammad Rafi Shaik --- sound/soc/qcom/Kconfig | 11 + sound/soc/qcom/Makefile | 2 + sound/soc/qcom/qaif-shikra.c | 702 +++++++++++++++++++++++++++++++++++ 3 files changed, 715 insertions(+) create mode 100644 sound/soc/qcom/qaif-shikra.c diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index e6e24f3b99222..008b5ccae331a 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -71,6 +71,16 @@ config SND_SOC_QCOM_COMMON config SND_SOC_QCOM_SDW tristate +config SND_SOC_QCOM_QAIF + tristate "Qualcomm QAIF audio interface support" + depends on COMMON_CLK + select REGMAP_MMIO + help + Say Y or M to enable Qualcomm Audio Interface (QAIF) support + used on Shikra audio platforms. QAIF provides DMA-based audio + data transfer between the application processor and the audio + hardware interfaces (AIF and CIF). + config SND_SOC_QDSP6_COMMON tristate @@ -202,6 +212,7 @@ config SND_SOC_SC8280XP select SND_SOC_QDSP6 select SND_SOC_QCOM_COMMON select SND_SOC_QCOM_SDW + select SND_SOC_QCOM_QAIF help To add support for audio on Qualcomm Technologies Inc. SC8280XP SoC-based systems. diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 985ce2ae286ba..857bb2a032a2d 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -31,6 +31,7 @@ snd-soc-qcom-common-y := common.o snd-soc-qcom-sdw-y := sdw.o snd-soc-x1e80100-y := x1e80100.o snd-soc-qcom-offload-utils-objs := usb_offload_utils.o +snd-soc-qcom-qaif-y := qaif-cpu.o qaif-platform.o qaif-shikra.o obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o @@ -44,6 +45,7 @@ obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o obj-$(CONFIG_SND_SOC_QCOM_SDW) += snd-soc-qcom-sdw.o obj-$(CONFIG_SND_SOC_X1E80100) += snd-soc-x1e80100.o obj-$(CONFIG_SND_SOC_QCOM_OFFLOAD_UTILS) += snd-soc-qcom-offload-utils.o +obj-$(CONFIG_SND_SOC_QCOM_QAIF) += snd-soc-qcom-qaif.o #DSP lib obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/ diff --git a/sound/soc/qcom/qaif-shikra.c b/sound/soc/qcom/qaif-shikra.c new file mode 100644 index 0000000000000..6e87ec7e22a16 --- /dev/null +++ b/sound/soc/qcom/qaif-shikra.c @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + * + * qaif-shikra.c -- ALSA SoC CPU-Platform DAI driver for QTi QAIF + */ + +#include +#include +#include +#include +#include "qaif.h" + +struct qaif_dmaidx_dai_map shikra_aif_dma_dai_map[] = { + { MI2S_QUATERNARY }, + { MI2S_QUINARY }, + { MI2S_SENARY }, + { MI2S_SEPTENARY } +}; + +struct qaif_dmaidx_dai_map shikra_cif_rx_dma_dai_map[] = { + { LPASS_CDC_DMA_RX0 }, + { LPASS_CDC_DMA_RX1 }, + { LPASS_CDC_DMA_RX2 }, + { LPASS_CDC_DMA_RX3 } +}; + +struct qaif_dmaidx_dai_map shikra_cif_tx_dma_dai_map[] = { + { LPASS_CDC_DMA_TX0 }, + { LPASS_CDC_DMA_TX1 }, + { LPASS_CDC_DMA_TX2 }, + { LPASS_CDC_DMA_TX3 } +}; + +struct qaif_dmaidx_dai_map shikra_cif_va_dma_dai_map[] = { + { LPASS_CDC_DMA_VA_TX0 }, + { LPASS_CDC_DMA_VA_TX1 }, + { LPASS_CDC_DMA_VA_TX2 }, + { LPASS_CDC_DMA_VA_TX3 } +}; + +static struct snd_soc_dai_driver shikra_qaif_cpu_dai_driver[] = { + { + .id = MI2S_PRIMARY, + .name = "Primary MI2S", + .playback = { + .stream_name = "Primary MI2S Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Primary MI2S Capture", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_qaif_aif_cpu_dai_ops, + }, { + .id = MI2S_SECONDARY, + .name = "Secondary MI2S", + .playback = { + .stream_name = "Secondary MI2S Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Secondary MI2S Capture", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_qaif_aif_cpu_dai_ops, + }, { + .id = MI2S_TERTIARY, + .name = "Tertiary MI2S", + .playback = { + .stream_name = "Tertiary MI2S Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Tertiary MI2S Capture", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_qaif_aif_cpu_dai_ops, + }, { + .id = MI2S_QUATERNARY, + .name = "Quaternary MI2S", + .playback = { + .stream_name = "Quaternary MI2S Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Quaternary MI2S Capture", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_qaif_aif_cpu_dai_ops, + }, { + .id = MI2S_QUINARY, + .name = "Quinary MI2S", + .playback = { + .stream_name = "Quinary MI2S Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Quinary MI2S Capture", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_qaif_aif_cpu_dai_ops, + }, { + .id = MI2S_SENARY, + .name = "Senary MI2S", + .playback = { + .stream_name = "Senary MI2S Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Senary MI2S Capture", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_qaif_aif_cpu_dai_ops, + }, { + .id = MI2S_SEPTENARY, + .name = "Septenary MI2S", + .playback = { + .stream_name = "Septenary MI2S Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Septenary MI2S Capture", + .formats = SNDRV_PCM_FMTBIT_S16 | + SNDRV_PCM_FMTBIT_S32, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_qaif_aif_cpu_dai_ops, + }, { + .id = LPASS_CDC_DMA_RX0, + .name = "CDC DMA RX0", + .playback = { + .stream_name = "WCD Playback0", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &asoc_qcom_qaif_cif_dai_ops, + }, { + .id = LPASS_CDC_DMA_RX1, + .name = "CDC DMA RX1", + .playback = { + .stream_name = "WCD Playback1", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &asoc_qcom_qaif_cif_dai_ops, + }, { + .id = LPASS_CDC_DMA_TX0, + .name = "CDC DMA TX0", + .capture = { + .stream_name = "WCD Capture0", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &asoc_qcom_qaif_cif_dai_ops, + }, { + .id = LPASS_CDC_DMA_TX3, + .name = "CDC DMA TX3", + .capture = { + .stream_name = "WCD Capture1", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &asoc_qcom_qaif_cif_dai_ops, + }, { + .id = LPASS_CDC_DMA_VA_TX0, + .name = "CDC DMA VA0", + .capture = { + .stream_name = "DMIC Capture0", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &asoc_qcom_qaif_cif_dai_ops, + }, { + .id = LPASS_CDC_DMA_VA_TX1, + .name = "CDC DMA VA1", + .capture = { + .stream_name = "DMIC Capture1", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 4, + }, + .ops = &asoc_qcom_qaif_cif_dai_ops, + }, +}; + +static int shikra_qaif_get_dma_idx(unsigned int dai_id) +{ + int i; + + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + case MI2S_SENARY: + case MI2S_SEPTENARY: + for (i = 0; i < ARRAY_SIZE(shikra_aif_dma_dai_map); i++) { + if (shikra_aif_dma_dai_map[i].dai_id == dai_id) + return i; + } + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + for (i = 0; i < ARRAY_SIZE(shikra_cif_rx_dma_dai_map); i++) { + if (shikra_cif_rx_dma_dai_map[i].dai_id == dai_id) + return i; + } + break; + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + for (i = 0; i < ARRAY_SIZE(shikra_cif_tx_dma_dai_map); i++) { + if (shikra_cif_tx_dma_dai_map[i].dai_id == dai_id) + return i; + } + break; + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + for (i = 0; i < ARRAY_SIZE(shikra_cif_va_dma_dai_map); i++) { + if (shikra_cif_va_dma_dai_map[i].dai_id == dai_id) + return i; + } + break; + default: + pr_debug("DAI ID not Supported\n"); + break; + } + + pr_debug("DAI ID %u not found in map\n", dai_id); + return -EINVAL; +} + +static int shikra_qaif_alloc_stream_dma_idx(struct qaif_drv_data *drvdata, + int direction, unsigned int dai_id) +{ + struct qaif_variant *v = drvdata->variant; + int dma_idx; + int index = 0; + + if (!v) + return -EINVAL; + + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + case MI2S_SENARY: + case MI2S_SEPTENARY: + dma_idx = shikra_qaif_get_dma_idx(dai_id); + if (dma_idx < 0) + return dma_idx; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + index = dma_idx; + if (index >= v->num_rddma) + return -EBUSY; + } else { + index = v->wrdma_start + dma_idx; + if (index >= v->wrdma_start + v->num_wrdma) + return -EBUSY; + } + if (test_bit(index, &drvdata->aif_dma_idx_bit_map)) + return -EBUSY; + + set_bit(index, &drvdata->aif_dma_idx_bit_map); + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + dma_idx = shikra_qaif_get_dma_idx(dai_id); + if (dma_idx < 0) + return dma_idx; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + index = dma_idx; + if (index >= v->num_codec_rddma) + return -EBUSY; + } else { + index = v->codec_wrdma_start + dma_idx; + if (index >= v->codec_wrdma_start + v->num_codec_wrdma) + return -EBUSY; + } + if (test_bit(index, &drvdata->cif_dma_idx_bit_map)) + return -EBUSY; + + set_bit(index, &drvdata->cif_dma_idx_bit_map); + break; + default: + return -EINVAL; + } + + return index; +} + +static int shikra_qaif_free_stream_dma_idx(struct qaif_drv_data *drvdata, + int index, unsigned int dai_id) +{ + switch (dai_id) { + case MI2S_PRIMARY ... MI2S_QUINARY: + case MI2S_SENARY: + case MI2S_SEPTENARY: + clear_bit(index, &drvdata->aif_dma_idx_bit_map); + break; + case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9: + case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8: + case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8: + clear_bit(index, &drvdata->cif_dma_idx_bit_map); + break; + default: + break; + } + + return 0; +} + +static int shikra_qaif_init(struct platform_device *pdev) +{ + struct qaif_drv_data *drvdata = platform_get_drvdata(pdev); + struct qaif_variant *v = drvdata->variant; + struct device *dev = &pdev->dev; + int ret, i; + + if (!v) { + dev_err(dev, "No variant data\n"); + return -EINVAL; + } + if (v->num_clks == 0 || v->num_clks > 32) { + dev_err(dev, "Invalid clock count: %d\n", v->num_clks); + return -EINVAL; + } + drvdata->clks = devm_kcalloc(dev, v->num_clks, + sizeof(*drvdata->clks), GFP_KERNEL); + if (!drvdata->clks) + return -ENOMEM; + + drvdata->num_clks = v->num_clks; + + for (i = 0; i < drvdata->num_clks; i++) + drvdata->clks[i].id = v->clk_name[i]; + + ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks); + if (ret) { + dev_err(dev, "Failed to get clocks %d\n", ret); + return ret; + } + + ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); + if (ret) { + dev_err(dev, "shikra clk_enable failed\n"); + return ret; + } + + return 0; +} + +static int shikra_qaif_exit(struct platform_device *pdev) +{ + struct qaif_drv_data *drvdata = platform_get_drvdata(pdev); + + if (!drvdata || !drvdata->clks) + return -EINVAL; + + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); + return 0; +} + +static int __maybe_unused shikra_qaif_dev_resume(struct device *dev) +{ + struct qaif_drv_data *drvdata = dev_get_drvdata(dev); + + if (!drvdata || !drvdata->clks) { + dev_err(dev, "Invalid drvdata in resume\n"); + return -EINVAL; + } + return clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); +} + +static int __maybe_unused shikra_qaif_dev_suspend(struct device *dev) +{ + struct qaif_drv_data *drvdata = dev_get_drvdata(dev); + + if (!drvdata || !drvdata->clks) { + dev_err(dev, "Invalid drvdata in suspend\n"); + return -EINVAL; + } + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); + return 0; +} + +static const struct dev_pm_ops shikra_qaif_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(shikra_qaif_dev_suspend, + shikra_qaif_dev_resume) +}; + +static struct qaif_variant shikra_qaif_data = { + .ee = 0, + .qaif_type = QAIF, + + .num_rddma = 4, + .num_wrdma = 4, + .wrdma_start = 4, + + .num_codec_rddma = 4, //RX + .num_codec_wrdma = 4, //TX + .codec_wrdma_start = 4, + .num_intf = 0, + + .rddma_reg_base = 0x8000, + .rddma_stride = 0x1000, + .codec_rddma_reg_base = 0xC000, + .codec_rddma_stride = 0x1000, + + .wrdma_reg_base = 0x11000, + .wrdma_stride = 0x1000, + .codec_wrdma_reg_base = 0x15000, + .codec_wrdma_stride = 0x1000, + + .rddma_irq_reg_base = 0x19000, + .rddma_irq_stride = 0x1000, + .codec_rddma_irq_reg_base = 0x191A0, + .codec_rddma_irq_stride = 0x1000, + + .wrdma_irq_reg_base = 0x19078, + .wrdma_irq_stride = 0x1000, + .codec_wrdma_irq_reg_base = 0x19290, + .codec_wrdma_irq_stride = 0x1000, + + .qxm_type = QXM0, + .rd_len = 512, + .rddma_shram_len = 64, + .rddma_shram_start_addr = {0, 256}, + .wr_len = 512, + .wrdma_shram_len = 64, + .wrdma_shram_start_addr = {0, 256}, + + /* ============================================================================ */ + /* AIF RDDMA (Audio Interface Read DMA) Register Fields */ + /* Base: 0x8000, 4 channels, stride 0x1000 */ + /* ============================================================================ */ + + /* CTL register (0x8000) */ + .rddma_enable = REG_FIELD_ID(0x8000, 0, 0, 4, 0x1000), /* ENABLE [0] */ + .rddma_reset = REG_FIELD_ID(0x8000, 4, 4, 4, 0x1000), /* RESET [4] */ + + /* CFG register (0x8004) */ + .rddma_shram_wm = REG_FIELD_ID(0x8004, 0, 11, 4, 0x1000), /* SHRAM_WATERMRK [11:0] */ + .rddma_burst1 = REG_FIELD_ID(0x8004, 16, 16, 4, 0x1000), /* BURST1_EN [16] */ + .rddma_burst2 = REG_FIELD_ID(0x8004, 17, 17, 4, 0x1000), /* BURST2_EN [17] */ + .rddma_burst4 = REG_FIELD_ID(0x8004, 18, 18, 4, 0x1000), /* BURST4_EN [18] */ + .rddma_burst8 = REG_FIELD_ID(0x8004, 19, 19, 4, 0x1000), /* BURST8_EN [19] */ + .rddma_burst16 = REG_FIELD_ID(0x8004, 20, 20, 4, 0x1000), /* BURST16_EN [20] */ + .rddma_dma_dyncclk = REG_FIELD_ID(0x8004, 24, 24, 4, 0x1000), /* DYNAMIC_CLOCK [24] */ + .rddma_num_ot = REG_FIELD_ID(0x8004, 28, 29, 4, 0x1000), /* RDDMA_NUM_OT [29:28] */ + + /* ============================================================================ */ + /* AIF WRDMA (Audio Interface Write DMA) Register Fields */ + /* Base: 0x11000, 4 channels, stride 0x1000 */ + /* ============================================================================ */ + + /* CTL register (0x11000) */ + .wrdma_enable = REG_FIELD_ID(0x11000, 0, 0, 4, 0x1000), /* ENABLE [0] */ + .wrdma_reset = REG_FIELD_ID(0x11000, 4, 4, 4, 0x1000), /* RESET [4] */ + + /* CFG register (0x11004) */ + .wrdma_shram_wm = REG_FIELD_ID(0x11004, 0, 11, 4, 0x1000), /* SHRAM_WATERMRK [11:0] */ + .wrdma_burst1 = REG_FIELD_ID(0x11004, 16, 16, 4, 0x1000), /* BURST1_EN [16] */ + .wrdma_burst2 = REG_FIELD_ID(0x11004, 17, 17, 4, 0x1000), /* BURST2_EN [17] */ + .wrdma_burst4 = REG_FIELD_ID(0x11004, 18, 18, 4, 0x1000), /* BURST4_EN [18] */ + .wrdma_burst8 = REG_FIELD_ID(0x11004, 19, 19, 4, 0x1000), /* BURST8_EN [19] */ + .wrdma_burst16 = REG_FIELD_ID(0x11004, 20, 20, 4, 0x1000), /* BURST16_EN [20] */ + .wrdma_dma_dyncclk = REG_FIELD_ID(0x11004, 24, 24, 4, 0x1000), /* DYNAMIC_CLOCK [24] */ + .wrdma_num_ot = REG_FIELD_ID(0x11004, 28, 29, 4, 0x1000), /* WRDMA_NUM_OT [29:28] */ + + /* ============================================================================ */ + /* CODEC RDDMA (RX/Playback) Register Fields */ + /* Base: 0xC000, 4 channels, stride 0x1000 */ + /* ============================================================================ */ + + /* CTL register (0xC000) */ + .cif_rddma_enable = REG_FIELD_ID(0xC000, 0, 0, 4, 0x1000), + .cif_rddma_reset = REG_FIELD_ID(0xC000, 4, 4, 4, 0x1000), + + /* CFG register (0xC004) */ + .cif_rddma_shram_wm = REG_FIELD_ID(0xC004, 0, 11, 4, 0x1000), /* SHRAM_WATERMRK [11:0] */ + .cif_rddma_burst1 = REG_FIELD_ID(0xC004, 16, 16, 4, 0x1000), /* BURST1_EN [16] */ + .cif_rddma_burst2 = REG_FIELD_ID(0xC004, 17, 17, 4, 0x1000), /* BURST2_EN [17] */ + .cif_rddma_burst4 = REG_FIELD_ID(0xC004, 18, 18, 4, 0x1000), /* BURST4_EN [18] */ + .cif_rddma_burst8 = REG_FIELD_ID(0xC004, 19, 19, 4, 0x1000), /* BURST8_EN [19] */ + .cif_rddma_burst16 = REG_FIELD_ID(0xC004, 20, 20, 4, 0x1000), /* BURST16_EN [20] */ + .cif_rddma_dma_dyncclk = REG_FIELD_ID(0xC004, 24, 24, 4, 0x1000), /* DYNAMIC_CLOCK [24] */ + .cif_rddma_num_ot = REG_FIELD_ID(0xC004, 28, 29, 4, 0x1000), /* RDDMA_NUM_OT [29:28] */ + + /* INTF_CFG register (0xC05C) */ + .cif_rddma_en_16bit_unpack = REG_FIELD_ID(0xC05C, 0, 0, 4, 0x1000), + /* ENABLE_16B_UNPACKING [0] */ + .cif_rddma_intf_dyncclk = REG_FIELD_ID(0xC05C, 2, 2, 4, 0x1000), /* DYNAMIC_CLOCK [2] */ + .cif_rddma_fs_out_gate = REG_FIELD_ID(0xC05C, 3, 3, 4, 0x1000), /* FS_OUT_GATING_EN [3] */ + .cif_rddma_fs_sel = REG_FIELD_ID(0xC05C, 4, 7, 4, 0x1000), /* FS_SEL [7:4] */ + .cif_rddma_fs_delay = REG_FIELD_ID(0xC05C, 8, 11, 4, 0x1000), /* FS_DELAY [11:8] */ + .cif_rddma_active_ch_en = REG_FIELD_ID(0xC05C, 12, 27, 4, 0x1000), + /* ACTIVE_CHANNEL_EN [27:12] */ + + /* ============================================================================ */ + /* CODEC WRDMA (TX/Capture) Register Fields */ + /* Base: 0x15000, 4 channels, stride 0x1000 */ + /* ============================================================================ */ + + /* CTL register (0x15000) */ + .cif_wrdma_enable = REG_FIELD_ID(0x15000, 0, 0, 4, 0x1000), + .cif_wrdma_reset = REG_FIELD_ID(0x15000, 4, 4, 4, 0x1000), + + /* CFG register (0x15004) */ + .cif_wrdma_shram_wm = REG_FIELD_ID(0x15004, 0, 11, 4, 0x1000), /* SHRAM_WATERMRK [11:0] */ + .cif_wrdma_burst1 = REG_FIELD_ID(0x15004, 16, 16, 4, 0x1000), /* BURST1_EN [16] */ + .cif_wrdma_burst2 = REG_FIELD_ID(0x15004, 17, 17, 4, 0x1000), /* BURST2_EN [17] */ + .cif_wrdma_burst4 = REG_FIELD_ID(0x15004, 18, 18, 4, 0x1000), /* BURST4_EN [18] */ + .cif_wrdma_burst8 = REG_FIELD_ID(0x15004, 19, 19, 4, 0x1000), /* BURST8_EN [19] */ + .cif_wrdma_burst16 = REG_FIELD_ID(0x15004, 20, 20, 4, 0x1000), /* BURST16_EN [20] */ + .cif_wrdma_dma_dyncclk = REG_FIELD_ID(0x15004, 24, 24, 4, 0x1000), /* DYNAMIC_CLOCK [24] */ + .cif_wrdma_num_ot = REG_FIELD_ID(0x15004, 28, 29, 4, 0x1000), /* WRDMA_NUM_OT [29:28] */ + + /* INTF_CFG register (0x15058) */ + .cif_wrdma_en_16bit_unpack = REG_FIELD_ID(0x15058, 0, 0, 4, 0x1000), + /* ENABLE_16B_PACKING [0] */ + .cif_wrdma_intf_dyncclk = REG_FIELD_ID(0x15058, 2, 2, 4, 0x1000), /* DYNAMIC_CLOCK [2] */ + .cif_wrdma_fs_out_gate = REG_FIELD_ID(0x15058, 3, 3, 4, 0x1000), /* FS_OUT_GATING_EN [3] */ + .cif_wrdma_fs_sel = REG_FIELD_ID(0x15058, 4, 7, 4, 0x1000), /* FS_SEL [7:4] */ + .cif_wrdma_fs_delay = REG_FIELD_ID(0x15058, 8, 11, 4, 0x1000), /* FS_DELAY [11:8] */ + .cif_wrdma_active_ch_en = REG_FIELD_ID(0x15058, 12, 27, 4, 0x1000), + /* ACTIVE_CHANNEL_EN [27:12] */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_SYNC_CFG (0x4004 + 0x1000*a) */ + .aif_inv_sync = REG_FIELD_ID(0x4004, 12, 12, 4, 0x1000), /* bit 12 */ + .aif_sync_delay = REG_FIELD_ID(0x4004, 8, 9, 4, 0x1000), /* bits 9:8 */ + .aif_sync_mode = REG_FIELD_ID(0x4004, 4, 5, 4, 0x1000), /* bits 5:4 */ + .aif_sync_src = REG_FIELD_ID(0x4004, 0, 0, 4, 0x1000), /* bit 0 */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_BIT_WIDTH_CFG (0x4008 + 0x1000*a) */ + .aif_sample_width_rx = REG_FIELD_ID(0x4008, 24, 28, 4, 0x1000), /* bits 28:24 */ + .aif_sample_width_tx = REG_FIELD_ID(0x4008, 16, 20, 4, 0x1000), /* bits 20:16 */ + .aif_slot_width_rx = REG_FIELD_ID(0x4008, 8, 12, 4, 0x1000), /* bits 12:8 */ + .aif_slot_width_tx = REG_FIELD_ID(0x4008, 0, 4, 4, 0x1000), /* bits 4:0 */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_FRAME_CFG (0x400C + 0x1000*a) */ + .aif_bits_per_lane = REG_FIELD_ID(0x400C, 0, 9, 4, 0x1000), /* bits 9:0 */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_ACTV_SLOT_EN_TX (0x4010 + 0x1000*a) */ + .aif_slot_en_tx_mask = REG_FIELD_ID(0x4010, 0, 31, 4, 0x1000), /* bits 31:0 */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_ACTV_SLOT_EN_RX (0x4030 + 0x1000*a) */ + .aif_slot_en_rx_mask = REG_FIELD_ID(0x4030, 0, 31, 4, 0x1000), /* bits 31:0 */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_LANE_CFG (0x4050 + 0x1000*a) */ + .aif_loopback_en = REG_FIELD_ID(0x4050, 31, 31, 4, 0x1000), /* bit 31 */ + .aif_ctrl_data_oe = REG_FIELD_ID(0x4050, 16, 16, 4, 0x1000), /* bit 16 */ + .aif_lane_en = REG_FIELD_ID(0x4050, 8, 15, 4, 0x1000), /* bits 15:8 */ + .aif_lane_dir = REG_FIELD_ID(0x4050, 0, 7, 4, 0x1000), /* bits 7:0 */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_MI2S_CFG (0x4054 + 0x1000*a) */ + .aif_mono_mode_rx = REG_FIELD_ID(0x4054, 1, 1, 4, 0x1000), /* bit 1 */ + .aif_mono_mode_tx = REG_FIELD_ID(0x4054, 0, 0, 4, 0x1000), /* bit 0 */ + + /* AUDIO_CORE_QAIF_AUD_INTFa_CFG (0x4058 + 0x1000*a) */ + .aif_full_cycle_en = REG_FIELD_ID(0x4058, 0, 0, 4, 0x1000), /* bit 0 */ + + .clk_name = (const char*[]) { + "gcc_lpass_config_clk", + "gcc_lpass_core_axim_clk", + "audio_core_cc_bus_clk" + }, + .num_clks = 3, + + .dai_driver = shikra_qaif_cpu_dai_driver, + .num_dai = ARRAY_SIZE(shikra_qaif_cpu_dai_driver), + /* Below Clocks should be mapped as per interface index */ + .dai_osr_clk_names = (const char *[]) { + "null" + }, + .dai_bit_clk_names = (const char *[]) { + "audio_core_cc_aif_if0_ibit_clk", /* if0 */ + "audio_core_cc_aif_if1_ibit_clk", /* if1 */ + "audio_core_cc_aif_if2_ibit_clk", /* if2 */ + "audio_core_cc_aif_if3_ibit_clk" /* if3 */ + }, + .init = shikra_qaif_init, + .exit = shikra_qaif_exit, + .alloc_stream_dma_idx = shikra_qaif_alloc_stream_dma_idx, + .free_stream_dma_idx = shikra_qaif_free_stream_dma_idx, + .get_dma_idx = shikra_qaif_get_dma_idx, +}; + +static const struct of_device_id shikra_qaif_cpu_device_id[] = { + {.compatible = "qcom,shikra-qaif-cpu", .data = &shikra_qaif_data}, + {} +}; +MODULE_DEVICE_TABLE(of, shikra_qaif_cpu_device_id); + +static struct platform_driver shikra_qaif_cpu_platform_driver = { + .driver = { + .name = "shikra-qaif-cpu", + .of_match_table = of_match_ptr(shikra_qaif_cpu_device_id), + .pm = &shikra_qaif_pm_ops, + }, + .probe = asoc_qcom_qaif_cpu_platform_probe, + .remove = asoc_qcom_qaif_cpu_platform_remove, + .shutdown = asoc_qcom_qaif_cpu_platform_shutdown, +}; +module_platform_driver(shikra_qaif_cpu_platform_driver); + +MODULE_DESCRIPTION("SHIKRA QAIF CPU DRIVER"); +MODULE_LICENSE("GPL");