From: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
To: Srinivas Kandagatla <srini@kernel.org>
Cc: Mark Brown <broonie@kernel.org>,
Liam Girdwood <lgirdwood@gmail.com>,
Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.com>,
linux-sound@vger.kernel.org, linux-arm-msm@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>,
Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>,
Sarath Ganapathiraju <sarath.ganapathiraju@oss.qualcomm.com>,
Kumar Anurag Singh <kumar.singh@oss.qualcomm.com>,
Mohammad Rafi Shaik <mohammad.rafi.shaik@oss.qualcomm.com>
Subject: [PATCH v3 6/9] ASoC: qcom: Add QAIF PCM operations
Date: Mon, 24 Aug 2026 12:06:24 +0530 [thread overview]
Message-ID: <20260824063627.3595610-7-harendra.gautam@oss.qualcomm.com> (raw)
In-Reply-To: <20260824063627.3595610-1-harendra.gautam@oss.qualcomm.com>
Allocate and map the DMA buffer in open(), reserve a stream DMA index
and perform the one-time QAIF hardware initialization. Program the DMA
configuration in hw_params(), set the DMA clock rate and program the
DMA base, buffer and period registers in prepare(), and report the
current DMA position from pointer(). Release all resources in close().
The DMA clocks themselves are prepared and enabled by the CPU DAI
driver's startup and shutdown callbacks.
Support mmap() for CIF and AIF paths, and add copy() to transfer PCM
data between userspace and the DMA buffer.
Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
sound/soc/qcom/qaif-platform.c | 544 +++++++++++++++++++++++++++++++++
1 file changed, 544 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 000000000000..4b4647641d11
--- /dev/null
+++ b/sound/soc/qcom/qaif-platform.c
@@ -0,0 +1,544 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ * qaif-platform.c -- ALSA SoC PCM platform driver for the Qualcomm Audio Interface (QAIF)
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "qaif-reg.h"
+#include "qaif.h"
+
+#define DRV_NAME "qaif-platform"
+
+/* period: 5ms @ 48kHz/1ch/S16 min, 20ms @ 48kHz/8ch/S32 max */
+#define QAIF_PLATFORM_PERIOD_BYTES_MIN (48 * 5 * 1 * 2) /* 480 B */
+#define QAIF_PLATFORM_PERIOD_BYTES_MAX (48 * 20 * 8 * 4) /* 30720 B */
+#define QAIF_PLATFORM_PERIODS_MIN 2
+#define QAIF_PLATFORM_PERIODS_MAX 4
+/* QAIF_PLATFORM_PERIODS_MAX * QAIF_PLATFORM_PERIOD_BYTES_MAX = 122880 B */
+#define QAIF_PLATFORM_BUFFER_BYTES_MAX \
+ (QAIF_PLATFORM_PERIODS_MAX * QAIF_PLATFORM_PERIOD_BYTES_MAX)
+
+/* DMA CFG register bit positions */
+#define QAIF_DMACFG_SHRAM_WM_MASK GENMASK(11, 0)
+#define QAIF_DMACFG_BURST4_BIT BIT(18)
+#define QAIF_DMACFG_DYNCLK_BIT BIT(24)
+
+/* DMA CTL register bit positions */
+#define QAIF_DMACTL_ENABLE_BIT BIT(0)
+#define QAIF_ALL_CLIENTS_MASK (QAIF_BITMASK_AIF_RDDMA_WRDMA | \
+ QAIF_BITMASK_CIF_RDDMA_WRDMA)
+
+static const struct snd_pcm_hardware qaif_platform_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_BYTES_MAX,
+ .period_bytes_min = QAIF_PLATFORM_PERIOD_BYTES_MIN,
+ .period_bytes_max = QAIF_PLATFORM_PERIOD_BYTES_MAX,
+ .periods_min = QAIF_PLATFORM_PERIODS_MIN,
+ .periods_max = QAIF_PLATFORM_PERIODS_MAX,
+ .fifo_size = 0,
+};
+
+static int qaif_map_ee_resource(struct qaif_drv_data *drvdata)
+{
+ const struct qaif_variant *v = drvdata->variant;
+ struct regmap *map = drvdata->audio_qaif_map;
+ int ret;
+
+ ret = regmap_write(map, QAIF_EE_RDDMA_MAP_REG(v), GENMASK(v->num_rddma - 1, 0));
+ if (ret)
+ return ret;
+ ret = regmap_write(map, QAIF_EE_WRDMA_MAP_REG(v), GENMASK(v->num_wrdma - 1, 0));
+ if (ret)
+ return ret;
+ if (v->num_intf > 0) {
+ ret = regmap_write(map, QAIF_EE_INTF_MAP_REG(v), GENMASK(v->num_intf - 1, 0));
+ if (ret)
+ return ret;
+ }
+ ret = regmap_write(map, QAIF_EE_CODEC_RDDMA_MAP_REG(v),
+ GENMASK(v->num_codec_rddma - 1, 0));
+ if (ret)
+ return ret;
+ return regmap_write(map, QAIF_EE_CODEC_WRDMA_MAP_REG(v),
+ GENMASK(v->num_codec_wrdma - 1, 0));
+}
+
+static int qaif_map_dma_path(struct qaif_drv_data *drvdata)
+{
+ struct regmap *map = drvdata->audio_qaif_map;
+ int ret;
+
+ if (drvdata->variant->qxm_type != QAIF_QXM0) {
+ dev_err(regmap_get_device(map),
+ "only QXM0 is supported, qxm_type=%d\n",
+ drvdata->variant->qxm_type);
+ return -EINVAL;
+ }
+
+ ret = regmap_write(map, QAIF_RDDMA_MAP_QXM, QAIF_QXM0);
+ if (ret)
+ return ret;
+ ret = regmap_write(map, QAIF_WRDMA_MAP_QXM, QAIF_QXM0);
+ if (ret)
+ return ret;
+ ret = regmap_write(map, QAIF_CDC_RDDMA_MAP_QXM, QAIF_QXM0);
+ if (ret)
+ return ret;
+ return regmap_write(map, QAIF_CDC_WRDMA_MAP_QXM, QAIF_QXM0);
+}
+
+static int qaif_config_shram(struct qaif_drv_data *drvdata)
+{
+ const struct qaif_variant *v = drvdata->variant;
+ struct regmap *map = drvdata->audio_qaif_map;
+ u32 start_addr, shram_len;
+ int ret, i;
+
+ if (v->qxm_type != QAIF_QXM0) {
+ dev_err(regmap_get_device(map),
+ "only QXM0 is supported, qxm_type=%d\n",
+ v->qxm_type);
+ return -EINVAL;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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_CDC_RDDMA_QXM0_SHRAM_ST_ADDR(i),
+ start_addr + (shram_len * i));
+ if (ret)
+ return ret;
+ ret = regmap_write(map, QAIF_CDC_RDDMA_QXM0_SHRAM_LEN(i), shram_len);
+ if (ret)
+ return ret;
+ }
+
+ 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_CDC_WRDMA_QXM0_SHRAM_ST_ADDR(i),
+ start_addr + (shram_len * i));
+ if (ret)
+ return ret;
+ ret = regmap_write(map, QAIF_CDC_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;
+
+ if (drvdata->qaif_hw_configured)
+ return 0;
+
+ ret = qaif_config_shram(drvdata);
+ if (ret) {
+ dev_err(component->dev, "failed to config shram: %d\n", ret);
+ return ret;
+ }
+
+ ret = qaif_map_ee_resource(drvdata);
+ if (ret) {
+ dev_err(component->dev, "failed to map EE resources: %d\n", ret);
+ return ret;
+ }
+
+ ret = qaif_map_dma_path(drvdata);
+ if (ret)
+ return ret;
+
+ drvdata->qaif_hw_configured = true;
+ return 0;
+}
+
+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 qaif_drv_data *drvdata = snd_soc_component_get_drvdata(component);
+ const struct qaif_variant *v = drvdata->variant;
+ unsigned int dai_id = cpu_dai->driver->id;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ struct qaif_dma_mem_info *dma_mem_info;
+ struct qaif_pcm_data *data;
+ int ret, stream_dma_idx, dma_reg_idx, dir = substream->stream;
+
+ dma_reg_idx = v->get_dma_idx(dai_id);
+ if (dma_reg_idx < 0) {
+ dev_err(component->dev, "invalid DMA register index for DAI %u: %d\n",
+ dai_id, dma_reg_idx);
+ return dma_reg_idx;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ runtime->private_data = data;
+
+ buf->dev.dev = component->dev;
+ buf->private_data = NULL;
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+
+ dma_mem_info = kzalloc(sizeof(*dma_mem_info), GFP_KERNEL);
+ if (!dma_mem_info) {
+ ret = -ENOMEM;
+ goto err_free_data;
+ }
+ dma_mem_info->alloc_size = qaif_platform_hardware.buffer_bytes_max;
+ dma_mem_info->vaddr = dma_alloc_coherent(component->dev,
+ dma_mem_info->alloc_size,
+ &dma_mem_info->dma_addr,
+ GFP_KERNEL);
+ if (!dma_mem_info->vaddr) {
+ ret = -ENOMEM;
+ goto err_free_mem_info;
+ }
+
+ mutex_lock(&drvdata->stream_lock);
+ if (!v->alloc_stream_dma_idx) {
+ ret = -EINVAL;
+ goto err_unlock;
+ }
+ stream_dma_idx = v->alloc_stream_dma_idx(drvdata, dir, dai_id);
+ if (stream_dma_idx < 0) {
+ ret = stream_dma_idx;
+ goto err_unlock;
+ }
+ mutex_unlock(&drvdata->stream_lock);
+
+ data->stream_dma_idx = stream_dma_idx;
+ data->dma_reg_idx = dma_reg_idx;
+
+ buf->bytes = qaif_platform_hardware.buffer_bytes_max;
+ buf->addr = dma_mem_info->dma_addr;
+ buf->area = (unsigned char *)dma_mem_info->vaddr;
+
+ if (qaif_is_cif_dma_port(dai_id)) {
+ WRITE_ONCE(drvdata->cif_substream[stream_dma_idx], substream);
+ drvdata->cif_dma_heap[stream_dma_idx] = dma_mem_info;
+ } else {
+ WRITE_ONCE(drvdata->aif_substream[stream_dma_idx], substream);
+ drvdata->aif_dma_heap[stream_dma_idx] = dma_mem_info;
+ }
+
+ snd_soc_set_runtime_hwparams(substream, &qaif_platform_hardware);
+ runtime->dma_bytes = qaif_platform_hardware.buffer_bytes_max;
+ snd_pcm_set_runtime_buffer(substream, buf);
+
+ /*
+ * The DMA buffer/period length registers are programmed in 64-bit
+ * (8-byte) words, so constrain buffer and period sizes to that step
+ * to keep the ALSA and hardware sizes identical.
+ */
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+ BIT(QAIF_DMA_BYTES_TO_WORDS_SHIFT));
+ if (ret >= 0)
+ ret = snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+ BIT(QAIF_DMA_BYTES_TO_WORDS_SHIFT));
+ if (ret >= 0)
+ ret = snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ if (ret < 0) {
+ dev_err(soc_runtime->dev, "setting constraints failed: %d\n", ret);
+ if (qaif_is_cif_dma_port(dai_id)) {
+ WRITE_ONCE(drvdata->cif_substream[stream_dma_idx], NULL);
+ drvdata->cif_dma_heap[stream_dma_idx] = NULL;
+ } else {
+ WRITE_ONCE(drvdata->aif_substream[stream_dma_idx], NULL);
+ drvdata->aif_dma_heap[stream_dma_idx] = NULL;
+ }
+ mutex_lock(&drvdata->stream_lock);
+ if (v->free_stream_dma_idx)
+ v->free_stream_dma_idx(drvdata, stream_dma_idx, dai_id);
+ mutex_unlock(&drvdata->stream_lock);
+ goto err_free_dma;
+ }
+ return 0;
+
+err_unlock:
+ mutex_unlock(&drvdata->stream_lock);
+err_free_dma:
+ dma_free_coherent(component->dev, dma_mem_info->alloc_size,
+ dma_mem_info->vaddr, dma_mem_info->dma_addr);
+err_free_mem_info:
+ kfree(dma_mem_info);
+err_free_data:
+ kfree(data);
+ return ret;
+}
+
+static int qaif_platform_pcmops_close(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;
+ struct qaif_pcm_data *data = substream->runtime->private_data;
+ struct qaif_dma_mem_info *dma_mem_info;
+ unsigned int dai_id = cpu_dai->driver->id;
+
+ if (qaif_is_cif_dma_port(dai_id)) {
+ WRITE_ONCE(drvdata->cif_substream[data->stream_dma_idx], NULL);
+ dma_mem_info = drvdata->cif_dma_heap[data->stream_dma_idx];
+ drvdata->cif_dma_heap[data->stream_dma_idx] = NULL;
+ } else {
+ WRITE_ONCE(drvdata->aif_substream[data->stream_dma_idx], NULL);
+ dma_mem_info = drvdata->aif_dma_heap[data->stream_dma_idx];
+ drvdata->aif_dma_heap[data->stream_dma_idx] = NULL;
+ }
+
+ mutex_lock(&drvdata->stream_lock);
+ if (v->free_stream_dma_idx)
+ v->free_stream_dma_idx(drvdata, data->stream_dma_idx, dai_id);
+ mutex_unlock(&drvdata->stream_lock);
+
+ if (dma_mem_info) {
+ dma_free_coherent(component->dev, dma_mem_info->alloc_size,
+ dma_mem_info->vaddr, dma_mem_info->dma_addr);
+ kfree(dma_mem_info);
+ }
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ substream->runtime->private_data = NULL;
+
+ kfree(data);
+ 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_pcm_data *data = substream->runtime->private_data;
+ unsigned int dai_id = cpu_dai->driver->id;
+ int idx = data->dma_reg_idx;
+ int ret;
+
+ mutex_lock(&drvdata->stream_lock);
+ ret = qaif_init(component);
+ mutex_unlock(&drvdata->stream_lock);
+ if (ret) {
+ dev_err(soc_runtime->dev, "qaif_init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(drvdata->audio_qaif_map,
+ qaif_dmacfg_reg(v, idx, substream->stream, dai_id),
+ QAIF_DMACFG_BURST4_BIT | QAIF_DMACFG_SHRAM_WM_MASK,
+ QAIF_DMACFG_BURST4_BIT | QAIF_DMACTL_WM_5);
+ if (ret)
+ dev_err(soc_runtime->dev, "error updating DMA CFG: %d\n", ret);
+ return ret;
+}
+
+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;
+ struct qaif_pcm_data *data = substream->runtime->private_data;
+ unsigned int dai_id = cpu_dai->driver->id;
+ int idx = data->dma_reg_idx;
+ int ret;
+
+ ret = regmap_update_bits(drvdata->audio_qaif_map,
+ qaif_dmactl_reg(v, idx, substream->stream, dai_id),
+ QAIF_DMACTL_ENABLE_BIT, 0);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error disabling DMA: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(drvdata->audio_qaif_map,
+ qaif_dmacfg_reg(v, idx, substream->stream, dai_id), 0);
+ if (ret)
+ dev_err(soc_runtime->dev, "error clearing DMA CFG: %d\n", 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 regmap *map = drvdata->audio_qaif_map;
+ struct qaif_pcm_data *data = runtime->private_data;
+ unsigned int dai_id = cpu_dai->driver->id;
+ int idx = data->dma_reg_idx;
+ int ret, dir = substream->stream;
+
+ ret = clk_set_rate(drvdata->aud_dma_clk, QAIF_DMA_CLOCK_FREQ);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error setting aud_dma_clk rate: %d\n", ret);
+ return ret;
+ }
+ ret = clk_set_rate(drvdata->aud_dma_mem_clk, QAIF_DMA_CLOCK_FREQ);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error setting aud_dma_mem_clk rate: %d\n", ret);
+ return ret;
+ }
+
+ 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),
+ lower_32_bits(runtime->dma_addr));
+ if (ret) {
+ dev_err(soc_runtime->dev, "error writing to dma base reg: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(map, qaif_dmabuff_reg(v, idx, dir, dai_id),
+ (snd_pcm_lib_buffer_bytes(substream) >>
+ QAIF_DMA_BYTES_TO_WORDS_SHIFT) - 1);
+ if (ret) {
+ dev_err(soc_runtime->dev, "error writing to dma buf 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) >>
+ QAIF_DMA_BYTES_TO_WORDS_SHIFT) - 1);
+ if (ret)
+ dev_err(soc_runtime->dev, "error writing to dma period reg: %d\n", ret);
+ return ret;
+}
+
+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;
+ struct regmap *map = drvdata->audio_qaif_map;
+ struct qaif_pcm_data *data = substream->runtime->private_data;
+ unsigned int dai_id = cpu_dai->driver->id;
+ unsigned int base_addr, curr_addr;
+ int idx = data->dma_reg_idx;
+ int ret, dir = substream->stream;
+
+ 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 SNDRV_PCM_POS_XRUN;
+ }
+
+ 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 SNDRV_PCM_POS_XRUN;
+ }
+
+ return bytes_to_frames(substream->runtime,
+ (curr_addr - base_addr) %
+ snd_pcm_lib_buffer_bytes(substream));
+}
+
+static int qaif_platform_pcmops_mmap(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ return dma_mmap_coherent(component->dev, vma,
+ runtime->dma_area, runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+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 buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+ size_t channel_bytes = buffer_bytes / rt->channels;
+ unsigned long offset;
+ void *dma_buf;
+ size_t copied;
+
+ if (channel < 0 || channel >= rt->channels)
+ return -EINVAL;
+
+ /* Absolute byte offset into the configured (not the max) buffer. */
+ offset = pos + channel * channel_bytes;
+ if (offset > buffer_bytes || bytes > buffer_bytes - offset)
+ return -EINVAL;
+
+ dma_buf = rt->dma_area + offset;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ copied = copy_from_iter(dma_buf, bytes, buf);
+ else
+ copied = copy_to_iter(dma_buf, bytes, buf);
+
+ return (copied != bytes) ? -EFAULT : 0;
+}
--
2.34.1
next prev parent reply other threads:[~2026-08-24 6:36 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-24 6:36 [PATCH v3 0/9] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
2026-08-24 6:36 ` [PATCH v3 1/9] ASoC: dt-bindings: qcom,qaif-cpu: Add binding Harendra Gautam
2026-08-24 6:46 ` sashiko-bot
2026-08-24 8:39 ` Manuel Ebner
2026-08-24 9:14 ` Harendra Gautam
2026-08-25 5:59 ` Krzysztof Kozlowski
2026-08-26 8:31 ` Mohammad Rafi Shaik
2026-08-26 9:39 ` Harendra Gautam
2026-08-26 9:43 ` Srinivas Kandagatla
2026-08-26 9:55 ` Harendra Gautam
2026-08-26 10:01 ` Srinivas Kandagatla
2026-08-26 10:06 ` Harendra Gautam
2026-08-26 10:25 ` Harendra Gautam
2026-08-26 23:32 ` Mark Brown
2026-08-27 4:00 ` Harendra Gautam
2026-08-24 6:36 ` [PATCH v3 2/9] ASoC: qcom: Add QAIF shared data structures and variant interface Harendra Gautam
2026-08-24 6:36 ` [PATCH v3 3/9] ASoC: qcom: Add QAIF hardware register map Harendra Gautam
2026-08-24 6:48 ` sashiko-bot
2026-08-24 6:36 ` [PATCH v3 4/9] ASoC: qcom: Add QAIF CPU DAI ops, regmap, DT parsing and platform init Harendra Gautam
2026-08-24 6:49 ` sashiko-bot
2026-08-24 6:36 ` [PATCH v3 5/9] ASoC: soc-core: Add snd_soc_of_xlate_dai_name() generic helper Harendra Gautam
2026-08-25 4:20 ` Kuninori Morimoto
2026-08-24 6:36 ` Harendra Gautam [this message]
2026-08-24 6:53 ` [PATCH v3 6/9] ASoC: qcom: Add QAIF PCM operations sashiko-bot
2026-08-24 9:25 ` Markus Elfring
2026-08-24 10:10 ` Harendra Gautam
2026-08-25 6:07 ` Krzysztof Kozlowski
2026-08-24 6:36 ` [PATCH v3 7/9] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register Harendra Gautam
2026-08-24 6:53 ` sashiko-bot
2026-08-24 6:36 ` [PATCH v3 8/9] ASoC: qcom: Add Shikra QAIF support Harendra Gautam
2026-08-26 8:35 ` Mohammad Rafi Shaik
2026-08-26 8:58 ` Harendra Gautam
2026-08-26 10:13 ` Srinivas Kandagatla
2026-08-26 10:37 ` Harendra Gautam
2026-08-24 6:36 ` [PATCH v3 9/9] MAINTAINERS: Add Qualcomm QAIF driver entry Harendra Gautam
2026-08-24 8:22 ` Abel Vesa
2026-08-24 8:31 ` Harendra Gautam
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260824063627.3595610-7-harendra.gautam@oss.qualcomm.com \
--to=harendra.gautam@oss.qualcomm.com \
--cc=broonie@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=krzysztof.kozlowski@oss.qualcomm.com \
--cc=kumar.singh@oss.qualcomm.com \
--cc=lgirdwood@gmail.com \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=mohammad.rafi.shaik@oss.qualcomm.com \
--cc=perex@perex.cz \
--cc=prasad.kumpatla@oss.qualcomm.com \
--cc=robh@kernel.org \
--cc=sarath.ganapathiraju@oss.qualcomm.com \
--cc=srini@kernel.org \
--cc=tiwai@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox