Unverified Commit 6e1dda5f authored by Mark Brown's avatar Mark Brown
Browse files

Add support for audio on SC7280 based targets

Merge series from Srinivasa Rao Mandadapu <quic_srivasam@quicinc.com>:

This patch set is to add support for Audio over wcd codec,
digital mics, through digital codecs and without ADSP.

Changes Since V15:
    -- Bisect patches to avoid build failure in other architectures.
    -- Remove redundant variables lpass variant structure.
Changes Since V14:
    -- Split common wrapper function to separate wrapper for each handle in platform driver.
    -- Update cdc dma buffer handling with memremap with ioremap.
    -- Remove redundant error prints.
    -- Update irq flag.
Changes Since V13:
    -- Change bulk cdc clock voting to individual clock voting.
    -- Remove redundant code, conditional check and prints.
    -- Fix typo errors.
Changes Since V12:
    -- Fix arguments type mismatch.
Changes Since V11:
    -- Fix kernel robot issue on arguments type mismatch.
Changes Since V10:
    -- Split bulk clock voting to individual clock voting as per use case in cdc-dma driver.
    -- Add missing codec dma clocks.
    -- Update rxtx lpm buffer size.
Changes Since V9:
    -- Change individual clock voting to bulk clock voting of lpass-sc7280 platform driver.
    -- Remove redundant clocks in lpass variant structure.
    -- Add mclk for MI2S based headset path.
    -- Remove unused lpass variant structure members in lpass header.
Changes Since V8:
    -- Fix errors in sc7280 lpass cpu dt-bindings.
    -- Move to quicinc domain email id's.
Changes Since V7:
    -- Fix indentation errors.
    -- Bisect patches to avoid interdependency.
Changes Since V6:
    -- Split cdc dma regmap config macros.
    -- Add write dma reg fields for i2s path.
    -- Add helper function to distinguish rxtx and va dma ports.
    -- Optimizing clock and reg name in cpu dt-bindings.
    -- Update buffer management for cdc dma path.
    -- Remove Kconfig fields of machine driver.
Changes Since V5:
    -- Include MI2S primary node to snd_soc_dai_driver in lpass-sc7280 platform driver.
    -- Move dependency patch list to corresponding patch.
    -- Add support for missing cdc-dma ports.
    -- Change if/else conditional statements to switch cases.
    -- Add missing error handlings.
    -- Typo errors fix.
Changes Since V4:
    -- Remove unused variable in lpass-sc7280 platform driver.
Changes Since V3:
    -- Remove redundant power domain controls. As power domains can be configured from dtsi.
Changes Since V2:
    -- Split lpass sc7280 cpu driver patch and create regmap config patch.
    -- Create patches based on latest kernel tip.
    -- Add helper function to get dma control and lpaif handle.
    -- Remove unused variables.
Changes Since V1:
    -- Typo errors fix
    -- CPU driver readable/writable apis optimization.
    -- Add Missing config patch
    -- Add Common api for repeated dmactl initialization.
Srinivasa Rao Mandadapu (9):
  ASoC: qcom: Move lpass_pcm_data structure to lpass header
  ASoC: qcom: lpass: Add dma fields for codec dma lpass interface
  ASoC: qcom: Add helper function to get dma control and lpaif handle
  ASoC: qcom: Add register definition for codec rddma and wrdma
  ASoC: qcom: Add regmap config support for codec dma driver
  ASoC: qcom: Add support for codec dma driver
  ASoC: qcom: Add lpass CPU driver for codec dma control
  ASoC: dt-bindings: Add SC7280 lpass cpu bindings
  ASoC: qcom: lpass-sc7280: Add platform driver for lpass audio

 .../devicetree/bindings/sound/qcom,lpass-cpu.yaml  |  75 ++-
 sound/soc/qcom/Kconfig                             |  11 +
 sound/soc/qcom/Makefile                            |   4 +
 sound/soc/qcom/lpass-cdc-dma.c                     | 301 ++++++++++
 sound/soc/qcom/lpass-cpu.c                         | 253 ++++++++-
 sound/soc/qcom/lpass-lpaif-reg.h                   | 127 ++++-
 sound/soc/qcom/lpass-platform.c                    | 628 ++++++++++++++++++---
 sound/soc/qcom/lpass-sc7280.c                      | 438 ++++++++++++++
 sound/soc/qcom/lpass.h                             | 141 +++++
 9 files changed, 1890 insertions(+), 88 deletions(-)
 create mode 100644 sound/soc/qcom/lpass-cdc-dma.c
 create mode 100644 sound/soc/qcom/lpass-sc7280.c

