aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2020-05-05 16:38:45 +0100
committerSrinivas Kandagatla <srinivas.kandagatla@linaro.org>2021-03-16 16:00:31 +0000
commitf68709bd5d3da39139cf600f0687aed88081b9ee (patch)
tree0eb59acb93222ff55a8b1a355b0c66598a56a2d2
parent9c01122852eb38dd94c6de3b2e30dfc82c167f98 (diff)
ASoC: qcom: audio-reach: add modules
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
-rw-r--r--sound/soc/qcom/audio-reach/Makefile2
-rw-r--r--sound/soc/qcom/audio-reach/module.c1083
-rw-r--r--sound/soc/qcom/audio-reach/module.h5
3 files changed, 1089 insertions, 1 deletions
diff --git a/sound/soc/qcom/audio-reach/Makefile b/sound/soc/qcom/audio-reach/Makefile
index 55bf46861f323..ea30f03b7e443 100644
--- a/sound/soc/qcom/audio-reach/Makefile
+++ b/sound/soc/qcom/audio-reach/Makefile
@@ -1,2 +1,2 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-y += q6apm.o q6apm-static-graphs.o util.o
+obj-y += q6apm.o q6apm-static-graphs.o util.o module.o
diff --git a/sound/soc/qcom/audio-reach/module.c b/sound/soc/qcom/audio-reach/module.c
new file mode 100644
index 0000000000000..32dd5f3e8dab1
--- /dev/null
+++ b/sound/soc/qcom/audio-reach/module.c
@@ -0,0 +1,1083 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018, Linaro Limited
+// Support both in-band and out-of-band command modes
+// Support other flags in buffer_data time stamp flags
+//shared circular buffer
+
+
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/of_platform.h>
+#include <sound/soc.h>
+#include <linux/jiffies.h>
+#include "q6apm.h"
+#include "common.h"
+#include "graph.h"
+#include "util.h"
+
+struct apm_pcm_module_media_fmt_cmd {
+ struct apm_module_param_data param_data;
+ struct param_id_pcm_output_format_cfg header;
+ struct payload_pcm_output_format_cfg media_cfg;
+}__packed;
+
+struct apm_sh_module_media_fmt_cmd {
+ struct media_format header;
+ struct payload_media_fmt_pcm cfg;
+}__packed;
+
+#define APM_SHMEM_FMT_CFG_PSIZE(n) ALIGN( \
+ sizeof(struct apm_sh_module_media_fmt_cmd) + \
+ n * sizeof(uint8_t), 8);
+
+/* num of channels as argument */
+#define APM_PCM_MODULE_FMT_CMD_PSIZE(n) ALIGN( \
+ sizeof(struct apm_pcm_module_media_fmt_cmd) + \
+ n * sizeof(uint8_t), 8)
+#define APM_PCM_OUT_FMT_CFG_PSIZE(n) (sizeof( \
+ struct payload_pcm_output_format_cfg) + \
+ n * sizeof(uint8_t));
+
+struct apm_i2s_module_intf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_i2s_intf_cfg cfg;
+}__packed;
+#define APM_I2S_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_i2s_module_intf_cfg), \
+ 8)
+
+struct apm_i2s_module_hw_ep_mf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_hw_ep_mf mf;
+}__packed;
+#define APM_I2S_HW_EP_CFG_PSIZE ALIGN( \
+ sizeof(struct apm_i2s_module_hw_ep_mf_cfg), \
+ 8)
+
+struct apm_i2s_module_frame_size_factor_cfg {
+ struct apm_module_param_data param_data;
+ uint32_t frame_size_factor;
+}__packed;
+#define APM_I2S_FS_CFG_PSIZE ALIGN( \
+ sizeof(struct apm_i2s_module_frame_size_factor_cfg), \
+ 8)
+struct apm_rat_module_intf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_rat_mf cfg;
+}__packed;
+#define APM_RAT_MODULE_FMT_CMD_PSIZE(n) ALIGN( \
+ sizeof(struct apm_rat_module_intf_cfg) + \
+ n * sizeof(uint16_t), 8)
+
+struct apm_gain_module_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_gain_cfg gain_cfg;
+}__packed;
+#define APM_GAIN_CFG_PSIZE ALIGN(sizeof(struct apm_gain_module_cfg), 8)
+
+struct apm_mfc_media_format_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_mfc_media_format cfg;
+}__packed;
+
+#define APM_MFC_MEDIA_FMT_CFG_PSIZE(n) ALIGN( \
+ sizeof(struct apm_mfc_media_format_cfg) + \
+ n * sizeof(uint16_t), 8)
+
+static int audio_reach_graph_send_cmd_sync(struct q6apm_graph *graph,
+ struct gpr_pkt *pkt,
+ uint32_t rsp_opcode)
+{
+
+ struct device *dev = graph->dev;
+ struct gpr_hdr *hdr = &pkt->hdr;
+ int rc;
+
+ mutex_lock(&graph->cmd_lock);
+ graph->result.opcode = 0;
+ graph->result.status = 0;
+
+ rc = gpr_send_port_pkt(graph->port, pkt);
+ if (rc < 0)
+ goto err;
+
+ if (rsp_opcode)
+ rc = wait_event_timeout(graph->cmd_wait,
+ (graph->result.opcode == hdr->opcode) ||
+ (graph->result.opcode == rsp_opcode),
+ 5 * HZ);
+ else
+ rc = wait_event_timeout(graph->cmd_wait,
+ (graph->result.opcode == hdr->opcode),
+ 5 * HZ);
+
+ if (!rc) {
+ dev_err(dev, "CMD timeout for [%x] opcode\n", hdr->opcode);
+ rc = -ETIMEDOUT;
+ } else if (graph->result.status > 0) {
+ dev_err(dev, "DSP returned error[%x] %x\n", hdr->opcode,
+ graph->result.status);
+ rc = -EINVAL;
+ } else {
+ dev_err(dev, "DSP returned [%x]\n",
+ graph->result.status);
+ rc = 0;
+ }
+
+err:
+ mutex_unlock(&graph->cmd_lock);
+ return rc;
+}
+
+int audio_reach_mfc_set_media_format(struct q6apm_graph *graph,
+ struct audio_reach_module *module,
+ int direction, uint32_t rate,
+ uint32_t num_channels,
+ u8 channel_map[PCM_MAX_NUM_CHANNEL],
+ uint16_t bits_per_sample)
+{
+ struct gpr_pkt *pkt;
+ void *p;
+ int rc, payload_size;
+
+ struct apm_module_param_data *param_data;
+ struct param_id_mfc_media_format *cfg;
+
+ payload_size = APM_MFC_MEDIA_FMT_CFG_PSIZE(num_channels);
+
+ p = audio_reach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = p;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ cfg = p + APM_MODULE_PARAM_DATA_SIZE;
+
+ cfg->sample_rate = rate;
+ cfg->bit_width = bits_per_sample;
+ cfg->num_channels = num_channels;
+ cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ cfg->channel_mapping[1] = PCM_CHANNEL_R;
+
+ rc = q6apm_send_cmd(graph->apm, pkt);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int audio_reach_i2s_set_media_format(struct q6apm_graph *graph,
+ struct audio_reach_module *module,
+ int direction, uint32_t rate,
+ uint32_t num_channels,
+ u8 channel_map[PCM_MAX_NUM_CHANNEL],
+ uint16_t bits_per_sample)
+{
+ struct gpr_pkt *pkt;
+ void *p;
+ int rc, payload_size;
+ struct apm_module_param_data *param_data;
+ struct apm_i2s_module_intf_cfg *intf_cfg;
+ struct apm_i2s_module_hw_ep_mf_cfg *hw_cfg;
+ struct apm_i2s_module_frame_size_factor_cfg *fs_cfg;
+ int ic_sz, ep_sz, fs_sz;
+
+ ic_sz = APM_I2S_INTF_CFG_PSIZE;
+ ep_sz = APM_I2S_HW_EP_CFG_PSIZE;
+ fs_sz = APM_I2S_FS_CFG_PSIZE;
+
+ payload_size = ic_sz + ep_sz + fs_sz;
+
+ p = audio_reach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+ intf_cfg = p;
+
+ param_data = &intf_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_I2S_INTF_CFG;
+ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ intf_cfg->cfg.intf_idx = module->hw_interface_idx;
+ intf_cfg->cfg.sd_line_idx = module->sd_line_idx;
+ intf_cfg->cfg.ws_src = module->ws_src;
+
+ p += ic_sz;
+ hw_cfg = p;
+ param_data = &hw_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_MF_CFG;
+ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ hw_cfg->mf.sample_rate = rate;
+ hw_cfg->mf.bit_width = bits_per_sample;
+ hw_cfg->mf.num_channels = num_channels;
+ hw_cfg->mf.data_format = module->data_format;
+
+ p += ep_sz;
+ fs_cfg = p;
+ param_data = &fs_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_FRAME_SIZE_FACTOR;
+ param_data->param_size = fs_sz - APM_MODULE_PARAM_DATA_SIZE;
+ fs_cfg->frame_size_factor = 1;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int audio_reach_logging_set_media_format(struct q6apm_graph *graph,
+ struct audio_reach_module *module,
+ uint32_t log_code,
+ uint32_t log_tap_point_id)
+{
+
+ struct gpr_pkt *pkt;
+ void *p;
+ int rc, payload_size;
+ struct data_logging_config *cfg;
+ struct apm_module_param_data *param_data;
+
+ payload_size = sizeof(*cfg);
+ p = audio_reach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = p;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_DATA_LOGGING_CONFIG;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+ cfg = p;
+ cfg->log_code = log_code;
+ cfg->log_tap_point_id = log_tap_point_id;
+ cfg->mode = 0;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+
+int audio_reach_rat_set_media_format(struct q6apm_graph *graph,
+ struct audio_reach_module *module,
+ int direction, uint32_t rate,
+ uint32_t num_channels,
+ u8 channel_map[PCM_MAX_NUM_CHANNEL],
+ uint16_t bits_per_sample)
+{
+
+ struct gpr_pkt *pkt;
+ void *p;
+ int rc, payload_size;
+ struct param_id_rat_mf *cfg;
+ struct apm_module_param_data *param_data;
+
+ payload_size = APM_RAT_MODULE_FMT_CMD_PSIZE(num_channels);
+ p = audio_reach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ param_data = p;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_RAT_MEDIA_FORMAT;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ p = p + APM_MODULE_PARAM_DATA_SIZE;
+ cfg = p;
+ cfg->sample_rate = rate;
+ cfg->bits_per_sample = bits_per_sample;
+ cfg->q_factor = bits_per_sample - 1;
+ cfg->data_format = DATA_FORMAT_FIXED_POINT;
+ cfg->num_channels = num_channels;
+
+ //FIXME CHANNEL MAP should also come from Module define
+ cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ cfg->channel_mapping[1] = PCM_CHANNEL_R;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int audio_reach_pcm_set_media_format(struct q6apm_graph *graph,
+ struct audio_reach_module *module,
+ int direction, uint32_t rate,
+ uint32_t num_channels,
+ u8 channel_map[PCM_MAX_NUM_CHANNEL],
+ uint16_t bits_per_sample)
+{
+
+ struct gpr_pkt *pkt;
+ void *p;
+ int rc, payload_size;
+ struct apm_pcm_module_media_fmt_cmd *cfg;
+ struct apm_module_param_data *param_data;
+
+ payload_size = APM_PCM_MODULE_FMT_CMD_PSIZE(num_channels);
+
+ p = audio_reach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+ cfg = p;
+
+ param_data = &cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_PCM_OUTPUT_FORMAT_CFG;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ cfg->header.data_format = DATA_FORMAT_FIXED_POINT;
+ cfg->header.fmt_id = MEDIA_FMT_ID_PCM;
+ cfg->header.payload_size = APM_PCM_OUT_FMT_CFG_PSIZE(num_channels);
+
+ cfg->media_cfg.alignment = PCM_LSB_ALIGNED;
+ cfg->media_cfg.bit_width = bits_per_sample;
+ cfg->media_cfg.endianness = PCM_LITTLE_ENDIAN;
+ cfg->media_cfg.interleaved = module->interleave_type;
+ cfg->media_cfg.num_channels = num_channels;
+ cfg->media_cfg.q_factor = bits_per_sample -1;
+ cfg->media_cfg.bits_per_sample = bits_per_sample;
+ //FIXME CHANNEL MAP should also come from Module define
+ cfg->media_cfg.channel_mapping[0] = PCM_CHANNEL_L;
+ cfg->media_cfg.channel_mapping[1] = PCM_CHANNEL_R;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int audio_reach_shmem_set_media_format(struct q6apm_graph *graph,
+ struct audio_reach_module *module,
+ int direction, uint32_t rate,
+ uint32_t num_channels,
+ u8 channel_map[PCM_MAX_NUM_CHANNEL],
+ uint16_t bits_per_sample)
+{
+ struct gpr_pkt *pkt;
+ void *p;
+ int rc, payload_size;
+ struct media_format *header;
+ struct payload_media_fmt_pcm *cfg;
+
+ payload_size = APM_SHMEM_FMT_CFG_PSIZE(num_channels);
+ p = audio_reach_alloc_pkt(payload_size,
+ DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT,
+ 0,
+ graph->port->id, module->instance_id);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ p = p + GPR_HDR_SIZE;
+ header = p;
+
+ header->data_format = DATA_FORMAT_FIXED_POINT;
+ header->fmt_id = MEDIA_FMT_ID_PCM;
+ header->payload_size = payload_size - sizeof(*header);
+
+ p = p + sizeof(*header);
+ cfg = p;
+ cfg->sample_rate = rate;
+ cfg->bit_width = bits_per_sample;
+ cfg->alignment = PCM_LSB_ALIGNED;
+ cfg->bits_per_sample = bits_per_sample;
+ cfg->q_factor = bits_per_sample - 1;
+ cfg->endianness = PCM_LITTLE_ENDIAN;
+ cfg->num_channels = num_channels;
+ cfg->channel_mapping[0] = PCM_CHANNEL_L;
+ cfg->channel_mapping[1] = PCM_CHANNEL_R;
+
+ rc = audio_reach_graph_send_cmd_sync(graph, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int audio_reach_gain_set(
+ struct q6apm_graph *graph,
+ struct audio_reach_module *module)
+{
+ struct gpr_pkt *pkt;
+ void *p;
+ int rc, payload_size;
+ struct apm_gain_module_cfg *cfg;
+ struct apm_module_param_data *param_data;
+
+ payload_size = APM_GAIN_CFG_PSIZE;
+ p = audio_reach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ p = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+ cfg = p;
+
+ param_data = &cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = APM_PARAM_ID_GAIN;
+ param_data->param_size = payload_size - APM_MODULE_PARAM_DATA_SIZE;
+
+ cfg->gain_cfg.gain = module->gain;
+
+ rc = q6apm_send_cmd_sync(graph->apm, pkt, 0);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int audio_reach_set_media_format(struct q6apm_graph *graph,
+ struct audio_reach_module *module,
+ int direction, uint32_t rate,
+ uint32_t channels,
+ u8 channel_map[PCM_MAX_NUM_CHANNEL],
+ uint16_t bits_per_sample)
+{
+ int rc;
+
+ switch (module->module_id) {
+ case MODULE_ID_DATA_LOGGING:
+ rc = audio_reach_logging_set_media_format(graph, module, 0x19af, 1);
+ break;
+ case MODULE_ID_RATE_ADAPTED_TIMER:
+ rc = audio_reach_rat_set_media_format(graph, module,
+ direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+ break;
+ case MODULE_ID_PCM_DEC:
+ case MODULE_ID_PCM_CNV:
+ rc = audio_reach_pcm_set_media_format(graph, module,
+ direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+ break;
+ case MODULE_ID_I2S_SINK:
+ rc = audio_reach_i2s_set_media_format(graph, module,
+ direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+ break;
+ case MODULE_ID_WR_SHARED_MEM_EP:
+ rc = audio_reach_shmem_set_media_format(graph, module,
+ direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+ break;
+ case MODULE_ID_GAIN:
+ rc = audio_reach_gain_set(graph, module);
+ break;
+ case MODULE_ID_MFC:
+ rc = audio_reach_mfc_set_media_format(graph, module,
+ direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+ break;
+ default:
+ rc = 0;
+ }
+
+ return 0;
+}
+
+int audio_reach_set_media_format_modules(struct q6apm_graph *graph,
+ struct audio_reach_module_info *minfo,
+ int direction, uint32_t rate,
+ uint32_t channels,
+ u8 channel_map[PCM_MAX_NUM_CHANNEL],
+ uint16_t bits_per_sample)
+{
+ struct audio_reach_module *module;
+ struct q6apm *apm = graph->apm;
+ struct device *dev = graph->dev;
+ int i;
+
+ for (i = 0; i < minfo->num_modules; i++) {
+ module = q6apm_find_module(apm, minfo->iids[i]);
+ if (!module) {
+ dev_err(dev, "No matching Module found for graph\n");
+ return -EINVAL;
+ }
+ audio_reach_set_media_format(graph, module, direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+ }
+
+ return 0;
+}
+
+int q6apm_graph_media_format_shmem(struct q6apm_graph *graph,
+ int direction, uint32_t rate,
+ uint32_t channels,
+ u8 channel_map[PCM_MAX_NUM_CHANNEL],
+ uint16_t bits_per_sample)
+{
+ struct audio_reach_graph_info *info = graph->info;
+
+ return audio_reach_set_media_format_modules(graph, &info->rx_sh_ep,
+ direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+}
+
+int q6apm_graph_media_format_pcm(struct q6apm_graph *graph,
+ int direction, uint32_t rate,
+ uint32_t channels,
+ u8 channel_map[PCM_MAX_NUM_CHANNEL],
+ uint16_t bits_per_sample)
+{
+ struct audio_reach_graph_info *info = graph->info;
+ int rc;
+
+ rc = audio_reach_set_media_format_modules(graph,
+ &info->input_media_format,
+ direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+ if (rc)
+ return rc;
+
+ rc = audio_reach_set_media_format_modules(graph, &info->output_media_format,
+ direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+ if (rc)
+ return rc;
+
+ rc = audio_reach_set_media_format_modules(graph, &info->logging,
+ direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+ if (rc)
+ return 0;
+
+
+ rc = audio_reach_set_media_format_modules(graph, &info->tx_sh_ep,
+ direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+// if (rc)
+// return 0;
+
+ return audio_reach_set_media_format_modules(graph, &info->hw_ep,
+ direction, rate,
+ channels, channel_map,
+ bits_per_sample);
+
+}
+
+int graph_callback(struct gpr_resp_pkt *data, void *priv, int op)
+{
+ struct q6apm_graph *graph = priv;
+ struct device *dev = graph->dev;
+ struct gpr_hdr *hdr = &data->hdr;
+ struct gpr_ibasic_rsp_result_t *result;
+ int ret = -EINVAL;
+ uint32_t client_event = 0;
+
+ result = data->payload;
+
+ switch (hdr->opcode) {
+ case DATA_CMD_RSP_WR_SH_MEM_EP_DATA_BUFFER_DONE:
+ client_event = APM_CLIENT_EVENT_DATA_WRITE_DONE;
+ if (graph) {
+ struct data_cmd_rsp_wr_sh_mem_ep_data_buffer_done *done;
+ phys_addr_t phys;
+ unsigned long flags;
+
+ spin_lock_irqsave(&graph->lock, flags);
+
+
+ done = data->payload;
+ phys = graph->rx_data.buf[hdr->token].phys;
+
+ if (lower_32_bits(phys) != done->buf_addr_lsw ||
+ upper_32_bits(phys) != done->buf_addr_msw) {
+ dev_err(dev, "WR BUFF Expected Token %d addr %pa\n", hdr->token, &phys);
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ graph->result.opcode = hdr->opcode;
+ graph->result.status = done->status;
+ }
+ spin_unlock_irqrestore(&graph->lock, flags);
+ } else {
+ dev_err(dev, "No SH Mem module found\n");
+ ret = -EINVAL;
+ }
+ graph->cb(client_event, hdr->token, data->payload,
+ graph->priv);
+
+ break;
+ case APM_CMD_RSP_SHARED_MEM_MAP_REGIONS:
+ if (graph) {
+ struct apm_cmd_rsp_shared_mem_map_regions *rsp;
+
+ graph->result.opcode = hdr->opcode;
+ graph->result.status = 0;
+ rsp = data->payload;
+
+ if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
+ graph->rx_data.mem_map_handle = rsp->mem_map_handle;
+ else
+ graph->tx_data.mem_map_handle = rsp->mem_map_handle;
+
+ wake_up(&graph->cmd_wait);
+ ret = 0;
+ }
+ break;
+ case DATA_CMD_RSP_RD_SH_MEM_EP_DATA_BUFFER:
+ if (graph) {
+ struct data_cmd_rsp_rd_sh_mem_ep_data_buffer_done *done;
+ unsigned long flags;
+ phys_addr_t phys;
+
+ done = data->payload;
+ spin_lock_irqsave(&graph->lock, flags);
+ phys = graph->tx_data.buf[hdr->token].phys;
+
+ if (upper_32_bits(phys) != done->buf_addr_msw ||
+ lower_32_bits(phys) != done->buf_addr_lsw) {
+ dev_err(dev, "RD BUFF Expected addr %pa %08x-%08x\n",
+ &phys,
+ done->buf_addr_lsw,
+ done->buf_addr_msw);
+ ret = -EINVAL;
+ //goto done;
+ } else {
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&graph->lock, flags);
+ client_event = APM_CLIENT_EVENT_DATA_READ_DONE;
+ wake_up(&graph->cmd_wait);
+
+ graph->cb(client_event, hdr->token, data->payload,
+ graph->priv);
+ //FIXME get TIMESTAMP values
+ }
+
+ break;
+ //TODO FIXME
+ case DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED:
+ pr_err("GRAPH-DEBUG: %s: DATA_CMD_WR_SH_MEM_EP_EOS_RENDERED\n", __func__);
+ break;
+ case GPR_BASIC_RSP_RESULT:
+ pr_err("GRAPH-DEBUG: %s: %d GPR_BASIC_RSP_RESULT for %x\n",
+ __func__, __LINE__, result->opcode);
+ switch (result->opcode) {
+ case APM_CMD_SHARED_MEM_UNMAP_REGIONS:
+ if (graph) {
+ graph->result.opcode = result->opcode;
+ graph->result.status = 0;
+ pr_err("GRAPH-DEBUG: APM_CMD_SHARED_MEM_UNMAP_REGIONS token %d\n", hdr->token);
+ if (hdr->token == SNDRV_PCM_STREAM_PLAYBACK)
+ graph->rx_data.mem_map_handle = 0;
+ else
+ graph->tx_data.mem_map_handle = 0;
+
+ wake_up(&graph->cmd_wait);
+ ret = 0;
+ }
+
+ break;
+ case APM_CMD_SHARED_MEM_MAP_REGIONS:
+ case DATA_CMD_WR_SH_MEM_EP_MEDIA_FORMAT:
+ graph->result.opcode = result->opcode;
+ graph->result.status = result->status;
+ if (result->status) {
+ dev_err(dev,
+ "Error (%d) Processing 0x%08x cmd\n",
+ result->status, result->opcode);
+ ret = -EINVAL;
+ } else {
+ ret = 0;
+ }
+ wake_up(&graph->cmd_wait);
+ graph->cb(client_event, hdr->token, data->payload,
+ graph->priv);
+
+ }
+ break;
+ }
+
+ return ret;
+}
+
+static void audio_reach_graph_free_buf(struct q6apm_graph *graph)
+{
+ struct audio_reach_graph_data *port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&graph->lock, flags);
+ port = &graph->rx_data;
+ port->num_periods = 0;
+ kfree(port->buf);
+ port->buf = NULL;
+
+ port = &graph->tx_data;
+ port->num_periods = 0;
+ kfree(port->buf);
+ port->buf = NULL;
+ spin_unlock_irqrestore(&graph->lock, flags);
+}
+
+int q6apm_graph_get_tx_shmem_module_iid(struct q6apm_graph *graph)
+{
+ struct audio_reach_module *module;
+
+ module = q6apm_find_module(graph->apm, graph->info->tx_sh_ep.iids[0]);
+ if (!module)
+ return -ENODEV;
+
+ return module->instance_id;
+
+}
+
+int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph)
+{
+ struct audio_reach_module *module;
+
+ module = q6apm_find_module(graph->apm, graph->info->rx_sh_ep.iids[0]);
+ if (!module)
+ return -ENODEV;
+
+ return module->instance_id;
+
+}
+
+int q6apm_unmap_memory_regions(struct q6apm_graph *graph,
+ unsigned int dir)
+{
+ struct audio_reach_graph_data *data;
+ struct apm_cmd_shared_mem_unmap_regions *cmd = NULL;
+ struct gpr_pkt *pkt;
+ void *p;
+ int rc;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ if (!data->mem_map_handle) {
+ pr_err("GRAPH-DEBUG: Invalid memmap handle %s: %d GRAPH ID %d handle %x\n",
+ __func__, __LINE__,graph->id, data->mem_map_handle);
+ return 0;
+ }
+
+
+ p = audio_reach_alloc_apm_pkt(sizeof(*cmd),
+ APM_CMD_SHARED_MEM_UNMAP_REGIONS, dir,
+ graph->port->id);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ cmd = p + GPR_HDR_SIZE;
+ cmd->mem_map_handle = data->mem_map_handle;
+
+ rc = audio_reach_graph_send_cmd_sync(graph, pkt, APM_CMD_SHARED_MEM_UNMAP_REGIONS);
+ kfree(pkt);
+
+ audio_reach_graph_free_buf(graph);
+
+ return rc;
+}
+
+static int __audio_reach_map_memory_regions(struct q6apm_graph *graph,
+ unsigned int dir, size_t period_sz,
+ unsigned int periods,
+ bool is_contiguous)
+{
+ struct apm_cmd_shared_mem_map_regions *cmd = NULL;
+ struct apm_shared_map_region_payload *mregions = NULL;
+ struct audio_buffer *ab = NULL;
+ struct audio_reach_graph_data *data;
+ struct gpr_pkt *pkt;
+ void *p;
+ unsigned long flags;
+ uint32_t num_regions, buf_sz, payload_size;
+ int rc, i;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ //FIXME
+ if (is_contiguous) {
+ num_regions = 1;
+ buf_sz = period_sz * periods;
+ } else {
+ buf_sz = period_sz;
+ num_regions = periods;
+ }
+
+ /* DSP expects size should be aligned to 4K */
+ buf_sz = ALIGN(buf_sz, 4096);
+
+ payload_size = sizeof(*cmd) + (sizeof(*mregions) * num_regions);
+
+ p = audio_reach_alloc_apm_pkt(payload_size,
+ APM_CMD_SHARED_MEM_MAP_REGIONS, dir/*iid*/,
+ graph->port->id);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ p = p + GPR_HDR_SIZE;
+ cmd = p;
+ cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL;
+ cmd->num_regions = num_regions;
+
+ //FIXME..
+ cmd->property_flag = 0x0;
+
+ mregions = p + sizeof(*cmd);
+
+ spin_lock_irqsave(&graph->lock, flags);
+
+ for (i = 0; i < num_regions; i++) {
+ ab = &data->buf[i];
+ mregions->shm_addr_lsw = lower_32_bits(ab->phys);
+ mregions->shm_addr_msw = upper_32_bits(ab->phys);
+ mregions->mem_size_bytes = buf_sz;
+ ++mregions;
+ }
+ spin_unlock_irqrestore(&graph->lock, flags);
+
+ rc = audio_reach_graph_send_cmd_sync(graph, pkt,
+ APM_CMD_RSP_SHARED_MEM_MAP_REGIONS);
+
+ kfree(pkt);
+
+ return rc;
+}
+
+int q6apm_map_memory_regions(struct q6apm_graph *graph,
+ unsigned int dir, phys_addr_t phys,
+ size_t period_sz, unsigned int periods)
+{
+ struct audio_reach_graph_data *data;
+ struct audio_buffer *buf;
+ unsigned long flags;
+ int cnt;
+ int rc;
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ spin_lock_irqsave(&graph->lock, flags);
+
+ if (data->buf) {
+ dev_err(graph->dev, "Buffer already allocated\n");
+ spin_unlock_irqrestore(&graph->lock, flags);
+ return 0;
+ }
+
+ buf = kzalloc(((sizeof(struct audio_buffer)) * periods), GFP_ATOMIC);
+ if (!buf) {
+ spin_unlock_irqrestore(&graph->lock, flags);
+ return -ENOMEM;
+ }
+
+
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+ data = &graph->rx_data;
+ else
+ data = &graph->tx_data;
+
+ data->buf = buf;
+
+ buf[0].phys = phys;
+ buf[0].size = period_sz;
+
+ for (cnt = 1; cnt < periods; cnt++) {
+ if (period_sz > 0) {
+ buf[cnt].phys = buf[0].phys + (cnt * period_sz);
+ buf[cnt].size = period_sz;
+ }
+ }
+ data->num_periods = periods;
+
+ spin_unlock_irqrestore(&graph->lock, flags);
+
+ rc = __audio_reach_map_memory_regions(graph, dir, period_sz,
+ periods, 1);
+ if (rc < 0) {
+ dev_err(graph->dev, "Memory_map_regions failed\n");
+ audio_reach_graph_free_buf(graph);
+ }
+
+ return rc;
+}
+
+/* Shared Memory Modules */
+/* Packet Based */
+int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
+ uint32_t lsw_ts, uint32_t wflags)
+{
+ struct gpr_pkt *pkt;
+ void *p;
+ int rc, payload_size, iid;
+ struct apm_data_cmd_wr_sh_mem_ep_data_buffer *write;
+ struct audio_buffer *ab;
+ unsigned long flags;
+
+ payload_size = sizeof(*write);
+
+ iid = q6apm_graph_get_rx_shmem_module_iid(graph);
+ p = audio_reach_alloc_pkt(payload_size,
+ DATA_CMD_WR_SH_MEM_EP_DATA_BUFFER,
+ graph->rx_data.dsp_buf,
+ graph->port->id, iid);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ p = p + GPR_HDR_SIZE;
+ write = p;
+
+ spin_lock_irqsave(&graph->lock, flags);
+ ab = &graph->rx_data.buf[graph->rx_data.dsp_buf];
+
+
+
+ write->buf_addr_lsw = lower_32_bits(ab->phys);
+ write->buf_addr_msw = upper_32_bits(ab->phys);
+ write->buf_size = len;
+ write->timestamp_lsw = lsw_ts;
+ write->timestamp_msw = msw_ts;
+ write->mem_map_handle = graph->rx_data.mem_map_handle;
+
+ //FIXME use other flags
+ if (wflags == NO_TIMESTAMP)
+ write->flags = 0;//(wflags & 0x800000FF);
+ else
+ write->flags = 0x80000000;// | wflags);
+
+ graph->rx_data.dsp_buf++;
+
+ if (graph->rx_data.dsp_buf >= graph->rx_data.num_periods)
+ graph->rx_data.dsp_buf = 0;
+
+ spin_unlock_irqrestore(&graph->lock, flags);
+
+ rc = gpr_send_port_pkt(graph->port, pkt);
+
+ kfree(pkt);
+
+ return 0;
+}
+
+int q6apm_read(struct q6apm_graph *graph)
+{
+ struct data_cmd_rd_sh_mem_ep_data_buffer *read;
+ struct audio_reach_graph_data *port;
+ struct audio_buffer *ab;
+ struct gpr_pkt *pkt;
+ unsigned long flags;
+ int rc = 0, iid;
+ void *p;
+
+ iid = q6apm_graph_get_tx_shmem_module_iid(graph);
+ p = audio_reach_alloc_pkt(sizeof(*read),
+ DATA_CMD_RD_SH_MEM_EP_DATA_BUFFER,
+ graph->tx_data.dsp_buf,
+ graph->port->id, iid);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ read = p + GPR_HDR_SIZE;
+
+ spin_lock_irqsave(&graph->lock, flags);
+ port = &graph->tx_data;
+ ab = &port->buf[port->dsp_buf];
+
+ read->buf_addr_lsw = lower_32_bits(ab->phys);
+ read->buf_addr_msw = upper_32_bits(ab->phys);
+ read->mem_map_handle = port->mem_map_handle;
+ read->buf_size = ab->size;
+
+ port->dsp_buf++;
+
+ if (port->dsp_buf >= port->num_periods)
+ port->dsp_buf = 0;
+
+ spin_unlock_irqrestore(&graph->lock, flags);
+
+ rc = gpr_send_port_pkt(graph->port, pkt);
+ kfree(pkt);
+
+ return rc;
+}
+
+int audio_reach_shared_memory_send_eos(struct q6apm_graph *graph)
+{
+ struct data_cmd_wr_sh_mem_ep_eos *eos;
+ struct gpr_pkt *pkt;
+ int rc = 0, iid;
+ void *p;
+
+ iid = q6apm_graph_get_rx_shmem_module_iid(graph);
+ p = audio_reach_alloc_cmd_pkt(sizeof(*eos),
+ DATA_CMD_WR_SH_MEM_EP_EOS,
+ 0,
+ graph->port->id, iid);
+ if (IS_ERR(p))
+ return -ENOMEM;
+
+ pkt = p;
+ eos = p + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+
+ eos->policy = WR_SH_MEM_EP_EOS_POLICY_LAST;
+
+ rc = gpr_send_port_pkt(graph->port, pkt);
+ kfree(pkt);
+
+ return rc;
+}
diff --git a/sound/soc/qcom/audio-reach/module.h b/sound/soc/qcom/audio-reach/module.h
new file mode 100644
index 0000000000000..0ef65fae5bb54
--- /dev/null
+++ b/sound/soc/qcom/audio-reach/module.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __APM_MODULE__
+#define __APM_MODULE__
+#include "graph.h"
+#endif /* __APM_MODULE__ */