root/sound/soc/qcom/qdsp6/q6core.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. q6core_callback
  2. q6core_get_fwk_versions
  3. q6core_get_svc_versions
  4. __q6core_is_adsp_ready
  5. q6core_get_svc_api_info
  6. q6core_is_adsp_ready
  7. q6core_probe
  8. q6core_exit

   1 // SPDX-License-Identifier: GPL-2.0
   2 // Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
   3 // Copyright (c) 2018, Linaro Limited
   4 
   5 #include <linux/slab.h>
   6 #include <linux/wait.h>
   7 #include <linux/kernel.h>
   8 #include <linux/module.h>
   9 #include <linux/sched.h>
  10 #include <linux/of.h>
  11 #include <linux/of_platform.h>
  12 #include <linux/jiffies.h>
  13 #include <linux/soc/qcom/apr.h>
  14 #include "q6core.h"
  15 #include "q6dsp-errno.h"
  16 
  17 #define ADSP_STATE_READY_TIMEOUT_MS    3000
  18 #define Q6_READY_TIMEOUT_MS 100
  19 #define AVCS_CMD_ADSP_EVENT_GET_STATE           0x0001290C
  20 #define AVCS_CMDRSP_ADSP_EVENT_GET_STATE        0x0001290D
  21 #define AVCS_GET_VERSIONS       0x00012905
  22 #define AVCS_GET_VERSIONS_RSP   0x00012906
  23 #define AVCS_CMD_GET_FWK_VERSION        0x001292c
  24 #define AVCS_CMDRSP_GET_FWK_VERSION     0x001292d
  25 
  26 struct avcs_svc_info {
  27         uint32_t service_id;
  28         uint32_t version;
  29 } __packed;
  30 
  31 struct avcs_cmdrsp_get_version {
  32         uint32_t build_id;
  33         uint32_t num_services;
  34         struct avcs_svc_info svc_api_info[];
  35 } __packed;
  36 
  37 /* for ADSP2.8 and above */
  38 struct avcs_svc_api_info {
  39         uint32_t service_id;
  40         uint32_t api_version;
  41         uint32_t api_branch_version;
  42 } __packed;
  43 
  44 struct avcs_cmdrsp_get_fwk_version {
  45         uint32_t build_major_version;
  46         uint32_t build_minor_version;
  47         uint32_t build_branch_version;
  48         uint32_t build_subbranch_version;
  49         uint32_t num_services;
  50         struct avcs_svc_api_info svc_api_info[];
  51 } __packed;
  52 
  53 struct q6core {
  54         struct apr_device *adev;
  55         wait_queue_head_t wait;
  56         uint32_t avcs_state;
  57         struct mutex lock;
  58         bool resp_received;
  59         uint32_t num_services;
  60         struct avcs_cmdrsp_get_fwk_version *fwk_version;
  61         struct avcs_cmdrsp_get_version *svc_version;
  62         bool fwk_version_supported;
  63         bool get_state_supported;
  64         bool get_version_supported;
  65         bool is_version_requested;
  66 };
  67 
  68 static struct q6core *g_core;
  69 
  70 static int q6core_callback(struct apr_device *adev, struct apr_resp_pkt *data)
  71 {
  72         struct q6core *core = dev_get_drvdata(&adev->dev);
  73         struct aprv2_ibasic_rsp_result_t *result;
  74         struct apr_hdr *hdr = &data->hdr;
  75 
  76         result = data->payload;
  77         switch (hdr->opcode) {
  78         case APR_BASIC_RSP_RESULT:{
  79                 result = data->payload;
  80                 switch (result->opcode) {
  81                 case AVCS_GET_VERSIONS:
  82                         if (result->status == ADSP_EUNSUPPORTED)
  83                                 core->get_version_supported = false;
  84                         core->resp_received = true;
  85                         break;
  86                 case AVCS_CMD_GET_FWK_VERSION:
  87                         if (result->status == ADSP_EUNSUPPORTED)
  88                                 core->fwk_version_supported = false;
  89                         core->resp_received = true;
  90                         break;
  91                 case AVCS_CMD_ADSP_EVENT_GET_STATE:
  92                         if (result->status == ADSP_EUNSUPPORTED)
  93                                 core->get_state_supported = false;
  94                         core->resp_received = true;
  95                         break;
  96                 }
  97                 break;
  98         }
  99         case AVCS_CMDRSP_GET_FWK_VERSION: {
 100                 struct avcs_cmdrsp_get_fwk_version *fwk;
 101 
 102                 fwk = data->payload;
 103 
 104                 core->fwk_version = kmemdup(data->payload,
 105                                             struct_size(fwk, svc_api_info,
 106                                                         fwk->num_services),
 107                                             GFP_ATOMIC);
 108                 if (!core->fwk_version)
 109                         return -ENOMEM;
 110 
 111                 core->fwk_version_supported = true;
 112                 core->resp_received = true;
 113 
 114                 break;
 115         }
 116         case AVCS_GET_VERSIONS_RSP: {
 117                 struct avcs_cmdrsp_get_version *v;
 118 
 119                 v = data->payload;
 120 
 121                 core->svc_version = kmemdup(data->payload,
 122                                             struct_size(v, svc_api_info,
 123                                                         v->num_services),
 124                                             GFP_ATOMIC);
 125                 if (!core->svc_version)
 126                         return -ENOMEM;
 127 
 128                 core->get_version_supported = true;
 129                 core->resp_received = true;
 130 
 131                 break;
 132         }
 133         case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
 134                 core->get_state_supported = true;
 135                 core->avcs_state = result->opcode;
 136 
 137                 core->resp_received = true;
 138                 break;
 139         default:
 140                 dev_err(&adev->dev, "Message id from adsp core svc: 0x%x\n",
 141                         hdr->opcode);
 142                 break;
 143         }
 144 
 145         if (core->resp_received)
 146                 wake_up(&core->wait);
 147 
 148         return 0;
 149 }
 150 
 151 static int q6core_get_fwk_versions(struct q6core *core)
 152 {
 153         struct apr_device *adev = core->adev;
 154         struct apr_pkt pkt;
 155         int rc;
 156 
 157         pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
 158                                       APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
 159         pkt.hdr.pkt_size = APR_HDR_SIZE;
 160         pkt.hdr.opcode = AVCS_CMD_GET_FWK_VERSION;
 161 
 162         rc = apr_send_pkt(adev, &pkt);
 163         if (rc < 0)
 164                 return rc;
 165 
 166         rc = wait_event_timeout(core->wait, (core->resp_received),
 167                                 msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
 168         if (rc > 0 && core->resp_received) {
 169                 core->resp_received = false;
 170 
 171                 if (!core->fwk_version_supported)
 172                         return -ENOTSUPP;
 173                 else
 174                         return 0;
 175         }
 176 
 177 
 178         return rc;
 179 }
 180 
 181 static int q6core_get_svc_versions(struct q6core *core)
 182 {
 183         struct apr_device *adev = core->adev;
 184         struct apr_pkt pkt;
 185         int rc;
 186 
 187         pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
 188                                       APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
 189         pkt.hdr.pkt_size = APR_HDR_SIZE;
 190         pkt.hdr.opcode = AVCS_GET_VERSIONS;
 191 
 192         rc = apr_send_pkt(adev, &pkt);
 193         if (rc < 0)
 194                 return rc;
 195 
 196         rc = wait_event_timeout(core->wait, (core->resp_received),
 197                                 msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
 198         if (rc > 0 && core->resp_received) {
 199                 core->resp_received = false;
 200                 return 0;
 201         }
 202 
 203         return rc;
 204 }
 205 
 206 static bool __q6core_is_adsp_ready(struct q6core *core)
 207 {
 208         struct apr_device *adev = core->adev;
 209         struct apr_pkt pkt;
 210         int rc;
 211 
 212         core->get_state_supported = false;
 213 
 214         pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
 215                                       APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
 216         pkt.hdr.pkt_size = APR_HDR_SIZE;
 217         pkt.hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE;
 218 
 219         rc = apr_send_pkt(adev, &pkt);
 220         if (rc < 0)
 221                 return false;
 222 
 223         rc = wait_event_timeout(core->wait, (core->resp_received),
 224                                 msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
 225         if (rc > 0 && core->resp_received) {
 226                 core->resp_received = false;
 227 
 228                 if (core->avcs_state)
 229                         return true;
 230         }
 231 
 232         /* assume that the adsp is up if we not support this command */
 233         if (!core->get_state_supported)
 234                 return true;
 235 
 236         return false;
 237 }
 238 
 239 /**
 240  * q6core_get_svc_api_info() - Get version number of a service.
 241  *
 242  * @svc_id: service id of the service.
 243  * @ainfo: Valid struct pointer to fill svc api information.
 244  *
 245  * Return: zero on success and error code on failure or unsupported
 246  */
 247 int q6core_get_svc_api_info(int svc_id, struct q6core_svc_api_info *ainfo)
 248 {
 249         int i;
 250         int ret = -ENOTSUPP;
 251 
 252         if (!g_core || !ainfo)
 253                 return 0;
 254 
 255         mutex_lock(&g_core->lock);
 256         if (!g_core->is_version_requested) {
 257                 if (q6core_get_fwk_versions(g_core) == -ENOTSUPP)
 258                         q6core_get_svc_versions(g_core);
 259                 g_core->is_version_requested = true;
 260         }
 261 
 262         if (g_core->fwk_version_supported) {
 263                 for (i = 0; i < g_core->fwk_version->num_services; i++) {
 264                         struct avcs_svc_api_info *info;
 265 
 266                         info = &g_core->fwk_version->svc_api_info[i];
 267                         if (svc_id != info->service_id)
 268                                 continue;
 269 
 270                         ainfo->api_version = info->api_version;
 271                         ainfo->api_branch_version = info->api_branch_version;
 272                         ret = 0;
 273                         break;
 274                 }
 275         } else if (g_core->get_version_supported) {
 276                 for (i = 0; i < g_core->svc_version->num_services; i++) {
 277                         struct avcs_svc_info *info;
 278 
 279                         info = &g_core->svc_version->svc_api_info[i];
 280                         if (svc_id != info->service_id)
 281                                 continue;
 282 
 283                         ainfo->api_version = info->version;
 284                         ainfo->api_branch_version = 0;
 285                         ret = 0;
 286                         break;
 287                 }
 288         }
 289 
 290         mutex_unlock(&g_core->lock);
 291 
 292         return ret;
 293 }
 294 EXPORT_SYMBOL_GPL(q6core_get_svc_api_info);
 295 
 296 /**
 297  * q6core_is_adsp_ready() - Get status of adsp
 298  *
 299  * Return: Will be an true if adsp is ready and false if not.
 300  */
 301 bool q6core_is_adsp_ready(void)
 302 {
 303         unsigned long  timeout;
 304         bool ret = false;
 305 
 306         if (!g_core)
 307                 return false;
 308 
 309         mutex_lock(&g_core->lock);
 310         timeout = jiffies + msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
 311         for (;;) {
 312                 if (__q6core_is_adsp_ready(g_core)) {
 313                         ret = true;
 314                         break;
 315                 }
 316 
 317                 if (!time_after(timeout, jiffies)) {
 318                         ret = false;
 319                         break;
 320                 }
 321         }
 322 
 323         mutex_unlock(&g_core->lock);
 324         return ret;
 325 }
 326 EXPORT_SYMBOL_GPL(q6core_is_adsp_ready);
 327 
 328 static int q6core_probe(struct apr_device *adev)
 329 {
 330         g_core = kzalloc(sizeof(*g_core), GFP_KERNEL);
 331         if (!g_core)
 332                 return -ENOMEM;
 333 
 334         dev_set_drvdata(&adev->dev, g_core);
 335 
 336         mutex_init(&g_core->lock);
 337         g_core->adev = adev;
 338         init_waitqueue_head(&g_core->wait);
 339         return 0;
 340 }
 341 
 342 static int q6core_exit(struct apr_device *adev)
 343 {
 344         struct q6core *core = dev_get_drvdata(&adev->dev);
 345 
 346         if (core->fwk_version_supported)
 347                 kfree(core->fwk_version);
 348         if (core->get_version_supported)
 349                 kfree(core->svc_version);
 350 
 351         g_core = NULL;
 352         kfree(core);
 353 
 354         return 0;
 355 }
 356 
 357 static const struct of_device_id q6core_device_id[]  = {
 358         { .compatible = "qcom,q6core" },
 359         {},
 360 };
 361 MODULE_DEVICE_TABLE(of, q6core_device_id);
 362 
 363 static struct apr_driver qcom_q6core_driver = {
 364         .probe = q6core_probe,
 365         .remove = q6core_exit,
 366         .callback = q6core_callback,
 367         .driver = {
 368                 .name = "qcom-q6core",
 369                 .of_match_table = of_match_ptr(q6core_device_id),
 370         },
 371 };
 372 
 373 module_apr_driver(qcom_q6core_driver);
 374 MODULE_DESCRIPTION("q6 core");
 375 MODULE_LICENSE("GPL v2");

/* [<][>][^][v][top][bottom][index][help] */