--
2.7.4
parents 29990588 b62c4e5f
Loading
Loading
Loading
Loading
+67 −8
Original line number Diff line number Diff line
@@ -22,35 +22,41 @@ properties:
      - qcom,lpass-cpu
      - qcom,apq8016-lpass-cpu
      - qcom,sc7180-lpass-cpu
      - qcom,sc7280-lpass-cpu

  reg:
    maxItems: 2
    minItems: 2
    maxItems: 6
    description: LPAIF core registers

  reg-names:
    maxItems: 2
    minItems: 2
    maxItems: 6

  clocks:
    minItems: 3
    maxItems: 6
    maxItems: 7

  clock-names:
    minItems: 3
    maxItems: 6
    maxItems: 7

  interrupts:
    maxItems: 2
    minItems: 2
    maxItems: 4
    description: LPAIF DMA buffer interrupt

  interrupt-names:
    maxItems: 2
    minItems: 2
    maxItems: 4

  qcom,adsp:
    $ref: /schemas/types.yaml#/definitions/phandle
    description: Phandle for the audio DSP node

  iommus:
    maxItems: 2
    minItems: 2
    maxItems: 3
    description: Phandle to apps_smmu node with sid mask

  power-domains:
@@ -69,7 +75,7 @@ patternProperties:
  "^dai-link@[0-9a-f]$":
    type: object
    description: |
      LPASS CPU dai node for each I2S device. Bindings of each node
      LPASS CPU dai node for each I2S device or Soundwire device. Bindings of each node
      depends on the specific driver providing the functionality and
      properties.
    properties:
@@ -174,6 +180,59 @@ allOf:
        - iommus
        - power-domains

  - if:
      properties:
        compatible:
          contains:
            const: qcom,sc7280-lpass-cpu

    then:
      properties:
        clock-names:
          oneOf:
            - items:   #for I2S
                - const: aon_cc_audio_hm_h
                - const: core_cc_sysnoc_mport_core
                - const: core_cc_ext_if1_ibit
            - items:   #for Soundwire
                - const: aon_cc_audio_hm_h
                - const: audio_cc_codec_mem0
                - const: audio_cc_codec_mem1
                - const: audio_cc_codec_mem2
            - items:   #for HDMI
                - const: aon_cc_audio_hm_h

        reg-names:
          anyOf:
            - items:   #for I2S
                - const: lpass-lpaif
            - items:   #for I2S and HDMI
                - const: lpass-hdmiif
                - const: lpass-lpaif
            - items:   #for I2S, soundwire and HDMI
                - const: lpass-hdmiif
                - const: lpass-lpaif
                - const: lpass-rxtx-cdc-dma-lpm
                - const: lpass-rxtx-lpaif
                - const: lpass-va-lpaif
                - const: lpass-va-cdc-dma-lpm
        interrupt-names:
          anyOf:
            - items:   #for I2S
                - const: lpass-irq-lpaif
            - items:   #for I2S and HDMI
                - const: lpass-irq-lpaif
                - const: lpass-irq-hdmi
            - items:   #for I2S, soundwire and HDMI
                - const: lpass-irq-lpaif
                - const: lpass-irq-hdmi
                - const: lpass-irq-vaif
                - const: lpass-irq-rxtxif

      required:
        - iommus
        - power-domains

examples:
  - |
    #include <dt-bindings/sound/sc7180-lpass.h>
+11 −0
Original line number Diff line number Diff line
@@ -20,6 +20,10 @@ config SND_SOC_LPASS_PLATFORM
	tristate
	select REGMAP_MMIO

config SND_SOC_LPASS_CDC_DMA
	tristate
	select REGMAP_MMIO

