[PATCH 5/6] ASoC: qcom: q6apm: add watermark event support
From: Srinivas Kandagatla
Date: Tue May 19 2026 - 09:21:02 EST
Push-pull shared memory modules can report watermark events when the DSP
read/write index reaches configured circular buffer levels.
Add support for registering watermark levels with the shared memory module
and route the resulting module event to q6apm clients using a new
APM_CLIENT_EVENT_WATERMARK_EVENT event.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@xxxxxxxxxxxxxxxx>
---
sound/soc/qcom/qdsp6/audioreach.c | 36 ++++++++++++++++++++++
sound/soc/qcom/qdsp6/audioreach.h | 50 +++++++++++++++++++++++++++++++
sound/soc/qcom/qdsp6/q6apm.c | 19 ++++++++++++
sound/soc/qcom/qdsp6/q6apm.h | 2 ++
4 files changed, 107 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
index c984b12409dd..e6e9eb2e85aa 100644
--- a/sound/soc/qcom/qdsp6/audioreach.c
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -1118,6 +1118,42 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
return q6apm_send_cmd_sync(graph->apm, pkt, 0);
}
+int audioreach_shmem_register_event(struct q6apm_graph *graph, int bytes, int num_levels)
+{
+ struct apm_module_register_events *event;
+ struct event_cfg_sh_mem_pull_push_mode_watermark_t *level;
+ int i, payload_size;
+ struct gpr_pkt *pkt __free(kfree) = NULL;
+ void *p;
+
+ if (num_levels <= 0 || bytes <= 0)
+ return -EINVAL;
+
+ payload_size = sizeof(*event) + sizeof(*level) + num_levels * sizeof(uint32_t);
+
+ pkt = audioreach_alloc_cmd_pkt(payload_size, APM_CMD_REGISTER_MODULE_EVENTS, 0,
+ graph->port->id, graph->shm_iid);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ event = p;
+ event->module_instance_id = graph->shm_iid;
+ event->event_id = EVENT_ID_SH_MEM_PULL_PUSH_MODE_WATERMARK;
+ event->is_register = 1;
+ event->event_config_payload_size = sizeof(*level) + num_levels * sizeof(uint32_t);
+ p += sizeof(*event);
+ level = p;
+ level->num_water_mark_levels = num_levels;
+
+ for (i = 0; i < num_levels; i++)
+ level->level[i] = (i + 1) * bytes;
+
+ return audioreach_graph_send_cmd_sync(graph, pkt, 0);
+}
+EXPORT_SYMBOL_GPL(audioreach_shmem_register_event);
+
static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
const struct audioreach_module *module,
const struct audioreach_module_config *mcfg)
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
index 0504e8abc8e2..50c7aa9a82aa 100644
--- a/sound/soc/qcom/qdsp6/audioreach.h
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -62,10 +62,59 @@ struct q6apm_graph;
#define APM_CMD_GET_CFG 0x01001007
#define APM_CMD_SHARED_MEM_MAP_REGIONS 0x0100100C
#define APM_CMD_SHARED_MEM_UNMAP_REGIONS 0x0100100D
+#define APM_CMD_REGISTER_MODULE_EVENTS 0x0100100E
+#define APM_EVENT_MODULE_TO_CLIENT 0x03001000
+
#define APM_CMD_RSP_SHARED_MEM_MAP_REGIONS 0x02001001
+#define APM_MMAP_TOKEN_GID_MASK GENMASK(15, 0)
+#define APM_MMAP_TOKEN_MAP_TYPE_POS_BUF BIT(16)
+#define APM_MMAP_TOKEN_MAP_TYPE_SHIFT 16
+
#define APM_CMD_RSP_GET_CFG 0x02001000
#define APM_CMD_CLOSE_ALL 0x01001013
#define APM_CMD_REGISTER_SHARED_CFG 0x0100100A
+#define EVENT_ID_SH_MEM_PULL_PUSH_MODE_WATERMARK 0x0800101C
+
+/**
+ * struct event_cfg_sh_mem_pull_push_mode_watermark_t - Watermark config
+ * @num_water_mark_levels: Number of watermark levels.
+ * @level: Watermark levels.
+ *
+ * If @num_water_mark_levels is zero, no watermark levels are specified
+ * and watermark events are not supported.
+ */
+struct event_cfg_sh_mem_pull_push_mode_watermark_t {
+ uint32_t num_water_mark_levels;
+ uint32_t level[];
+} __packed;
+
+/**
+ * struct apm_module_register_events - Register or unregister module events
+ * @module_instance_id: Module instance identifier.
+ * @event_id: Module event identifier.
+ * @is_register: 1 to register the event, 0 to unregister it.
+ * @error_code: Error code for out-of-band command mode.
+ * @event_config_payload_size: Event configuration payload size in bytes.
+ * @reserved: Reserved for alignment; must be zero.
+ */
+struct apm_module_register_events {
+ uint32_t module_instance_id;
+ uint32_t event_id;
+ uint32_t is_register;
+ uint32_t error_code;
+ uint32_t event_config_payload_size;
+ uint32_t reserved;
+} __packed;
+
+/**
+ * struct apm_module_event - Module event descriptor
+ * @event_id: Module event identifier.
+ * @event_payload_size: Event payload size in bytes.
+ */
+struct apm_module_event {
+ uint32_t event_id;
+ uint32_t event_payload_size;
+} __packed;
#define APM_MEMORY_MAP_SHMEM8_4K_POOL 3
@@ -904,4 +953,5 @@ int audioreach_setup_push_pull(struct q6apm_graph *graph, phys_addr_t bphys,
uint32_t pos_buf_mem_map_handle, uint32_t size);
int audioreach_map_memory_position_buffer(struct q6apm_graph *graph, unsigned int dir);
+int audioreach_shmem_register_event(struct q6apm_graph *graph, int bytes, int num_levels);
#endif /* __AUDIOREACH_H__ */
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index 9235089c1b46..2e5b25b8d00f 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -557,6 +557,7 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
{
struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done_v2 *rd_done;
struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done_v2 *done;
+ struct apm_module_event *event;
const struct gpr_ibasic_rsp_result_t *result;
struct q6apm_graph *graph = priv;
const struct gpr_hdr *hdr = &data->hdr;
@@ -568,6 +569,16 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
result = data->payload;
switch (hdr->opcode) {
+ case APM_EVENT_MODULE_TO_CLIENT:
+ event = data->payload;
+ switch (event->event_id) {
+ case EVENT_ID_SH_MEM_PULL_PUSH_MODE_WATERMARK:
+ client_event = APM_CLIENT_EVENT_WATERMARK_EVENT;
+ graph->cb(client_event, hdr->token, data->payload, graph->priv);
+ break;
+ }
+
+ break;
case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE_V2:
if (!graph->ar_graph)
break;
@@ -623,6 +634,7 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
switch (result->opcode) {
case APM_CMD_SHARED_MEM_MAP_REGIONS:
case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT:
+ case APM_CMD_REGISTER_MODULE_EVENTS:
case APM_CMD_SET_CFG:
graph->result.opcode = result->opcode;
graph->result.status = result->status;
@@ -641,6 +653,13 @@ static int graph_callback(const struct gpr_resp_pkt *data, void *priv, int op)
return 0;
}
+int q6apm_register_watermark_event(struct q6apm_graph *graph, int water_mark_level_bytes,
+ int num_levels)
+{
+ return audioreach_shmem_register_event(graph, water_mark_level_bytes, num_levels);
+}
+EXPORT_SYMBOL_GPL(q6apm_register_watermark_event);
+
int q6apm_push_pull_config(struct q6apm_graph *graph, phys_addr_t bphys,
phys_addr_t pphys, uint32_t size)
{
diff --git a/sound/soc/qcom/qdsp6/q6apm.h b/sound/soc/qcom/qdsp6/q6apm.h
index 780933ff17e9..5cb51ca491dc 100644
--- a/sound/soc/qcom/qdsp6/q6apm.h
+++ b/sound/soc/qcom/qdsp6/q6apm.h
@@ -41,6 +41,7 @@
#define APM_CLIENT_EVENT_CMD_RUN_DONE 0x1008
#define APM_CLIENT_EVENT_DATA_WRITE_DONE 0x1009
#define APM_CLIENT_EVENT_DATA_READ_DONE 0x100a
+#define APM_CLIENT_EVENT_WATERMARK_EVENT 0x100b
#define APM_WRITE_TOKEN_MASK GENMASK(15, 0)
#define APM_WRITE_TOKEN_LEN_MASK GENMASK(31, 16)
#define APM_WRITE_TOKEN_LEN_SHIFT 16
@@ -164,4 +165,5 @@ bool q6apm_is_graph_in_push_pull_mode_from_id(struct device *dev, unsigned int g
int q6apm_push_pull_config(struct q6apm_graph *graph, phys_addr_t bphys,
phys_addr_t pphys, uint32_t size);
+int q6apm_register_watermark_event(struct q6apm_graph *graph, int watermark_bytes, int num_levels);
#endif /* __APM_GRAPH_ */
--
2.47.3