Unverified Commit 5477518b authored by Srinivas Kandagatla's avatar Srinivas Kandagatla Committed by Mark Brown
Browse files

ASoC: qdsp6: audioreach: add q6apm support



Add support to q6apm (Audio Process Manager) component which is
core Audioreach service running in the DSP.

Signed-off-by: default avatarSrinivas Kandagatla <srinivas.kandagatla@linaro.org>
Reviewed-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/r/20211026111655.1702-11-srinivas.kandagatla@linaro.org


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 44c28dbd
Loading
Loading
Loading
Loading
+305 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#include <linux/slab.h>
#include <linux/soc/qcom/apr.h>
#include <dt-bindings/soc/qcom,gpr.h>
#include "q6apm.h"
#include "audioreach.h"

/* SubGraph Config */
@@ -253,3 +254,307 @@ void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t token
				       APM_MODULE_INSTANCE_ID, true);
}
EXPORT_SYMBOL_GPL(audioreach_alloc_apm_cmd_pkt);

static void apm_populate_container_config(struct apm_container_obj *cfg,
					  struct audioreach_container *cont)
{

	/* Container Config */
	cfg->container_cfg.container_id = cont->container_id;
	cfg->container_cfg.num_prop = 4;

	/* Capability list */
	cfg->cap_data.prop_id = APM_CONTAINER_PROP_ID_CAPABILITY_LIST;
	cfg->cap_data.prop_size = APM_CONTAINER_PROP_ID_CAPABILITY_SIZE;
	cfg->num_capability_id = 1;
	cfg->capability_id = cont->capability_id;

	/* Graph Position */
	cfg->pos_data.prop_id = APM_CONTAINER_PROP_ID_GRAPH_POS;
	cfg->pos_data.prop_size = sizeof(struct apm_cont_prop_id_graph_pos);
	cfg->pos.graph_pos = cont->graph_pos;

	/* Stack size */
	cfg->stack_data.prop_id = APM_CONTAINER_PROP_ID_STACK_SIZE;
	cfg->stack_data.prop_size = sizeof(struct apm_cont_prop_id_stack_size);
	cfg->stack.stack_size = cont->stack_size;

	/* Proc domain */
	cfg->domain_data.prop_id = APM_CONTAINER_PROP_ID_PROC_DOMAIN;
	cfg->domain_data.prop_size = sizeof(struct apm_cont_prop_id_domain);
	cfg->domain.proc_domain = cont->proc_domain;
}

static void apm_populate_sub_graph_config(struct apm_sub_graph_data *cfg,
					  struct audioreach_sub_graph *sg)
{
	cfg->sub_graph_cfg.sub_graph_id = sg->sub_graph_id;
	cfg->sub_graph_cfg.num_sub_graph_prop = APM_SUB_GRAPH_CFG_NPROP;

	/* Perf Mode */
	cfg->perf_data.prop_id = APM_SUB_GRAPH_PROP_ID_PERF_MODE;
	cfg->perf_data.prop_size = APM_SG_PROP_ID_PERF_MODE_SIZE;
	cfg->perf.perf_mode = sg->perf_mode;

	/* Direction */
	cfg->dir_data.prop_id = APM_SUB_GRAPH_PROP_ID_DIRECTION;
	cfg->dir_data.prop_size = APM_SG_PROP_ID_DIR_SIZE;
	cfg->dir.direction = sg->direction;

	/* Scenario ID */
	cfg->sid_data.prop_id = APM_SUB_GRAPH_PROP_ID_SCENARIO_ID;
	cfg->sid_data.prop_size = APM_SG_PROP_ID_SID_SIZE;
	cfg->sid.scenario_id = sg->scenario_id;
}

static void apm_populate_connection_obj(struct apm_module_conn_obj *obj,
					struct audioreach_module *module)
{
	obj->src_mod_inst_id = module->src_mod_inst_id;
	obj->src_mod_op_port_id = module->src_mod_op_port_id;
	obj->dst_mod_inst_id = module->instance_id;
	obj->dst_mod_ip_port_id = module->in_port;
}

static void apm_populate_module_prop_obj(struct apm_mod_prop_obj *obj,
					 struct audioreach_module *module)
{