config SND_SOC_LPASS_IPQ806X
	tristate
	select SND_SOC_LPASS_CPU
@@ -36,6 +40,13 @@ config SND_SOC_LPASS_SC7180
	select SND_SOC_LPASS_PLATFORM
	select SND_SOC_LPASS_HDMI

config SND_SOC_LPASS_SC7280
	tristate
	select SND_SOC_LPASS_CPU
	select SND_SOC_LPASS_PLATFORM
	select SND_SOC_LPASS_HDMI
	select SND_SOC_LPASS_CDC_DMA

config SND_SOC_STORM
	tristate "ASoC I2S support for Storm boards"
	depends on GPIOLIB
+4 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
# Platform
snd-soc-lpass-cpu-objs := lpass-cpu.o
snd-soc-lpass-cdc-dma-objs := lpass-cdc-dma.o
snd-soc-lpass-hdmi-objs := lpass-hdmi.o
snd-soc-lpass-platform-objs := lpass-platform.o
snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o
snd-soc-lpass-apq8016-objs := lpass-apq8016.o
snd-soc-lpass-sc7180-objs := lpass-sc7180.o
snd-soc-lpass-sc7280-objs := lpass-sc7280.o

obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o
obj-$(CONFIG_SND_SOC_LPASS_CDC_DMA) += snd-soc-lpass-cdc-dma.o
obj-$(CONFIG_SND_SOC_LPASS_HDMI) += snd-soc-lpass-hdmi.o
obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o
obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o
obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o
obj-$(CONFIG_SND_SOC_LPASS_SC7180) += snd-soc-lpass-sc7180.o
obj-$(CONFIG_SND_SOC_LPASS_SC7280) += snd-soc-lpass-sc7280.o

# Machine
snd-soc-storm-objs := storm.o
+301 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2021 The Linux Foundation. All rights reserved.
 *
 * lpass-cdc-dma.c -- ALSA SoC CDC DMA CPU DAI driver for QTi LPASS
 */

#include <linux/clk.h>
#include <linux/module.h>
#include <linux/export.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>

#include "lpass-lpaif-reg.h"
#include "lpass.h"

#define CODEC_MEM_HZ_NORMAL 153600000

enum codec_dma_interfaces {
	LPASS_CDC_DMA_INTERFACE1 = 1,
	LPASS_CDC_DMA_INTERFACE2,
	LPASS_CDC_DMA_INTERFACE3,
	LPASS_CDC_DMA_INTERFACE4,
	LPASS_CDC_DMA_INTERFACE5,
	LPASS_CDC_DMA_INTERFACE6,
	LPASS_CDC_DMA_INTERFACE7,
	LPASS_CDC_DMA_INTERFACE8,
	LPASS_CDC_DMA_INTERFACE9,
	LPASS_CDC_DMA_INTERFACE10,
};

static void __lpass_get_dmactl_handle(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
				      struct lpaif_dmactl **dmactl, int *id)
{
	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
	struct snd_pcm_runtime *rt = substream->runtime;
	struct lpass_pcm_data *pcm_data = rt->private_data;
	struct lpass_variant *v = drvdata->variant;
	unsigned int dai_id = cpu_dai->driver->id;

	switch (dai_id) {
	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
		*dmactl = drvdata->rxtx_rd_dmactl;
		*id = pcm_data->dma_ch;
		break;
	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
		*dmactl = drvdata->rxtx_wr_dmactl;
		*id = pcm_data->dma_ch - v->rxtx_wrdma_channel_start;
		break;
	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX8:
		*dmactl = drvdata->va_wr_dmactl;
		*id = pcm_data->dma_ch - v->va_wrdma_channel_start;
		break;
	default:
		dev_err(soc_runtime->dev, "invalid dai id for dma ctl: %d\n", dai_id);
		break;
	}
}

