Merge "dsp: Add support to dynamically load and unload BT modules"
diff --git a/dsp/q6afe.c b/dsp/q6afe.c
index d1fcb51..3d76e48 100644
--- a/dsp/q6afe.c
+++ b/dsp/q6afe.c
@@ -25,6 +25,21 @@
#define WAKELOCK_TIMEOUT 5000
#define AFE_CLK_TOKEN 1024
+
+struct afe_avcs_payload_port_mapping {
+ u16 port_id;
+ struct avcs_load_unload_modules_payload *payload;
+} __packed;
+
+enum {
+ ENCODER_CASE,
+ DECODER_CASE,
+ /* Add new use case here */
+ MAX_ALLOWED_USE_CASES
+};
+
+static struct afe_avcs_payload_port_mapping *pm[MAX_ALLOWED_USE_CASES];
+
enum {
AFE_COMMON_RX_CAL = 0,
AFE_COMMON_TX_CAL,
@@ -237,6 +252,124 @@
#define SIZEOF_CFG_CMD(y) \
(sizeof(struct apr_hdr) + sizeof(u16) + (sizeof(struct y)))
+static void q6afe_unload_avcs_modules(u16 port_id, int index)
+{
+ int ret = 0;
+
+ ret = q6core_avcs_load_unload_modules(pm[index]->payload,
+ AVCS_UNLOAD_MODULES);
+
+ if (ret < 0)
+ pr_err("%s: avcs module unload failed %d\n", __func__, ret);
+
+ kfree(pm[index]->payload);
+ pm[index]->payload = NULL;
+ kfree(pm[index]);
+ pm[index] = NULL;
+}
+
+static int q6afe_load_avcs_modules(int num_modules, u16 port_id,
+ uint32_t use_case, u32 format_id)
+{
+ int i = 0;
+ int32_t ret = 0;
+ size_t payload_size = 0, port_struct_size = 0;
+ struct afe_avcs_payload_port_mapping payload_map;
+ struct avcs_load_unload_modules_sec_payload sec_payload;
+
+ if (num_modules <= 0) {
+ pr_err("%s: Invalid number of modules to load\n", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < MAX_ALLOWED_USE_CASES; i++) {
+ if (pm[i] == NULL) {
+ port_struct_size = sizeof(payload_map);
+ pm[i] = kzalloc(port_struct_size, GFP_KERNEL);
+ if (!pm[i])
+ return -ENOMEM;
+
+ pm[i]->port_id = port_id;
+ payload_size = sizeof(uint32_t) + (sizeof(sec_payload)
+ * num_modules);
+ pm[i]->payload = kzalloc(payload_size, GFP_KERNEL);
+ if (!pm[i]->payload) {
+ kfree(pm[i]);
+ pm[i] = NULL;
+ return -ENOMEM;
+ }
+
+ /*
+ * index 0 : packetizer/de-packetizer
+ * index 1 : encoder/decoder
+ */
+
+ pm[i]->payload->num_modules = num_modules;
+
+ /*
+ * Remaining fields of payload
+ * are initialized to zero
+ */
+
+ if (use_case == ENCODER_CASE) {
+ pm[i]->payload->load_unload_info[0].module_type =
+ AMDB_MODULE_TYPE_PACKETIZER;
+ pm[i]->payload->load_unload_info[0].id1 =
+ AVS_MODULE_ID_PACKETIZER_COP;
+ pm[i]->payload->load_unload_info[1].module_type =
+ AMDB_MODULE_TYPE_ENCODER;
+ pm[i]->payload->load_unload_info[1].id1 =
+ format_id;
+ } else if (use_case == DECODER_CASE) {
+ pm[i]->payload->load_unload_info[0].module_type =
+ AMDB_MODULE_TYPE_DEPACKETIZER;
+ pm[i]->payload->load_unload_info[0].id1 =
+ AVS_MODULE_ID_DEPACKETIZER_COP_V1;
+
+ if (format_id == ENC_CODEC_TYPE_LDAC) {
+ pm[i]->payload->load_unload_info[0].id1 =
+ AVS_MODULE_ID_DEPACKETIZER_COP;
+ goto load_unload;
+ }
+
+ pm[i]->payload->load_unload_info[1].module_type =
+ AMDB_MODULE_TYPE_DECODER;
+ pm[i]->payload->load_unload_info[1].id1 =
+ format_id;
+
+ } else {
+ pr_err("%s:load usecase %d not supported\n",
+ __func__, use_case);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+load_unload:
+ ret = q6core_avcs_load_unload_modules(pm[i]->payload,
+ AVCS_LOAD_MODULES);
+
+ if (ret < 0) {
+ pr_err("%s: load failed %d\n", __func__, ret);
+ goto fail;
+ }
+ return 0;
+ }
+
+ }
+
+ ret = -EINVAL;
+ if (i == MAX_ALLOWED_USE_CASES) {
+ pr_err("%s: Not enough ports available\n", __func__);
+ return ret;
+ }
+fail:
+ kfree(pm[i]->payload);
+ pm[i]->payload = NULL;
+ kfree(pm[i]);
+ pm[i] = NULL;
+ return ret;
+}
+
static int afe_get_cal_hw_delay(int32_t path,
struct audio_cal_hw_delay_entry *entry);
static int remap_cal_data(struct cal_block_data *cal_block, int cal_index);
@@ -5325,8 +5458,21 @@
if ((codec_format != ASM_MEDIA_FMT_NONE) &&
(cfg_type == AFE_PARAM_ID_SLIMBUS_CONFIG)) {
if (enc_cfg != NULL) {
- pr_debug("%s: Found AFE encoder support for SLIMBUS format = %d\n",
+ pr_debug("%s: Found AFE encoder support for SLIMBUS format = %d\n",
__func__, codec_format);
+
+ if ((q6core_get_avcs_api_version_per_service(
+ APRV2_IDS_SERVICE_ID_ADSP_CORE_V) >=
+ AVCS_API_VERSION_V5)) {
+ ret = q6afe_load_avcs_modules(2, port_id,
+ ENCODER_CASE, codec_format);
+ if (ret < 0) {
+ pr_err("%s:encoder load for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ goto fail_cmd;
+ }
+ }
+
ret = q6afe_send_enc_config(port_id, enc_cfg,
codec_format, *afe_config,
afe_in_channels,
@@ -5340,7 +5486,24 @@
}
if (dec_cfg != NULL) {
pr_debug("%s: Found AFE decoder support for SLIMBUS format = %d\n",
- __func__, codec_format);
+ __func__, codec_format);
+
+ if ((q6core_get_avcs_api_version_per_service(
+ APRV2_IDS_SERVICE_ID_ADSP_CORE_V) >=
+ AVCS_API_VERSION_V5)) {
+ /* LDAC doesn't require decoder */
+ if (codec_format == ENC_CODEC_TYPE_LDAC)
+ ret = q6afe_load_avcs_modules(1, port_id,
+ DECODER_CASE, codec_format);
+ else
+ ret = q6afe_load_avcs_modules(2, port_id,
+ DECODER_CASE, codec_format);
+ if (ret < 0) {
+ pr_err("%s:decoder load for port 0x%x failed %d\n",
+ __func__, port_id, ret);
+ goto fail_cmd;
+ }
+ }
ret = q6afe_send_dec_config(port_id, *afe_config,
dec_cfg, codec_format,
afe_in_channels,
@@ -8139,6 +8302,7 @@
struct afe_port_cmd_device_stop stop;
enum afe_mad_type mad_type;
int ret = 0;
+ u16 i;
int index = 0;
uint16_t port_index;
@@ -8255,6 +8419,15 @@
pr_err("%s: AFE close failed %d\n", __func__, ret);
fail_cmd:
+ if ((q6core_get_avcs_api_version_per_service(
+ APRV2_IDS_SERVICE_ID_ADSP_CORE_V) >= AVCS_API_VERSION_V5)) {
+ for (i = 0; i < MAX_ALLOWED_USE_CASES; i++) {
+ if (pm[i] && pm[i]->port_id == port_id) {
+ q6afe_unload_avcs_modules(port_id, i);
+ break;
+ }
+ }
+ }
mutex_unlock(&this_afe.afe_cmd_lock);
return ret;
}
diff --git a/dsp/q6core.c b/dsp/q6core.c
index 15c00bd..8e8bedf 100644
--- a/dsp/q6core.c
+++ b/dsp/q6core.c
@@ -61,9 +61,11 @@
wait_queue_head_t cmd_req_wait;
wait_queue_head_t avcs_fwk_ver_req_wait;
wait_queue_head_t lpass_npa_rsc_wait;
+ wait_queue_head_t avcs_module_load_unload_wait;
u32 lpass_npa_rsc_rsp_rcvd;
u32 bus_bw_resp_received;
u32 mdf_map_resp_received;
+ u32 avcs_module_resp_received;
enum cmd_flags {
FLAG_NONE,
FLAG_CMDRSP_LICENSE_RESULT
@@ -87,6 +89,9 @@
static struct q6core_str q6core_lcl;
+/* Global payload used for AVCS_CMD_RSP_MODULES command */
+static struct avcs_load_unload_modules_payload *rsp_payload;
+
struct generic_get_data_ {
int valid;
int size_in_ints;
@@ -343,6 +348,29 @@
q6core_lcl.lpass_npa_rsc_rsp_rcvd = 1;
wake_up(&q6core_lcl.lpass_npa_rsc_wait);
break;
+ case AVCS_CMD_LOAD_MODULES:
+ pr_err("%s: Cmd = %s failed status[%s]\n",
+ __func__, "AVCS_CMD_LOAD__MODULES",
+ adsp_err_get_err_str(payload1[1]));
+ q6core_lcl.avcs_module_resp_received = 1;
+ q6core_lcl.adsp_status = -payload1[1];
+ wake_up(&q6core_lcl.avcs_module_load_unload_wait);
+ break;
+ case AVCS_CMD_UNLOAD_MODULES:
+ if (payload1[1] == ADSP_EOK) {
+ pr_debug("%s: Cmd = %s success status[%s]\n",
+ __func__, "AVCS_CMD_UNLOAD_MODULES",
+ "ADSP_EOK");
+ } else {
+ pr_err("%s: Cmd = %s failed status[%s]\n",
+ __func__, "AVCS_CMD_UNLOAD_MODULES",
+ adsp_err_get_err_str(payload1[1]));
+ q6core_lcl.adsp_status = -payload1[1];
+ }
+ q6core_lcl.avcs_module_resp_received = 1;
+ wake_up(&q6core_lcl.avcs_module_load_unload_wait);
+ break;
+
default:
pr_err("%s: Invalid cmd rsp[0x%x][0x%x] opcode %d\n",
__func__,
@@ -441,6 +469,13 @@
q6core_lcl.avcs_fwk_ver_resp_received = 1;
wake_up(&q6core_lcl.avcs_fwk_ver_req_wait);
break;
+ case AVCS_CMD_RSP_LOAD_MODULES:
+ pr_debug("%s: Received AVCS_CMD_RSP_LOAD_MODULES\n",
+ __func__);
+ memcpy(rsp_payload, data->payload, data->payload_size);
+ q6core_lcl.avcs_module_resp_received = 1;
+ wake_up(&q6core_lcl.avcs_module_load_unload_wait);
+ break;
default:
pr_err("%s: Message id from adsp core svc: 0x%x\n",
__func__, data->opcode);
@@ -821,7 +856,6 @@
get_lvr_cmd.hdr.opcode = AVCS_CMD_GET_LICENSE_VALIDATION_RESULT;
get_lvr_cmd.id = module_id;
-
ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) &get_lvr_cmd);
if (ret < 0) {
pr_err("%s: license_validation request failed, err %d\n",
@@ -894,6 +928,105 @@
}
EXPORT_SYMBOL(core_set_dolby_manufacturer_id);
+int32_t q6core_avcs_load_unload_modules(struct avcs_load_unload_modules_payload
+ *payload, uint32_t preload_type)
+{
+ int ret = 0;
+ size_t packet_size = 0, payload_size = 0;
+ struct avcs_cmd_dynamic_modules *mod = NULL;
+ int num_modules;
+
+ if (payload == NULL) {
+ pr_err("%s: payload is null\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&(q6core_lcl.cmd_lock));
+ num_modules = payload->num_modules;
+ ocm_core_open();
+
+ if (q6core_lcl.core_handle_q == NULL) {
+ pr_err("%s: apr registration for CORE failed\n", __func__);
+ mutex_unlock(&(q6core_lcl.cmd_lock));
+ return -ENODEV;
+ }
+
+ payload_size = (sizeof(struct avcs_load_unload_modules_sec_payload)
+ * num_modules) + sizeof(uint32_t);
+ packet_size = sizeof(struct avcs_cmd_dynamic_modules) +
+ payload_size - sizeof(uint32_t);
+ mod = kzalloc(packet_size, GFP_KERNEL);
+
+ if (!mod) {
+ mutex_unlock(&(q6core_lcl.cmd_lock));
+ return -ENOMEM;
+ }
+
+ rsp_payload = kzalloc(payload_size, GFP_KERNEL);
+ if (!rsp_payload) {
+ kfree(mod);
+ mutex_unlock(&(q6core_lcl.cmd_lock));
+ return -ENOMEM;
+ }
+
+ memcpy((uint8_t *)mod + sizeof(struct apr_hdr) +
+ sizeof(struct avcs_load_unload_modules_meminfo),
+ payload, payload_size);
+
+ mod->hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mod->hdr.pkt_size = packet_size;
+ mod->hdr.src_port = 0;
+ mod->hdr.dest_port = 0;
+ mod->hdr.token = 0;
+ mod->meminfo.data_payload_addr_lsw = 0;
+ mod->meminfo.data_payload_addr_msw = 0;
+ mod->meminfo.mem_map_handle = 0;
+ mod->meminfo.buffer_size = payload_size;
+
+ if (preload_type == AVCS_LOAD_MODULES)
+ mod->hdr.opcode = AVCS_CMD_LOAD_MODULES;
+ else
+ mod->hdr.opcode = AVCS_CMD_UNLOAD_MODULES;
+
+ q6core_lcl.avcs_module_resp_received = 0;
+ ret = apr_send_pkt(q6core_lcl.core_handle_q,
+ (uint32_t *)mod);
+
+ if (ret < 0) {
+ pr_err("%s: modules load/unload failed ret = %d\n",
+ __func__, ret);
+ goto done;
+ }
+
+ ret = wait_event_timeout(q6core_lcl.avcs_module_load_unload_wait,
+ (q6core_lcl.avcs_module_resp_received == 1),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s wait event timeout for avcs load/unload module\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ goto done;
+ }
+
+ if (q6core_lcl.adsp_status < 0) {
+ pr_err("%s: modules load/unload failed %d\n", __func__,
+ q6core_lcl.adsp_status);
+ ret = q6core_lcl.adsp_status;
+ goto done;
+ } else {
+ if (mod->hdr.opcode == AVCS_CMD_LOAD_MODULES)
+ memcpy(payload, rsp_payload, payload_size);
+ }
+done:
+ kfree(mod);
+ kfree(rsp_payload);
+ mutex_unlock(&(q6core_lcl.cmd_lock));
+ return ret;
+}
+EXPORT_SYMBOL(q6core_avcs_load_unload_modules);
+
int32_t q6core_load_unload_topo_modules(uint32_t topo_id,
bool preload_type)
{
@@ -1871,7 +2004,6 @@
if (rc < 0)
goto err;
q6core_lcl.avs_state = avs_state;
-
rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
if (rc) {
dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
@@ -1926,6 +2058,7 @@
init_waitqueue_head(&q6core_lcl.avcs_fwk_ver_req_wait);
init_waitqueue_head(&q6core_lcl.mdf_map_resp_wait);
init_waitqueue_head(&q6core_lcl.lpass_npa_rsc_wait);
+ init_waitqueue_head(&q6core_lcl.avcs_module_load_unload_wait);
q6core_lcl.cmd_resp_received_flag = FLAG_NONE;
mutex_init(&q6core_lcl.cmd_lock);
mutex_init(&q6core_lcl.ver_lock);
diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h
index 86a803e..607b910 100644
--- a/include/dsp/apr_audio-v2.h
+++ b/include/dsp/apr_audio-v2.h
@@ -4137,6 +4137,16 @@
#define AFE_MODULE_ID_DEPACKETIZER_COP 0x00013233
#define AFE_MODULE_ID_DEPACKETIZER_COP_V1 0x000132E9
+/* Macros for dynamic loading of modules by AVCS */
+
+#define AVS_MODULE_ID_PACKETIZER_COP 0x0001322A
+
+#define AVS_MODULE_ID_PACKETIZER_COP_V1 0x000132E8
+
+#define AVS_MODULE_ID_DEPACKETIZER_COP 0x00013233
+
+#define AVS_MODULE_ID_DEPACKETIZER_COP_V1 0x000132E9
+
/*
* Depacketizer type parameter for the #AVS_MODULE_ID_DECODER module.
* This parameter cannot be set runtime.
diff --git a/include/dsp/q6core.h b/include/dsp/q6core.h
index 210471c..adfb83e 100644
--- a/include/dsp/q6core.h
+++ b/include/dsp/q6core.h
@@ -13,6 +13,7 @@
#define AVCS_CMD_ADSP_EVENT_GET_STATE 0x0001290C
#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE 0x0001290D
#define AVCS_API_VERSION_V4 4
+#define AVCS_API_VERSION_V5 5
#define APRV2_IDS_SERVICE_ID_ADSP_CORE_V (0x3)
bool q6core_is_adsp_ready(void);
@@ -191,6 +192,126 @@
uint32_t topology_id;
} __packed;
+#define AVCS_LOAD_MODULES 1
+
+#define AVCS_UNLOAD_MODULES 0
+
+#define AVCS_CMD_LOAD_MODULES 0x00012989
+
+#define AVCS_CMD_UNLOAD_MODULES 0x0001298A
+
+#define AVCS_CMD_RSP_LOAD_MODULES 0x0001298B
+
+/*
+ * Module is generic, such as a preprocessor,
+ * postprocessor, or volume control
+ * module.
+ */
+#define AMDB_MODULE_TYPE_GENERIC 0
+
+/** Module is a decoder. */
+#define AMDB_MODULE_TYPE_DECODER 1
+
+/** Module is an encoder. */
+#define AMDB_MODULE_TYPE_ENCODER 2
+
+/** Module is a converter. */
+#define AMDB_MODULE_TYPE_CONVERTER 3
+
+/** Module is a packetizer. */
+#define AMDB_MODULE_TYPE_PACKETIZER 4
+
+/** Module is a depacketizer. */
+#define AMDB_MODULE_TYPE_DEPACKETIZER 5
+
+
+struct avcs_load_unload_modules_sec_payload {
+ uint32_t module_type;
+ /*
+ * < Type of module.
+
+ * @values
+ * - #AMDB_MODULE_TYPE_GENERIC
+ * - #AMDB_MODULE_TYPE_DECODER
+ * - #AMDB_MODULE_TYPE_ENCODER
+ * - #AMDB_MODULE_TYPE_CONVERTER
+ * - #AMDB_MODULE_TYPE_PACKETIZER
+ * - #AMDB_MODULE_TYPE_DEPACKETIZER
+ */
+
+
+ uint32_t id1;
+ /*
+ * < One of the following types of IDs:
+ * - Format ID for the encoder, decoder, and packetizer module types
+ * - Module ID for the generic module type
+ * - Source format ID for the converter module type
+ */
+
+ uint32_t id2;
+ /*
+ * < Sink format ID for the converter module type.
+ * Zero for other module types
+ */
+
+ uint32_t handle_lsw;
+ /* < LSW of the Handle to the module loaded */
+
+ uint32_t handle_msw;
+ /* < MSW of the Handle to the module loaded. */
+} __packed;
+
+struct avcs_load_unload_modules_payload {
+ uint32_t num_modules;
+ /**< Number of modules being registered */
+
+ struct avcs_load_unload_modules_sec_payload load_unload_info[0];
+ /*
+ * < Load/unload information for num_modules.
+ * It will be of type avcs_load_unload_info_t[num_modules]
+ */
+} __packed;
+
+struct avcs_load_unload_modules_meminfo {
+ uint32_t data_payload_addr_lsw;
+ /**< Lower 32 bits of the 64-bit data payload address. */
+
+ uint32_t data_payload_addr_msw;
+ /**< Upper 32 bits of the 64-bit data payload address. */
+
+ uint32_t mem_map_handle;
+ /*
+ * < Unique identifier for an address. The aDSP returns this memory map
+ * handle through the #AVCS_CMD_SHARED_MEM_MAP_REGIONS command.
+
+ * @values @vertspace{-2}
+ * - NULL -- Parameter data payloads are within the message payload
+ * (in-band).
+ * - Non-NULL -- Parameter data payloads begin at the address specified
+ * in the data_payload_addr_lsw and data_payload_addr_msw fields
+ * (out-of-band).
+
+ * The client can choose in-band or out-of-band
+ */
+
+ uint32_t buffer_size;
+ /*
+ * < Actual size (in bytes) of the valid data
+ * in the data payload address.
+ */
+} __packed;
+
+struct avcs_cmd_dynamic_modules {
+ struct apr_hdr hdr;
+ struct avcs_load_unload_modules_meminfo meminfo;
+ struct avcs_load_unload_modules_payload payload;
+} __packed;
+
+
+int32_t q6core_avcs_load_unload_modules(struct avcs_load_unload_modules_payload *payload,
+ uint32_t preload_type);
+
+
/* This command allows a remote client(HLOS) creates a client to LPASS NPA
* resource node. Currently, this command supports only the NPA Sleep resource
* "/island/core/drivers" node ID.