	obj->instance_id = module->instance_id;
	obj->num_props = 1;
	obj->prop_data_1.prop_id = APM_MODULE_PROP_ID_PORT_INFO;
	obj->prop_data_1.prop_size = APM_MODULE_PROP_ID_PORT_INFO_SZ;
	obj->prop_id_port.max_ip_port = module->max_ip_port;
	obj->prop_id_port.max_op_port = module->max_op_port;
}

struct audioreach_module *audioreach_get_container_last_module(
							struct audioreach_container *container)
{
	struct audioreach_module *module;

	list_for_each_entry(module, &container->modules_list, node) {
		if (module->dst_mod_inst_id == 0)
			return module;
	}

	return NULL;
}
EXPORT_SYMBOL_GPL(audioreach_get_container_last_module);

static bool is_module_in_container(struct audioreach_container *container, int module_iid)
{
	struct audioreach_module *module;

	list_for_each_entry(module, &container->modules_list, node) {
		if (module->instance_id == module_iid)
			return true;
	}

	return false;
}

struct audioreach_module *audioreach_get_container_first_module(
							struct audioreach_container *container)
{
	struct audioreach_module *module;

	/* get the first module from both connected or un-connected containers */
	list_for_each_entry(module, &container->modules_list, node) {
		if (module->src_mod_inst_id == 0 ||
		    !is_module_in_container(container, module->src_mod_inst_id))
			return module;
	}
	return NULL;
}
EXPORT_SYMBOL_GPL(audioreach_get_container_first_module);

struct audioreach_module *audioreach_get_container_next_module(
						struct audioreach_container *container,
						struct audioreach_module *module)
{
	int nmodule_iid = module->dst_mod_inst_id;
	struct audioreach_module *nmodule;

	list_for_each_entry(nmodule, &container->modules_list, node) {
		if (nmodule->instance_id == nmodule_iid)
			return nmodule;
	}

	return NULL;
}
EXPORT_SYMBOL_GPL(audioreach_get_container_next_module);

static void apm_populate_module_list_obj(struct apm_mod_list_obj *obj,
					 struct audioreach_container *container,
					 int sub_graph_id)
{
	struct audioreach_module *module;
	int i;

	obj->sub_graph_id = sub_graph_id;
	obj->container_id = container->container_id;
	obj->num_modules = container->num_modules;
	i = 0;
	list_for_each_container_module(module, container) {
		obj->mod_cfg[i].module_id = module->module_id;
		obj->mod_cfg[i].instance_id = module->instance_id;
		i++;
	}
}

static void audioreach_populate_graph(struct apm_graph_open_params *open,
				      struct list_head *sg_list,
				      int num_sub_graphs)
{
	struct apm_mod_conn_list_params *mc_data = open->mod_conn_list_data;
	struct apm_module_list_params *ml_data = open->mod_list_data;
	struct apm_prop_list_params *mp_data = open->mod_prop_data;
	struct apm_container_params *c_data = open->cont_data;
	struct apm_sub_graph_params *sg_data = open->sg_data;
	int ncontainer = 0, nmodule = 0, nconn = 0;
	struct apm_mod_prop_obj *module_prop_obj;
	struct audioreach_container *container;
	struct apm_module_conn_obj *conn_obj;
	struct audioreach_module *module;
	struct audioreach_sub_graph *sg;
	struct apm_container_obj *cobj;
	struct apm_mod_list_obj *mlobj;
	int i = 0;

	mlobj = &ml_data->mod_list_obj[0];

	list_for_each_entry(sg, sg_list, node) {
		struct apm_sub_graph_data *sg_cfg = &sg_data->sg_cfg[i++];

		apm_populate_sub_graph_config(sg_cfg, sg);

		list_for_each_entry(container, &sg->container_list, node) {
			cobj = &c_data->cont_obj[ncontainer];

			apm_populate_container_config(cobj, container);
			apm_populate_module_list_obj(mlobj, container, sg->sub_graph_id);

			list_for_each_container_module(module, container) {
				uint32_t src_mod_inst_id;

				src_mod_inst_id = module->src_mod_inst_id;

				module_prop_obj = &mp_data->mod_prop_obj[nmodule];
				apm_populate_module_prop_obj(module_prop_obj, module);

				if (src_mod_inst_id) {
					conn_obj = &mc_data->conn_obj[nconn];
					apm_populate_connection_obj(conn_obj, module);
					nconn++;
				}

				nmodule++;
			}
			mlobj = (void *) mlobj + APM_MOD_LIST_OBJ_PSIZE(mlobj, container->num_modules);

			ncontainer++;
		}
	}
}

