diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h index 21b30bf7c74c..6078ef2e24de 100644 --- a/include/sound/apr_audio-v2.h +++ b/include/sound/apr_audio-v2.h @@ -9349,6 +9349,73 @@ struct asm_aptx_dec_fmt_blk_v2 { */ } __packed; +/* Q6Core Specific */ +#define AVCS_CMD_GET_FWK_VERSION (0x0001292C) +#define AVCS_CMDRSP_GET_FWK_VERSION (0x0001292D) + +#define AVCS_SERVICE_ID_ALL (0xFFFFFFFF) + +struct avcs_get_fwk_version { + /* + * Indicates the major version of the AVS build. + * This value is incremented on chipset family boundaries. + */ + uint32_t build_major_version; + + /* + * Minor version of the AVS build. + * This value represents the mainline to which the AVS build belongs. + */ + uint32_t build_minor_version; + + /* Indicates the AVS branch version to which the image belongs. */ + uint32_t build_branch_version; + + /* Indicates the AVS sub-branch or customer product line information. */ + uint32_t build_subbranch_version; + + /* Number of supported AVS services in the current build. */ + uint32_t num_services; +}; + +struct avs_svc_api_info { + /* + * APRV2 service IDs for the individual static services. + * + * @values + * - APRV2_IDS_SERVICE_ID_ADSP_CORE_V + * - APRV2_IDS_SERVICE_ID_ADSP_AFE_V + * - APRV2_IDS_SERVICE_ID_ADSP_ASM_V + * - APRV2_IDS_SERVICE_ID_ADSP_ADM_V + * - APRV2_IDS_SERVICE_ID_ADSP_MVM_V + * - APRV2_IDS_SERVICE_ID_ADSP_CVS_V + * - APRV2_IDS_SERVICE_ID_ADSP_CVP_V + * - APRV2_IDS_SERVICE_ID_ADSP_LSM_V + */ + uint32_t service_id; + + /* + * Indicates the API version of the service. + * + * Each new API update that warrants a change on the HLOS side triggers + * an increment in the version. + */ + uint32_t api_version; + + /* + * Indicates the API increments on a sub-branch (not on the mainline). + * + * API branch version numbers can increment independently on different + * sub-branches. + */ + uint32_t api_branch_version; +}; + +struct avcs_fwk_ver_info { + struct avcs_get_fwk_version avcs_build; + struct avs_svc_api_info services[0]; +}; + /* LSM Specific */ #define VW_FEAT_DIM (39) diff --git a/include/sound/q6core.h b/include/sound/q6core.h index 4f55880d410f..0ce171973cb5 100644 --- a/include/sound/q6core.h +++ b/include/sound/q6core.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2016, 2017 The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -13,6 +13,7 @@ #ifndef __Q6CORE_H__ #define __Q6CORE_H__ #include +#include @@ -23,6 +24,9 @@ bool q6core_is_adsp_ready(void); int q6core_add_remove_pool_pages(phys_addr_t buf_add, uint32_t bufsz, uint32_t mempool_id, bool add_pages); +int q6core_get_avcs_fwk_ver_info(uint32_t service_id, + struct avcs_fwk_ver_info *ver_info); + #define ADSP_CMD_SET_DTS_EAGLE_DATA_ID 0x00012919 #define DTS_EAGLE_LICENSE_ID 0x00028346 struct adsp_dts_eagle { diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/sound/soc/msm/qdsp6v2/q6core.c index 6fed443186e5..c38cdda74623 100644 --- a/sound/soc/msm/qdsp6v2/q6core.c +++ b/sound/soc/msm/qdsp6v2/q6core.c @@ -22,6 +22,7 @@ #include #include #include +#include #define TIMEOUT_MS 1000 /* @@ -37,16 +38,30 @@ enum { CORE_MAX_CAL }; +enum ver_query_status { + VER_QUERY_UNATTEMPTED, + VER_QUERY_UNSUPPORTED, + VER_QUERY_SUPPORTED +}; + +struct q6core_avcs_ver_info { + enum ver_query_status status; + struct avcs_fwk_ver_info avcs_fwk_ver_info; +}; + struct q6core_str { struct apr_svc *core_handle_q; wait_queue_head_t bus_bw_req_wait; wait_queue_head_t cmd_req_wait; + wait_queue_head_t avcs_fwk_ver_req_wait; u32 bus_bw_resp_received; enum cmd_flags { FLAG_NONE, FLAG_CMDRSP_LICENSE_RESULT } cmd_resp_received_flag; + u32 avcs_fwk_ver_resp_received; struct mutex cmd_lock; + struct mutex ver_lock; union { struct avcs_cmdrsp_get_license_validation_result cmdrsp_license_result; @@ -55,6 +70,7 @@ struct q6core_str { struct cal_type_data *cal_data[CORE_MAX_CAL]; uint32_t mem_map_cal_handle; int32_t adsp_status; + struct q6core_avcs_ver_info q6core_avcs_ver_info; }; static struct q6core_str q6core_lcl; @@ -131,6 +147,17 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) q6core_lcl.bus_bw_resp_received = 1; wake_up(&q6core_lcl.bus_bw_req_wait); break; + case AVCS_CMD_GET_FWK_VERSION: + pr_debug("%s: Cmd = AVCS_CMD_GET_FWK_VERSION status[%s]\n", + __func__, adsp_err_get_err_str(payload1[1])); + /* ADSP status to match Linux error standard */ + q6core_lcl.adsp_status = -payload1[1]; + if (payload1[1] == ADSP_EUNSUPPORTED) + q6core_lcl.q6core_avcs_ver_info.status = + VER_QUERY_UNSUPPORTED; + q6core_lcl.avcs_fwk_ver_resp_received = 1; + wake_up(&q6core_lcl.avcs_fwk_ver_req_wait); + break; default: pr_err("%s: Invalid cmd rsp[0x%x][0x%x] opcode %d\n", __func__, @@ -174,6 +201,13 @@ static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv) q6core_lcl.cmd_resp_received_flag = FLAG_CMDRSP_LICENSE_RESULT; wake_up(&q6core_lcl.cmd_req_wait); break; + case AVCS_CMDRSP_GET_FWK_VERSION: + pr_debug("%s: Received AVCS_CMDRSP_GET_FWK_VERSION\n", + __func__); + q6core_lcl.q6core_avcs_ver_info.status = VER_QUERY_SUPPORTED; + q6core_lcl.avcs_fwk_ver_resp_received = 1; + wake_up(&q6core_lcl.avcs_fwk_ver_req_wait); + break; default: pr_err("%s: Message id from adsp core svc: 0x%x\n", __func__, data->opcode); @@ -230,6 +264,97 @@ struct cal_block_data *cal_utils_get_cal_block_by_key( return NULL; } +static int q6core_send_get_avcs_fwk_ver_cmd(void) +{ + struct apr_hdr avcs_ver_cmd; + int ret; + + avcs_ver_cmd.hdr_field = + APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + avcs_ver_cmd.pkt_size = sizeof(struct apr_hdr); + avcs_ver_cmd.src_port = 0; + avcs_ver_cmd.dest_port = 0; + avcs_ver_cmd.token = 0; + avcs_ver_cmd.opcode = AVCS_CMD_GET_FWK_VERSION; + + q6core_lcl.adsp_status = 0; + q6core_lcl.avcs_fwk_ver_resp_received = 0; + + ret = apr_send_pkt(q6core_lcl.core_handle_q, + (uint32_t *) &avcs_ver_cmd); + if (ret < 0) { + pr_err("%s: failed to send apr packet, ret=%d\n", __func__, + ret); + goto done; + } + + ret = wait_event_timeout(q6core_lcl.avcs_fwk_ver_req_wait, + (q6core_lcl.avcs_fwk_ver_resp_received == 1), + msecs_to_jiffies(TIMEOUT_MS)); + if (!ret) { + pr_err("%s: wait_event timeout for AVCS fwk version info\n", + __func__); + ret = -ETIMEDOUT; + goto done; + } + + if (q6core_lcl.adsp_status < 0) { + /* + * adsp_err_get_err_str expects a positive value but we store + * the DSP error as negative to match the Linux error standard. + * Pass in the negated value so adsp_err_get_err_str returns + * the correct string. + */ + pr_err("%s: DSP returned error[%s]\n", __func__, + adsp_err_get_err_str(-q6core_lcl.adsp_status)); + ret = adsp_err_get_lnx_err_code(q6core_lcl.adsp_status); + goto done; + } + + ret = 0; + +done: + return ret; +} + +int q6core_get_avcs_fwk_ver_info(uint32_t service_id, + struct avcs_fwk_ver_info *ver_info) +{ + int ret; + + mutex_lock(&(q6core_lcl.ver_lock)); + pr_debug("%s: q6core_avcs_ver_info.status(%d)\n", __func__, + q6core_lcl.q6core_avcs_ver_info.status); + + switch (q6core_lcl.q6core_avcs_ver_info.status) { + case VER_QUERY_SUPPORTED: + ret = 0; + break; + case VER_QUERY_UNSUPPORTED: + ret = -EOPNOTSUPP; + break; + case VER_QUERY_UNATTEMPTED: + if (q6core_is_adsp_ready()) { + ret = q6core_send_get_avcs_fwk_ver_cmd(); + } else { + pr_err("%s: ADSP is not ready to query version\n", + __func__); + ret = -ENODEV; + } + break; + default: + pr_err("%s: Invalid version query status %d\n", __func__, + q6core_lcl.q6core_avcs_ver_info.status); + ret = -EINVAL; + break; + } + mutex_unlock(&(q6core_lcl.ver_lock)); + + return ret; +} +EXPORT_SYMBOL(q6core_get_avcs_fwk_ver_info); + int32_t core_set_license(uint32_t key, uint32_t module_id) { struct avcs_cmd_set_license *cmd_setl = NULL; @@ -887,18 +1012,16 @@ err: static int __init core_init(void) { + memset(&q6core_lcl, 0, sizeof(struct q6core_str)); init_waitqueue_head(&q6core_lcl.bus_bw_req_wait); - q6core_lcl.bus_bw_resp_received = 0; - - q6core_lcl.core_handle_q = NULL; - init_waitqueue_head(&q6core_lcl.cmd_req_wait); + init_waitqueue_head(&q6core_lcl.avcs_fwk_ver_req_wait); q6core_lcl.cmd_resp_received_flag = FLAG_NONE; mutex_init(&q6core_lcl.cmd_lock); - q6core_lcl.mem_map_cal_handle = 0; - q6core_lcl.adsp_status = 0; + mutex_init(&q6core_lcl.ver_lock); q6core_init_cal_data(); + return 0; } module_init(core_init); @@ -906,6 +1029,7 @@ module_init(core_init); static void __exit core_exit(void) { mutex_destroy(&q6core_lcl.cmd_lock); + mutex_destroy(&q6core_lcl.ver_lock); q6core_delete_cal_data(); } module_exit(core_exit);