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