void *audioreach_alloc_graph_pkt(struct q6apm *apm, struct list_head *sg_list, int graph_id)
{
	int payload_size, sg_sz, cont_sz, ml_sz, mp_sz, mc_sz;
	struct apm_module_param_data  *param_data;
	struct apm_container_params *cont_params;
	struct audioreach_container *container;
	struct apm_sub_graph_params *sg_params;
	struct apm_mod_conn_list_params *mcon;
	struct apm_graph_open_params params;
	struct apm_prop_list_params *mprop;
	struct audioreach_module *module;
	struct audioreach_sub_graph *sgs;
	struct apm_mod_list_obj *mlobj;
	int num_modules_per_list;
	int num_connections = 0;
	int num_containers = 0;
	int num_sub_graphs = 0;
	int num_modules = 0;
	int num_modules_list;
	struct gpr_pkt *pkt;
	void *p;

	list_for_each_entry(sgs, sg_list, node) {
		num_sub_graphs++;
		list_for_each_entry(container, &sgs->container_list, node) {
			num_containers++;
			num_modules += container->num_modules;
			list_for_each_container_module(module, container) {
				if (module->src_mod_inst_id)
					num_connections++;
			}
		}
	}

	num_modules_list = num_containers;
	num_modules_per_list = num_modules/num_containers;
	sg_sz = APM_SUB_GRAPH_PSIZE(sg_params, num_sub_graphs);
	cont_sz = APM_CONTAINER_PSIZE(cont_params, num_containers);
	ml_sz =	ALIGN(sizeof(struct apm_module_list_params) +
		num_modules_list * APM_MOD_LIST_OBJ_PSIZE(mlobj,  num_modules_per_list), 8);
	mp_sz = APM_MOD_PROP_PSIZE(mprop, num_modules);
	mc_sz =	APM_MOD_CONN_PSIZE(mcon, num_connections);

	payload_size = sg_sz + cont_sz + ml_sz + mp_sz + mc_sz;
	pkt = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_GRAPH_OPEN, 0);
	if (IS_ERR(pkt))
		return pkt;

	p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;

	/* SubGraph */
	params.sg_data = p;
	param_data = &params.sg_data->param_data;
	param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
	param_data->param_id = APM_PARAM_ID_SUB_GRAPH_CONFIG;
	param_data->param_size = sg_sz - APM_MODULE_PARAM_DATA_SIZE;
	params.sg_data->num_sub_graphs = num_sub_graphs;
	p += sg_sz;

	/* Container */
	params.cont_data = p;
	param_data = &params.cont_data->param_data;
	param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
	param_data->param_id = APM_PARAM_ID_CONTAINER_CONFIG;
	param_data->param_size = cont_sz - APM_MODULE_PARAM_DATA_SIZE;
	params.cont_data->num_containers = num_containers;
	p += cont_sz;

	/* Module List*/
	params.mod_list_data = p;
	param_data = &params.mod_list_data->param_data;
	param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
	param_data->param_id = APM_PARAM_ID_MODULE_LIST;
	param_data->param_size = ml_sz - APM_MODULE_PARAM_DATA_SIZE;
	params.mod_list_data->num_modules_list = num_sub_graphs;
	p += ml_sz;

	/* Module Properties */
	params.mod_prop_data = p;
	param_data = &params.mod_prop_data->param_data;
	param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
	param_data->param_id = APM_PARAM_ID_MODULE_PROP;
	param_data->param_size = mp_sz - APM_MODULE_PARAM_DATA_SIZE;
	params.mod_prop_data->num_modules_prop_cfg = num_modules;
	p += mp_sz;

	/* Module Connections */
	params.mod_conn_list_data = p;
	param_data = &params.mod_conn_list_data->param_data;
	param_data->module_instance_id = APM_MODULE_INSTANCE_ID;
	param_data->param_id = APM_PARAM_ID_MODULE_CONN;
	param_data->param_size = mc_sz - APM_MODULE_PARAM_DATA_SIZE;
	params.mod_conn_list_data->num_connections = num_connections;
	p += mc_sz;

	audioreach_populate_graph(&params, sg_list, num_sub_graphs);

	return pkt;
}
EXPORT_SYMBOL_GPL(audioreach_alloc_graph_pkt);
+29 −0
Original line number Diff line number Diff line
@@ -5,6 +5,8 @@
#include <linux/types.h>
#include <linux/soc/qcom/apr.h>
#include <sound/soc.h>
struct q6apm;
struct q6apm_graph;