static int __lpass_get_codec_dma_intf_type(int dai_id)
{
	int ret;

	switch (dai_id) {
	case LPASS_CDC_DMA_RX0:
	case LPASS_CDC_DMA_TX0:
	case LPASS_CDC_DMA_VA_TX0:
		ret = LPASS_CDC_DMA_INTERFACE1;
		break;
	case LPASS_CDC_DMA_RX1:
	case LPASS_CDC_DMA_TX1:
	case LPASS_CDC_DMA_VA_TX1:
		ret = LPASS_CDC_DMA_INTERFACE2;
		break;
	case LPASS_CDC_DMA_RX2:
	case LPASS_CDC_DMA_TX2:
	case LPASS_CDC_DMA_VA_TX2:
		ret = LPASS_CDC_DMA_INTERFACE3;
		break;
	case LPASS_CDC_DMA_RX3:
	case LPASS_CDC_DMA_TX3:
	case LPASS_CDC_DMA_VA_TX3:
		ret = LPASS_CDC_DMA_INTERFACE4;
		break;
	case LPASS_CDC_DMA_RX4:
	case LPASS_CDC_DMA_TX4:
	case LPASS_CDC_DMA_VA_TX4:
		ret = LPASS_CDC_DMA_INTERFACE5;
		break;
	case LPASS_CDC_DMA_RX5:
	case LPASS_CDC_DMA_TX5:
	case LPASS_CDC_DMA_VA_TX5:
		ret = LPASS_CDC_DMA_INTERFACE6;
		break;
	case LPASS_CDC_DMA_RX6:
	case LPASS_CDC_DMA_TX6:
	case LPASS_CDC_DMA_VA_TX6:
		ret = LPASS_CDC_DMA_INTERFACE7;
		break;
	case LPASS_CDC_DMA_RX7:
	case LPASS_CDC_DMA_TX7:
	case LPASS_CDC_DMA_VA_TX7:
		ret = LPASS_CDC_DMA_INTERFACE8;
		break;
	case LPASS_CDC_DMA_RX8:
	case LPASS_CDC_DMA_TX8:
	case LPASS_CDC_DMA_VA_TX8:
		ret = LPASS_CDC_DMA_INTERFACE9;
		break;
	case LPASS_CDC_DMA_RX9:
		ret  = LPASS_CDC_DMA_INTERFACE10;
		break;
	default:
		ret = -EINVAL;
		break;
	}
	return ret;
}

static int __lpass_platform_codec_intf_init(struct snd_soc_dai *dai,
					    struct snd_pcm_substream *substream)
{
	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
	struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0);
	struct lpaif_dmactl *dmactl = NULL;
	struct device *dev = soc_runtime->dev;
	int ret, id, codec_intf;
	unsigned int dai_id = cpu_dai->driver->id;

	codec_intf = __lpass_get_codec_dma_intf_type(dai_id);
	if (codec_intf < 0) {
		dev_err(dev, "failed to get codec_intf: %d\n", codec_intf);
		return codec_intf;
	}

	__lpass_get_dmactl_handle(substream, dai, &dmactl, &id);
	if (!dmactl)
		return -EINVAL;

	ret = regmap_fields_write(dmactl->codec_intf, id, codec_intf);
	if (ret) {
		dev_err(dev, "error writing to dmactl codec_intf reg field: %d\n", ret);
		return ret;
	}
	ret = regmap_fields_write(dmactl->codec_fs_sel, id, 0x0);
	if (ret) {
		dev_err(dev, "error writing to dmactl codec_fs_sel reg field: %d\n", ret);
		return ret;
	}
	ret = regmap_fields_write(dmactl->codec_fs_delay, id, 0x0);
	if (ret) {
		dev_err(dev, "error writing to dmactl codec_fs_delay reg field: %d\n", ret);
		return ret;
	}
	ret = regmap_fields_write(dmactl->codec_pack, id, 0x1);
	if (ret) {
		dev_err(dev, "error writing to dmactl codec_pack reg field: %d\n", ret);
		return ret;
	}
	ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_ON);
	if (ret) {
		dev_err(dev, "error writing to dmactl codec_enable reg field: %d\n", ret);
		return ret;
	}
	return 0;
}

static int lpass_cdc_dma_daiops_startup(struct snd_pcm_substream *substream,
				    struct snd_soc_dai *dai)
{
	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);

	switch (dai->id) {
	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
		clk_set_rate(drvdata->codec_mem0, CODEC_MEM_HZ_NORMAL);
		clk_prepare_enable(drvdata->codec_mem0);
		break;
	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
		clk_set_rate(drvdata->va_mem0, CODEC_MEM_HZ_NORMAL);
		clk_prepare_enable(drvdata->va_mem0);
		break;
	default:
		dev_err(soc_runtime->dev, "%s: invalid  interface: %d\n", __func__, dai->id);
		break;
	}
	return 0;
}

