Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 // Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
0003 // Copyright (c) 2018, Linaro Limited
0004 
0005 #include <linux/slab.h>
0006 #include <linux/wait.h>
0007 #include <linux/kernel.h>
0008 #include <linux/module.h>
0009 #include <linux/sched.h>
0010 #include <linux/of.h>
0011 #include <linux/of_platform.h>
0012 #include <linux/jiffies.h>
0013 #include <linux/soc/qcom/apr.h>
0014 #include "q6core.h"
0015 #include "q6dsp-errno.h"
0016 
0017 #define ADSP_STATE_READY_TIMEOUT_MS    3000
0018 #define Q6_READY_TIMEOUT_MS 100
0019 #define AVCS_CMD_ADSP_EVENT_GET_STATE       0x0001290C
0020 #define AVCS_CMDRSP_ADSP_EVENT_GET_STATE    0x0001290D
0021 #define AVCS_GET_VERSIONS       0x00012905
0022 #define AVCS_GET_VERSIONS_RSP   0x00012906
0023 #define AVCS_CMD_GET_FWK_VERSION    0x001292c
0024 #define AVCS_CMDRSP_GET_FWK_VERSION 0x001292d
0025 
0026 struct avcs_svc_info {
0027     uint32_t service_id;
0028     uint32_t version;
0029 } __packed;
0030 
0031 struct avcs_cmdrsp_get_version {
0032     uint32_t build_id;
0033     uint32_t num_services;
0034     struct avcs_svc_info svc_api_info[];
0035 } __packed;
0036 
0037 /* for ADSP2.8 and above */
0038 struct avcs_svc_api_info {
0039     uint32_t service_id;
0040     uint32_t api_version;
0041     uint32_t api_branch_version;
0042 } __packed;
0043 
0044 struct avcs_cmdrsp_get_fwk_version {
0045     uint32_t build_major_version;
0046     uint32_t build_minor_version;
0047     uint32_t build_branch_version;
0048     uint32_t build_subbranch_version;
0049     uint32_t num_services;
0050     struct avcs_svc_api_info svc_api_info[];
0051 } __packed;
0052 
0053 struct q6core {
0054     struct apr_device *adev;
0055     wait_queue_head_t wait;
0056     uint32_t avcs_state;
0057     struct mutex lock;
0058     bool resp_received;
0059     uint32_t num_services;
0060     struct avcs_cmdrsp_get_fwk_version *fwk_version;
0061     struct avcs_cmdrsp_get_version *svc_version;
0062     bool fwk_version_supported;
0063     bool get_state_supported;
0064     bool get_version_supported;
0065     bool is_version_requested;
0066 };
0067 
0068 static struct q6core *g_core;
0069 
0070 static int q6core_callback(struct apr_device *adev, struct apr_resp_pkt *data)
0071 {
0072     struct q6core *core = dev_get_drvdata(&adev->dev);
0073     struct aprv2_ibasic_rsp_result_t *result;
0074     struct apr_hdr *hdr = &data->hdr;
0075 
0076     result = data->payload;
0077     switch (hdr->opcode) {
0078     case APR_BASIC_RSP_RESULT:{
0079         result = data->payload;
0080         switch (result->opcode) {
0081         case AVCS_GET_VERSIONS:
0082             if (result->status == ADSP_EUNSUPPORTED)
0083                 core->get_version_supported = false;
0084             core->resp_received = true;
0085             break;
0086         case AVCS_CMD_GET_FWK_VERSION:
0087             if (result->status == ADSP_EUNSUPPORTED)
0088                 core->fwk_version_supported = false;
0089             core->resp_received = true;
0090             break;
0091         case AVCS_CMD_ADSP_EVENT_GET_STATE:
0092             if (result->status == ADSP_EUNSUPPORTED)
0093                 core->get_state_supported = false;
0094             core->resp_received = true;
0095             break;
0096         }
0097         break;
0098     }
0099     case AVCS_CMDRSP_GET_FWK_VERSION: {
0100         struct avcs_cmdrsp_get_fwk_version *fwk;
0101 
0102         fwk = data->payload;
0103 
0104         core->fwk_version = kmemdup(data->payload,
0105                         struct_size(fwk, svc_api_info,
0106                             fwk->num_services),
0107                         GFP_ATOMIC);
0108         if (!core->fwk_version)
0109             return -ENOMEM;
0110 
0111         core->fwk_version_supported = true;
0112         core->resp_received = true;
0113 
0114         break;
0115     }
0116     case AVCS_GET_VERSIONS_RSP: {
0117         struct avcs_cmdrsp_get_version *v;
0118 
0119         v = data->payload;
0120 
0121         core->svc_version = kmemdup(data->payload,
0122                         struct_size(v, svc_api_info,
0123                             v->num_services),
0124                         GFP_ATOMIC);
0125         if (!core->svc_version)
0126             return -ENOMEM;
0127 
0128         core->get_version_supported = true;
0129         core->resp_received = true;
0130 
0131         break;
0132     }
0133     case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
0134         core->get_state_supported = true;
0135         core->avcs_state = result->opcode;
0136 
0137         core->resp_received = true;
0138         break;
0139     default:
0140         dev_err(&adev->dev, "Message id from adsp core svc: 0x%x\n",
0141             hdr->opcode);
0142         break;
0143     }
0144 
0145     if (core->resp_received)
0146         wake_up(&core->wait);
0147 
0148     return 0;
0149 }
0150 
0151 static int q6core_get_fwk_versions(struct q6core *core)
0152 {
0153     struct apr_device *adev = core->adev;
0154     struct apr_pkt pkt;
0155     int rc;
0156 
0157     pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
0158                       APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
0159     pkt.hdr.pkt_size = APR_HDR_SIZE;
0160     pkt.hdr.opcode = AVCS_CMD_GET_FWK_VERSION;
0161 
0162     rc = apr_send_pkt(adev, &pkt);
0163     if (rc < 0)
0164         return rc;
0165 
0166     rc = wait_event_timeout(core->wait, (core->resp_received),
0167                 msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
0168     if (rc > 0 && core->resp_received) {
0169         core->resp_received = false;
0170 
0171         if (!core->fwk_version_supported)
0172             return -ENOTSUPP;
0173         else
0174             return 0;
0175     }
0176 
0177 
0178     return rc;
0179 }
0180 
0181 static int q6core_get_svc_versions(struct q6core *core)
0182 {
0183     struct apr_device *adev = core->adev;
0184     struct apr_pkt pkt;
0185     int rc;
0186 
0187     pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
0188                       APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
0189     pkt.hdr.pkt_size = APR_HDR_SIZE;
0190     pkt.hdr.opcode = AVCS_GET_VERSIONS;
0191 
0192     rc = apr_send_pkt(adev, &pkt);
0193     if (rc < 0)
0194         return rc;
0195 
0196     rc = wait_event_timeout(core->wait, (core->resp_received),
0197                 msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
0198     if (rc > 0 && core->resp_received) {
0199         core->resp_received = false;
0200         return 0;
0201     }
0202 
0203     return rc;
0204 }
0205 
0206 static bool __q6core_is_adsp_ready(struct q6core *core)
0207 {
0208     struct apr_device *adev = core->adev;
0209     struct apr_pkt pkt;
0210     int rc;
0211 
0212     core->get_state_supported = false;
0213 
0214     pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
0215                       APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
0216     pkt.hdr.pkt_size = APR_HDR_SIZE;
0217     pkt.hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE;
0218 
0219     rc = apr_send_pkt(adev, &pkt);
0220     if (rc < 0)
0221         return false;
0222 
0223     rc = wait_event_timeout(core->wait, (core->resp_received),
0224                 msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
0225     if (rc > 0 && core->resp_received) {
0226         core->resp_received = false;
0227 
0228         if (core->avcs_state)
0229             return true;
0230     }
0231 
0232     /* assume that the adsp is up if we not support this command */
0233     if (!core->get_state_supported)
0234         return true;
0235 
0236     return false;
0237 }
0238 
0239 /**
0240  * q6core_get_svc_api_info() - Get version number of a service.
0241  *
0242  * @svc_id: service id of the service.
0243  * @ainfo: Valid struct pointer to fill svc api information.
0244  *
0245  * Return: zero on success and error code on failure or unsupported
0246  */
0247 int q6core_get_svc_api_info(int svc_id, struct q6core_svc_api_info *ainfo)
0248 {
0249     int i;
0250     int ret = -ENOTSUPP;
0251 
0252     if (!g_core || !ainfo)
0253         return 0;
0254 
0255     mutex_lock(&g_core->lock);
0256     if (!g_core->is_version_requested) {
0257         if (q6core_get_fwk_versions(g_core) == -ENOTSUPP)
0258             q6core_get_svc_versions(g_core);
0259         g_core->is_version_requested = true;
0260     }
0261 
0262     if (g_core->fwk_version_supported) {
0263         for (i = 0; i < g_core->fwk_version->num_services; i++) {
0264             struct avcs_svc_api_info *info;
0265 
0266             info = &g_core->fwk_version->svc_api_info[i];
0267             if (svc_id != info->service_id)
0268                 continue;
0269 
0270             ainfo->api_version = info->api_version;
0271             ainfo->api_branch_version = info->api_branch_version;
0272             ret = 0;
0273             break;
0274         }
0275     } else if (g_core->get_version_supported) {
0276         for (i = 0; i < g_core->svc_version->num_services; i++) {
0277             struct avcs_svc_info *info;
0278 
0279             info = &g_core->svc_version->svc_api_info[i];
0280             if (svc_id != info->service_id)
0281                 continue;
0282 
0283             ainfo->api_version = info->version;
0284             ainfo->api_branch_version = 0;
0285             ret = 0;
0286             break;
0287         }
0288     }
0289 
0290     mutex_unlock(&g_core->lock);
0291 
0292     return ret;
0293 }
0294 EXPORT_SYMBOL_GPL(q6core_get_svc_api_info);
0295 
0296 /**
0297  * q6core_is_adsp_ready() - Get status of adsp
0298  *
0299  * Return: Will be an true if adsp is ready and false if not.
0300  */
0301 bool q6core_is_adsp_ready(void)
0302 {
0303     unsigned long  timeout;
0304     bool ret = false;
0305 
0306     if (!g_core)
0307         return false;
0308 
0309     mutex_lock(&g_core->lock);
0310     timeout = jiffies + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
0311     for (;;) {
0312         if (__q6core_is_adsp_ready(g_core)) {
0313             ret = true;
0314             break;
0315         }
0316 
0317         if (!time_after(timeout, jiffies)) {
0318             ret = false;
0319             break;
0320         }
0321     }
0322 
0323     mutex_unlock(&g_core->lock);
0324     return ret;
0325 }
0326 EXPORT_SYMBOL_GPL(q6core_is_adsp_ready);
0327 
0328 static int q6core_probe(struct apr_device *adev)
0329 {
0330     g_core = kzalloc(sizeof(*g_core), GFP_KERNEL);
0331     if (!g_core)
0332         return -ENOMEM;
0333 
0334     dev_set_drvdata(&adev->dev, g_core);
0335 
0336     mutex_init(&g_core->lock);
0337     g_core->adev = adev;
0338     init_waitqueue_head(&g_core->wait);
0339     return 0;
0340 }
0341 
0342 static int q6core_exit(struct apr_device *adev)
0343 {
0344     struct q6core *core = dev_get_drvdata(&adev->dev);
0345 
0346     if (core->fwk_version_supported)
0347         kfree(core->fwk_version);
0348     if (core->get_version_supported)
0349         kfree(core->svc_version);
0350 
0351     g_core = NULL;
0352     kfree(core);
0353 
0354     return 0;
0355 }
0356 
0357 #ifdef CONFIG_OF
0358 static const struct of_device_id q6core_device_id[]  = {
0359     { .compatible = "qcom,q6core" },
0360     {},
0361 };
0362 MODULE_DEVICE_TABLE(of, q6core_device_id);
0363 #endif
0364 
0365 static struct apr_driver qcom_q6core_driver = {
0366     .probe = q6core_probe,
0367     .remove = q6core_exit,
0368     .callback = q6core_callback,
0369     .driver = {
0370         .name = "qcom-q6core",
0371         .of_match_table = of_match_ptr(q6core_device_id),
0372     },
0373 };
0374 
0375 module_apr_driver(qcom_q6core_driver);
0376 MODULE_DESCRIPTION("q6 core");
0377 MODULE_LICENSE("GPL v2");