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>,
linux-sound@vger.kernel.org, linux-arm-msm@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH v2 09/11] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register
Date: Wed, 1 Jul 2026 16:27:55 +0530 [thread overview]
Message-ID: <20260701105757.2779738-10-harendra.gautam@oss.qualcomm.com> (raw)
In-Reply-To: <20260701105757.2779738-1-harendra.gautam@oss.qualcomm.com>
Complete the QAIF PCM platform support by registering the component,
requesting the QAIF interrupt, and handling DMA interrupt events.
The QAIF hardware reports DMA events through a summary interrupt register
and per-DMA status registers. Add the top-level IRQ handler and AIF/CIF DMA
handlers so period interrupts notify ALSA with snd_pcm_period_elapsed(),
xrun conditions are reported, and bus errors stop the affected stream.
Add suspend and resume callbacks to put the QAIF regmap into cache-only
mode while the clocks are disabled, then re-enable the clocks and sync the
cached register state on resume.
Read the SMMU stream ID from the iommus property during platform
registration so the existing resource setup code can program the SID
mapping correctly. Also add the component copy callback for transferring
PCM data through the runtime DMA buffer.
Signed-off-by: Harendra Gautam <harendra.gautam@oss.qualcomm.com>
---
sound/soc/qcom/qaif-platform.c | 331 +++++++++++++++++++++++++++++++++
1 file changed, 331 insertions(+)
diff --git a/sound/soc/qcom/qaif-platform.c b/sound/soc/qcom/qaif-platform.c
index 643def474aa0..b2d175811b2f 100644
--- a/sound/soc/qcom/qaif-platform.c
+++ b/sound/soc/qcom/qaif-platform.c
@@ -916,2 +916,333 @@ static int qaif_platform_pcmops_mmap(struct snd_soc_component *component,
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 irq_type,
+ enum qaif_dma_dir 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;
+ irqreturn_t ret = IRQ_NONE;
+ u32 mask = 0;
+
+ if (dma_type == QAIF_DMA_WRDMA)
+ stream_offset = (irq_type == QAIF_AIF_IRQ) ? v->wrdma_start
+ : v->codec_wrdma_start;
+ else
+ stream_offset = 0;
+
+ if (dma_type == QAIF_DMA_WRDMA)
+ num_dma = (irq_type == QAIF_AIF_IRQ) ? v->num_wrdma : v->num_codec_wrdma;
+ else
+ num_dma = (irq_type == QAIF_AIF_IRQ) ? v->num_rddma : v->num_codec_rddma;
+ mask = GENMASK(num_dma - 1, 0);
+ rv = regmap_read(drvdata->audio_qaif_map, stat_reg_addr, ®);
+ if (rv) {
+ dev_err(regmap_get_device(drvdata->audio_qaif_map),
+ "error reading stat reg 0x%x: %d\n", stat_reg_addr, rv);
+ return IRQ_NONE;
+ }
+
+ 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;
+ 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:
+ dev_warn_ratelimited(regmap_get_device(drvdata->audio_qaif_map),
+ "QAIF DMA xRun\n");
+ ret = IRQ_HANDLED;
+ break;
+
+ case QAIF_IRQ_ERROR:
+ snd_pcm_stop(stream, SNDRV_PCM_STATE_DISCONNECTED);
+ dev_err(regmap_get_device(drvdata->audio_qaif_map),
+ "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;
+
+ 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, QAIF_DMA_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, QAIF_DMA_WRDMA, QAIF_IRQ_PERIOD,
+ substream);
+ }
+ 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, QAIF_DMA_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, QAIF_DMA_RDDMA, QAIF_IRQ_UNDERFLOW,
+ substream);
+ }
+ if (summary_irq_status & QAIF_SUMMARY_BITMASK_AIF_ERR_RSP_RDDMA) {
+ ret |= qaif_process_dma_irq(drvdata,
+ QAIF_EE_RDDMA_ERR_RSP_IRQ_STAT_REG(v, QAIF_AIF_IRQ),
+ QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, QAIF_AIF_IRQ),
+ QAIF_AIF_IRQ, QAIF_DMA_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, QAIF_DMA_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;
+
+ 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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_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, QAIF_DMA_RDDMA, QAIF_IRQ_UNDERFLOW,
+ substream);
+ }
+
+ if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_ERR_RSP_RDDMA) {
+ ret |= qaif_process_dma_irq(drvdata,
+ QAIF_EE_RDDMA_ERR_RSP_IRQ_STAT_REG(v, QAIF_CIF_IRQ),
+ QAIF_EE_RDDMA_ERR_RSP_IRQ_CLR_REG(v, QAIF_CIF_IRQ),
+ QAIF_CIF_IRQ, QAIF_DMA_RDDMA, QAIF_IRQ_ERROR,
+ substream);
+ }
+ if (summary_irq_status & QAIF_SUMMARY_BITMASK_CIF_ERR_RSP_WRDMA) {
+ 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, QAIF_DMA_WRDMA, QAIF_IRQ_ERROR,
+ substream);
+ }
+
+ return ret;
+}
+
+#define QAIF_ALL_CLIENTS_MASK (QAIF_BITMASK_AIF_RDDMA_WRDMA | QAIF_BITMASK_CIF_RDDMA_WRDMA)
+
+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},
+};
+
+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) {
+ dev_err(regmap_get_device(drvdata->audio_qaif_map),
+ "error reading from irqstat reg: %d\n", rv);
+ return IRQ_NONE;
+ }
+ if (!(summary_irq_status & QAIF_ALL_CLIENTS_MASK))
+ return IRQ_NONE;
+ for (client = 0; client < ARRAY_SIZE(qaif_irq_clients); client++) {
+ 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 = drvdata->audio_qaif_map;
+
+ regcache_cache_only(map, true);
+ regcache_mark_dirty(map);
+ 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 = drvdata->audio_qaif_map;
+
+ regcache_cache_only(map, false);
+ return regcache_sync(map);
+}
+
+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;
+
+ 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)
+ return -EFAULT;
+ } else {
+ copied = copy_to_iter(dma_buf, bytes, buf);
+ if (copied != bytes)
+ 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,
+};
+
+static int qaif_parse_smmu_sid(struct platform_device *pdev,
+ struct qaif_drv_data *drvdata)
+{
+ struct device_node *np = pdev->dev.of_node;
+ int ret;
+ u32 sid;
+
+ ret = of_property_read_u32_index(np, "iommus", 1, &sid);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read SMMU SID from iommus property: %d\n", ret);
+ return ret;
+ }
+
+ drvdata->smmu_csid_bits = sid & 0x1;
+ dev_dbg(&pdev->dev, "sid=0x%x csid=0x%x\n", sid, drvdata->smmu_csid_bits);
+ return 0;
+}
+
+/**
+ * asoc_qcom_qaif_platform_register - Register the QAIF PCM platform component
+ * @pdev: Platform device
+ *
+ * Reads the SMMU stream ID from DT, requests the QAIF interrupt and
+ * registers the PCM platform component.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int asoc_qcom_qaif_platform_register(struct platform_device *pdev)
+{
+ struct qaif_drv_data *drvdata = platform_get_drvdata(pdev);
+ int ret;
+
+ if (!drvdata || !drvdata->variant)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "Invalid drvdata or variant\n");
+
+ ret = qaif_parse_smmu_sid(pdev, drvdata);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to parse SMMU SID\n");
+
+ drvdata->audio_qaif_irq = platform_get_irq(pdev, 0);
+ if (drvdata->audio_qaif_irq < 0)
+ return dev_err_probe(&pdev->dev, drvdata->audio_qaif_irq,
+ "Failed to get IRQ\n");
+
+ ret = devm_request_irq(&pdev->dev, drvdata->audio_qaif_irq,
+ asoc_platform_qaif_irq, IRQF_TRIGGER_HIGH,
+ "qaif-irq-audio-core", drvdata);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "irq request failed\n");
+
+ drvdata->qaif_init_ref_cnt = 0;
+ mutex_init(&drvdata->stream_lock);
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &qaif_component_driver,
+ NULL, 0);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to register platform component\n");
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_qcom_qaif_platform_register);
+
+MODULE_DESCRIPTION("Qualcomm Audio Interface (QAIF) PCM platform driver");
+MODULE_AUTHOR("Harendra Gautam <harendra.gautam@oss.qualcomm.com>");
+MODULE_LICENSE("GPL");
--
2.34.1
next prev parent reply other threads:[~2026-07-01 10:58 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-01 10:57 [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Harendra Gautam
2026-07-01 10:57 ` [PATCH v2 01/11] dt-bindings: sound: qcom,qaif-cpu: Add binding Harendra Gautam
2026-07-01 11:04 ` Konrad Dybcio
2026-07-02 6:52 ` Krzysztof Kozlowski
2026-07-01 11:09 ` sashiko-bot
2026-07-01 11:19 ` Mark Brown
2026-07-01 12:26 ` Mark Brown
2026-07-02 6:50 ` Krzysztof Kozlowski
2026-07-01 10:57 ` [PATCH v2 02/11] ASoC: qcom: Add QAIF hardware register map Harendra Gautam
2026-07-01 10:57 ` [PATCH v2 03/11] ASoC: qcom: Add QAIF shared data structures and variant interface Harendra Gautam
2026-07-01 11:26 ` sashiko-bot
2026-07-01 10:57 ` [PATCH v2 04/11] ASoC: qcom: Add QAIF CIF (CDC DMA) DAI ops Harendra Gautam
2026-07-01 11:09 ` sashiko-bot
2026-07-01 10:57 ` [PATCH v2 05/11] ASoC: qcom: Add QAIF AIF " Harendra Gautam
2026-07-01 11:14 ` sashiko-bot
2026-07-01 10:57 ` [PATCH v2 06/11] ASoC: qcom: Add generic of_xlate_dai_name helper and use it in lpass-cpu and qaif-cpu Harendra Gautam
2026-07-01 11:11 ` sashiko-bot
2026-07-02 7:12 ` Krzysztof Kozlowski
2026-07-01 10:57 ` [PATCH v2 07/11] ASoC: qcom: Add QAIF regmap, DT parsing and platform init Harendra Gautam
2026-07-01 11:11 ` sashiko-bot
2026-07-02 7:07 ` Krzysztof Kozlowski
2026-07-01 10:57 ` [PATCH v2 08/11] ASoC: qcom: Add QAIF PCM operations Harendra Gautam
2026-07-01 11:12 ` sashiko-bot
2026-07-01 10:57 ` Harendra Gautam [this message]
2026-07-01 11:27 ` [PATCH v2 09/11] ASoC: qcom: Add QAIF IRQ handling, suspend/resume and platform register sashiko-bot
2026-07-01 10:57 ` [PATCH v2 10/11] ASoC: qcom: Add Shikra QAIF support Harendra Gautam
2026-07-01 11:22 ` sashiko-bot
2026-07-02 7:01 ` Krzysztof Kozlowski
2026-07-01 10:57 ` [PATCH v2 11/11] MAINTAINERS: Add Qualcomm QAIF driver entry Harendra Gautam
2026-07-02 6:58 ` [PATCH v2 00/11] ASoC: qcom: Add QAIF driver for Shikra audio platform Krzysztof Kozlowski
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=20260701105757.2779738-10-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=lgirdwood@gmail.com \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-sound@vger.kernel.org \
--cc=robh@kernel.org \
--cc=srini@kernel.org \
/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