static void lpass_cdc_dma_daiops_shutdown(struct snd_pcm_substream *substream,
				      struct snd_soc_dai *dai)
{
	struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);

	switch (dai->id) {
	case LPASS_CDC_DMA_RX0 ... LPASS_CDC_DMA_RX9:
	case LPASS_CDC_DMA_TX0 ... LPASS_CDC_DMA_TX8:
		clk_disable_unprepare(drvdata->codec_mem0);
		break;
	case LPASS_CDC_DMA_VA_TX0 ... LPASS_CDC_DMA_VA_TX0:
		clk_disable_unprepare(drvdata->va_mem0);
		break;
	default:
		dev_err(soc_runtime->dev, "%s: invalid  interface: %d\n", __func__, dai->id);
		break;
	}
}

static int lpass_cdc_dma_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 = asoc_substream_to_rtd(substream);
	struct lpaif_dmactl *dmactl = NULL;
	unsigned int ret, regval;
	unsigned int channels = params_channels(params);
	int id;

	switch (channels) {
	case 1:
		regval = LPASS_CDC_DMA_INTF_ONE_CHANNEL;
		break;
	case 2:
		regval = LPASS_CDC_DMA_INTF_TWO_CHANNEL;
		break;
	case 4:
		regval = LPASS_CDC_DMA_INTF_FOUR_CHANNEL;
		break;
	case 6:
		regval = LPASS_CDC_DMA_INTF_SIX_CHANNEL;
		break;
	case 8:
		regval = LPASS_CDC_DMA_INTF_EIGHT_CHANNEL;
		break;
	default:
		dev_err(soc_runtime->dev, "invalid PCM config\n");
		return -EINVAL;
	}

	__lpass_get_dmactl_handle(substream, dai, &dmactl, &id);
	if (!dmactl)
		return -EINVAL;

	ret = regmap_fields_write(dmactl->codec_channel, id, regval);
	if (ret) {
		dev_err(soc_runtime->dev,
			"error writing to dmactl codec_channel reg field: %d\n", ret);
		return ret;
	}
	return 0;
}

static int lpass_cdc_dma_daiops_trigger(struct snd_pcm_substream *substream,
				    int cmd, struct snd_soc_dai *dai)
{
	struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream);
	struct lpaif_dmactl *dmactl;
	int ret = 0, id;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
	case SNDRV_PCM_TRIGGER_RESUME:
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		__lpass_platform_codec_intf_init(dai, substream);
		break;
	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		__lpass_get_dmactl_handle(substream, dai, &dmactl, &id);
		if (!dmactl)
			return -EINVAL;

		ret = regmap_fields_write(dmactl->codec_enable, id, LPAIF_DMACTL_ENABLE_OFF);
		if (ret) {
			dev_err(soc_runtime->dev,
				"error writing to dmactl codec_enable reg: %d\n", ret);
			return ret;
		}
		break;
	default:
		ret = -EINVAL;
		dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, cmd);
		break;
	}
	return ret;
}

const struct snd_soc_dai_ops asoc_qcom_lpass_cdc_dma_dai_ops = {
	.startup	= lpass_cdc_dma_daiops_startup,
	.shutdown	= lpass_cdc_dma_daiops_shutdown,
	.hw_params	= lpass_cdc_dma_daiops_hw_params,
	.trigger	= lpass_cdc_dma_daiops_trigger,
};
EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cdc_dma_dai_ops);

MODULE_DESCRIPTION("QTi LPASS CDC DMA Driver");
MODULE_LICENSE("GPL");
+252 −1
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@
#define LPASS_CPU_I2S_SD2_3_MASK	GENMASK(3, 2)
#define LPASS_CPU_I2S_SD0_1_2_MASK	GENMASK(2, 0)
#define LPASS_CPU_I2S_SD0_1_2_3_MASK	GENMASK(3, 0)
#define LPASS_REG_READ 1
#define LPASS_REG_WRITE 0