/* Module IDs */
#define MODULE_ID_WR_SHARED_MEM_EP	0x07001000
@@ -654,6 +656,20 @@ struct audioreach_module {
	struct snd_soc_dapm_widget *widget;
};

struct audioreach_module_config {
	int	direction;
	u32	sample_rate;
	u16	bit_width;
	u16	bits_per_sample;

	u16	data_format;
	u16	num_channels;
	u16	active_channels_mask;
	u32	sd_line_mask;
	int	fmt;
	u8 channel_map[AR_PCM_MAX_NUM_CHANNEL];
};

/* Packet Allocation routines */
void *audioreach_alloc_apm_cmd_pkt(int pkt_size, uint32_t opcode, uint32_t
				    token);
@@ -665,4 +681,17 @@ void *audioreach_alloc_apm_pkt(int pkt_size, uint32_t opcode, uint32_t token,
void *audioreach_alloc_pkt(int payload_size, uint32_t opcode,
			   uint32_t token, uint32_t src_port,
			   uint32_t dest_port);
void *audioreach_alloc_graph_pkt(struct q6apm *apm,
				 struct list_head *sg_list,
				  int graph_id);
struct audioreach_module *audioreach_get_container_last_module(
				struct audioreach_container *container);
struct audioreach_module *audioreach_get_container_first_module(
				struct audioreach_container *container);
struct audioreach_module *audioreach_get_container_next_module(
				struct audioreach_container *container,
				struct audioreach_module *module);
#define list_for_each_container_module(mod, cont) \
	for (mod = audioreach_get_container_first_module(cont); mod != NULL; \
	     mod = audioreach_get_container_next_module(cont, mod))
#endif /* __AUDIOREACH_H__ */
+596 −0

File added.

Preview size limit exceeded, changes collapsed.

+152 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __Q6APM_H__
#define __Q6APM_H__
#include <linux/types.h>
#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 <sound/soc.h>
#include <linux/of_platform.h>
#include <linux/jiffies.h>
#include <linux/soc/qcom/apr.h>
#include <dt-bindings/sound/qcom,q6dsp-lpass-ports.h>
#include "audioreach.h"

#define APM_PORT_MAX		127
#define APM_PORT_MAX_AUDIO_CHAN_CNT 8
#define PCM_CHANNEL_NULL 0
#define PCM_CHANNEL_FL    1	/* Front left channel. */
#define PCM_CHANNEL_FR    2	/* Front right channel. */
#define PCM_CHANNEL_FC    3	/* Front center channel. */
#define PCM_CHANNEL_LS   4	/* Left surround channel. */
#define PCM_CHANNEL_RS   5	/* Right surround channel. */
#define PCM_CHANNEL_LFE  6	/* Low frequency effect channel. */
#define PCM_CHANNEL_CS   7	/* Center surround channel; Rear center ch */
#define PCM_CHANNEL_LB   8	/* Left back channel; Rear left channel. */
#define PCM_CHANNEL_RB   9	/* Right back channel; Rear right channel. */
#define PCM_CHANNELS   10	/* Top surround channel. */

#define APM_TIMESTAMP_FLAG	0x80000000
#define FORMAT_LINEAR_PCM	0x0000
/* APM client callback events */
#define APM_CMD_EOS				0x0003
#define APM_CLIENT_EVENT_CMD_EOS_DONE		0x1003
#define APM_CMD_CLOSE				0x0004
#define APM_CLIENT_EVENT_CMD_CLOSE_DONE		0x1004
#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_WRITE_TOKEN_MASK                   GENMASK(15, 0)
#define APM_WRITE_TOKEN_LEN_MASK               GENMASK(31, 16)
#define APM_WRITE_TOKEN_LEN_SHIFT              16

#define APM_MAX_SESSIONS			8

struct q6apm {
	struct device *dev;
	gpr_port_t *port;
	gpr_device_t *gdev;
	/* For Graph OPEN/START/STOP/CLOSE operations */
	wait_queue_head_t wait;
	struct gpr_ibasic_rsp_result_t result;

	struct mutex cmd_lock;
	struct mutex lock;
	uint32_t state;

	struct idr graph_idr;
	struct idr graph_info_idr;
	struct idr sub_graphs_idr;
	struct idr containers_idr;
	struct idr modules_idr;
};

struct audio_buffer {
	phys_addr_t phys;
	uint32_t size;		/* size of buffer */
};

struct audioreach_graph_data {
	struct audio_buffer *buf;
	uint32_t num_periods;
	uint32_t dsp_buf;
	uint32_t mem_map_handle;
};

struct audioreach_graph {
	struct audioreach_graph_info *info;
	uint32_t id;
	int state;
	int start_count;
	/* Cached Graph data */
	void *graph;
	struct kref refcount;
	struct q6apm *apm;
};

typedef void (*q6apm_cb) (uint32_t opcode, uint32_t token,
			  void *payload, void *priv);
struct q6apm_graph {
	void *priv;
	q6apm_cb cb;
	uint32_t id;
	struct device *dev;
	struct q6apm *apm;
	gpr_port_t *port;
	struct audioreach_graph_data rx_data;
	struct audioreach_graph_data tx_data;
	struct gpr_ibasic_rsp_result_t result;
	wait_queue_head_t cmd_wait;
	struct mutex lock;
	struct audioreach_graph *ar_graph;
	struct audioreach_graph_info *info;
};

/* Graph Operations */
struct q6apm_graph *q6apm_graph_open(struct device *dev, q6apm_cb cb,
				     void *priv, int graph_id);
int q6apm_graph_close(struct q6apm_graph *graph);
int q6apm_graph_prepare(struct q6apm_graph *graph);
int q6apm_graph_start(struct q6apm_graph *graph);
int q6apm_graph_stop(struct q6apm_graph *graph);
int q6apm_graph_flush(struct q6apm_graph *graph);

/* Media Format */
int q6apm_graph_media_format_pcm(struct q6apm_graph *graph,
				 struct audioreach_module_config *cfg);

int q6apm_graph_media_format_shmem(struct q6apm_graph *graph,
				   struct audioreach_module_config *cfg);

/* read/write related */
int q6apm_send_eos_nowait(struct q6apm_graph *graph);
int q6apm_read(struct q6apm_graph *graph);
int q6apm_write_async(struct q6apm_graph *graph, uint32_t len, uint32_t msw_ts,
		      uint32_t lsw_ts, uint32_t wflags);

/* Memory Map related */
int q6apm_map_memory_regions(struct q6apm_graph *graph,
			     unsigned int dir, phys_addr_t phys,
			     size_t period_sz, unsigned int periods);
int q6apm_unmap_memory_regions(struct q6apm_graph *graph,
			       unsigned int dir);
/* Helpers */
int q6apm_send_cmd_sync(struct q6apm *apm, struct gpr_pkt *pkt,
			uint32_t rsp_opcode);

/* Callback for graph specific */
struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph,
						    uint32_t mid);

void q6apm_set_fe_dai_ops(struct snd_soc_dai_driver *dai_drv);
int q6apm_connect_sub_graphs(struct q6apm *apm, u32 src_sgid, u32 dst_sgid,
			     bool connect);
bool q6apm_is_sub_graphs_connected(struct q6apm *apm, u32 src_sgid,
				   u32 dst_sgid);
int q6apm_graph_get_rx_shmem_module_iid(struct q6apm_graph *graph);

#endif /* __APM_GRAPH_ */