/*
 * Channel maps for Quad channel playbacks on MI2S Secondary
@@ -798,6 +800,189 @@ static struct regmap_config lpass_hdmi_regmap_config = {
	.cache_type = REGCACHE_FLAT,
};

static bool __lpass_rxtx_regmap_accessible(struct device *dev, unsigned int reg, bool rw)
{
	struct lpass_data *drvdata = dev_get_drvdata(dev);
	struct lpass_variant *v = drvdata->variant;
	int i;

	for (i = 0; i < v->rxtx_irq_ports; ++i) {
		if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i))
			return true;
		if (reg == LPAIF_RXTX_IRQEN_REG(v, i))
			return true;
		if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i))
			return true;
	}

	for (i = 0; i < v->rxtx_rdma_channels; ++i) {
		if (reg == LPAIF_CDC_RXTX_RDMACTL_REG(v, i, LPASS_CDC_DMA_RX0))
			return true;
		if (reg == LPAIF_CDC_RXTX_RDMABASE_REG(v, i, LPASS_CDC_DMA_RX0))
			return true;
		if (reg == LPAIF_CDC_RXTX_RDMABUFF_REG(v, i, LPASS_CDC_DMA_RX0))
			return true;
		if (rw == LPASS_REG_READ) {
			if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0))
				return true;
		}
		if (reg == LPAIF_CDC_RXTX_RDMAPER_REG(v, i, LPASS_CDC_DMA_RX0))
			return true;
		if (reg == LPAIF_CDC_RXTX_RDMA_INTF_REG(v, i, LPASS_CDC_DMA_RX0))
			return true;
	}

	for (i = 0; i < v->rxtx_wrdma_channels; ++i) {
		if (reg == LPAIF_CDC_RXTX_WRDMACTL_REG(v, i + v->rxtx_wrdma_channel_start,
							LPASS_CDC_DMA_TX3))
			return true;
		if (reg == LPAIF_CDC_RXTX_WRDMABASE_REG(v, i + v->rxtx_wrdma_channel_start,
							LPASS_CDC_DMA_TX3))
			return true;
		if (reg == LPAIF_CDC_RXTX_WRDMABUFF_REG(v, i + v->rxtx_wrdma_channel_start,
							LPASS_CDC_DMA_TX3))
			return true;
		if (rw == LPASS_REG_READ) {
			if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i, LPASS_CDC_DMA_RX0))
				return true;
		}
		if (reg == LPAIF_CDC_RXTX_WRDMAPER_REG(v, i + v->rxtx_wrdma_channel_start,
							LPASS_CDC_DMA_TX3))
			return true;
		if (reg == LPAIF_CDC_RXTX_WRDMA_INTF_REG(v, i + v->rxtx_wrdma_channel_start,
							LPASS_CDC_DMA_TX3))
			return true;
	}
	return false;
}

static bool lpass_rxtx_regmap_writeable(struct device *dev, unsigned int reg)
{
	return __lpass_rxtx_regmap_accessible(dev, reg, LPASS_REG_WRITE);
}

static bool lpass_rxtx_regmap_readable(struct device *dev, unsigned int reg)
{
	return __lpass_rxtx_regmap_accessible(dev, reg, LPASS_REG_READ);
}

static bool lpass_rxtx_regmap_volatile(struct device *dev, unsigned int reg)
{
	struct lpass_data *drvdata = dev_get_drvdata(dev);
	struct lpass_variant *v = drvdata->variant;
	int i;

	for (i = 0; i < v->rxtx_irq_ports; ++i) {
		if (reg == LPAIF_RXTX_IRQCLEAR_REG(v, i))
			return true;
		if (reg == LPAIF_RXTX_IRQSTAT_REG(v, i))
			return true;
	}

	for (i = 0; i < v->rxtx_rdma_channels; ++i)
		if (reg == LPAIF_CDC_RXTX_RDMACURR_REG(v, i, LPASS_CDC_DMA_RX0))
			return true;

	for (i = 0; i < v->rxtx_wrdma_channels; ++i)
		if (reg == LPAIF_CDC_RXTX_WRDMACURR_REG(v, i + v->rxtx_wrdma_channel_start,
							LPASS_CDC_DMA_TX3))
			return true;

	return false;
}

static bool __lpass_va_regmap_accessible(struct device *dev, unsigned int reg, bool rw)
{
	struct lpass_data *drvdata = dev_get_drvdata(dev);
	struct lpass_variant *v = drvdata->variant;
	int i;

	for (i = 0; i < v->va_irq_ports; ++i) {
		if (reg == LPAIF_VA_IRQCLEAR_REG(v, i))
			return true;
		if (reg == LPAIF_VA_IRQEN_REG(v, i))
			return true;
		if (reg == LPAIF_VA_IRQSTAT_REG(v, i))
			return true;
	}

	for (i = 0; i < v->va_wrdma_channels; ++i) {
		if (reg == LPAIF_CDC_VA_WRDMACTL_REG(v, i + v->va_wrdma_channel_start,
							LPASS_CDC_DMA_VA_TX0))
			return true;
		if (reg == LPAIF_CDC_VA_WRDMABASE_REG(v, i + v->va_wrdma_channel_start,
							LPASS_CDC_DMA_VA_TX0))
			return true;
		if (reg == LPAIF_CDC_VA_WRDMABUFF_REG(v, i + v->va_wrdma_channel_start,
							LPASS_CDC_DMA_VA_TX0))
			return true;
		if (rw == LPASS_REG_READ) {
			if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start,
							LPASS_CDC_DMA_VA_TX0))
				return true;
		}
		if (reg == LPAIF_CDC_VA_WRDMAPER_REG(v, i + v->va_wrdma_channel_start,
							LPASS_CDC_DMA_VA_TX0))
			return true;
		if (reg == LPAIF_CDC_VA_WRDMA_INTF_REG(v, i + v->va_wrdma_channel_start,
							LPASS_CDC_DMA_VA_TX0))
			return true;
	}
	return false;
}

static bool lpass_va_regmap_writeable(struct device *dev, unsigned int reg)
{
	return __lpass_va_regmap_accessible(dev, reg, LPASS_REG_WRITE);
}

static bool lpass_va_regmap_readable(struct device *dev, unsigned int reg)
{
	return __lpass_va_regmap_accessible(dev, reg, LPASS_REG_READ);
}

static bool lpass_va_regmap_volatile(struct device *dev, unsigned int reg)
{
	struct lpass_data *drvdata = dev_get_drvdata(dev);
	struct lpass_variant *v = drvdata->variant;
	int i;

	for (i = 0; i < v->va_irq_ports; ++i) {
		if (reg == LPAIF_VA_IRQCLEAR_REG(v, i))
			return true;
		if (reg == LPAIF_VA_IRQSTAT_REG(v, i))
			return true;
	}

	for (i = 0; i < v->va_wrdma_channels; ++i) {
		if (reg == LPAIF_CDC_VA_WRDMACURR_REG(v, i + v->va_wrdma_channel_start,
							LPASS_CDC_DMA_VA_TX0))
			return true;
	}

	return false;
}

static struct regmap_config lpass_rxtx_regmap_config = {
	.reg_bits = 32,
	.reg_stride = 4,
	.val_bits = 32,
	.writeable_reg = lpass_rxtx_regmap_writeable,
	.readable_reg = lpass_rxtx_regmap_readable,
	.volatile_reg = lpass_rxtx_regmap_volatile,
	.cache_type = REGCACHE_FLAT,
};

static struct regmap_config lpass_va_regmap_config = {
	.reg_bits = 32,
	.reg_stride = 4,
	.val_bits = 32,
	.writeable_reg = lpass_va_regmap_writeable,
	.readable_reg = lpass_va_regmap_readable,
	.volatile_reg = lpass_va_regmap_volatile,
	.cache_type = REGCACHE_FLAT,
};

static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev,
						struct device_node *node,
						const char *name)
@@ -857,6 +1042,8 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
		}
		if (id == LPASS_DP_RX) {
			data->hdmi_port_enable = 1;
		} else if (is_cdc_dma_port(id)) {
			data->codec_dma_enable = 1;
		} else {
			data->mi2s_playback_sd_mode[id] =
				of_lpass_cpu_parse_sd_lines(dev, node,
@@ -868,10 +1055,33 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev,
	}
}

static int of_lpass_cdc_dma_clks_parse(struct device *dev,
					struct lpass_data *data)
{
	data->codec_mem0 = devm_clk_get(dev, "audio_cc_codec_mem0");
	if (IS_ERR(data->codec_mem0))
		return PTR_ERR(data->codec_mem0);

	data->codec_mem1 = devm_clk_get(dev, "audio_cc_codec_mem1");
	if (IS_ERR(data->codec_mem1))
		return PTR_ERR(data->codec_mem1);

	data->codec_mem2 = devm_clk_get(dev, "audio_cc_codec_mem2");
	if (IS_ERR(data->codec_mem2))
		return PTR_ERR(data->codec_mem2);

	data->va_mem0 = devm_clk_get(dev, "aon_cc_va_mem0");
	if (IS_ERR(data->va_mem0))
		return PTR_ERR(data->va_mem0);

	return 0;
}

int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
{
	struct lpass_data *drvdata;
	struct device_node *dsp_of_node;
	struct resource *res;
	struct lpass_variant *variant;
	struct device *dev = &pdev->dev;
	const struct of_device_id *match;
@@ -897,6 +1107,47 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)

	of_lpass_cpu_parse_dai_data(dev, drvdata);

	if (drvdata->codec_dma_enable) {
		drvdata->rxtx_lpaif =
				devm_platform_ioremap_resource_byname(pdev, "lpass-rxtx-lpaif");
		if (IS_ERR(drvdata->rxtx_lpaif))
			return PTR_ERR(drvdata->rxtx_lpaif);

		drvdata->va_lpaif = devm_platform_ioremap_resource_byname(pdev, "lpass-va-lpaif");
		if (IS_ERR(drvdata->va_lpaif))
			return PTR_ERR(drvdata->va_lpaif);

		lpass_rxtx_regmap_config.max_register = LPAIF_CDC_RXTX_WRDMAPER_REG(variant,
					variant->rxtx_wrdma_channels +
					variant->rxtx_wrdma_channel_start, LPASS_CDC_DMA_TX3);

		drvdata->rxtx_lpaif_map = devm_regmap_init_mmio(dev, drvdata->rxtx_lpaif,
					&lpass_rxtx_regmap_config);
		if (IS_ERR(drvdata->rxtx_lpaif_map))
			return PTR_ERR(drvdata->rxtx_lpaif_map);

		lpass_va_regmap_config.max_register = LPAIF_CDC_VA_WRDMAPER_REG(variant,
					variant->va_wrdma_channels +
					variant->va_wrdma_channel_start, LPASS_CDC_DMA_VA_TX0);

		drvdata->va_lpaif_map = devm_regmap_init_mmio(dev, drvdata->va_lpaif,
					&lpass_va_regmap_config);
		if (IS_ERR(drvdata->va_lpaif_map))
			return PTR_ERR(drvdata->va_lpaif_map);

		ret = of_lpass_cdc_dma_clks_parse(dev, drvdata);
		if (ret) {
			dev_err(dev, "failed to get cdc dma clocks %d\n", ret);
			return ret;
		}

		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-rxtx-cdc-dma-lpm");
		drvdata->rxtx_cdc_dma_lpm_buf = res->start;

		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-va-cdc-dma-lpm");
		drvdata->va_cdc_dma_lpm_buf = res->start;
	}

	drvdata->lpaif = devm_platform_ioremap_resource_byname(pdev, "lpass-lpaif");
	if (IS_ERR(drvdata->lpaif))
		return PTR_ERR(drvdata->lpaif);
@@ -939,7 +1190,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)

	for (i = 0; i < variant->num_dai; i++) {
		dai_id = variant->dai_driver[i].id;
		if (dai_id == LPASS_DP_RX)
		if (dai_id == LPASS_DP_RX || is_cdc_dma_port(dai_id))
			continue;

		drvdata->mi2s_osr_clk[dai_id] = devm_clk_get_optional(dev,
Loading