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/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
  26struct avcs_svc_info {
  27        uint32_t service_id;
  28        uint32_t version;
  29} __packed;
  30
  31struct 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 */
  38struct avcs_svc_api_info {
  39        uint32_t service_id;
  40        uint32_t api_version;
  41        uint32_t api_branch_version;
  42} __packed;
  43
  44struct 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
  53struct 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
  68static struct q6core *g_core;
  69
  70static 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
 151static 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
 181static 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
 206static 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 */
 247int 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}
 294EXPORT_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 */
 301bool 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}
 326EXPORT_SYMBOL_GPL(q6core_is_adsp_ready);
 327
 328static 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
 342static 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#ifdef CONFIG_OF
 358static const struct of_device_id q6core_device_id[]  = {
 359        { .compatible = "qcom,q6core" },
 360        {},
 361};
 362MODULE_DEVICE_TABLE(of, q6core_device_id);
 363#endif
 364
 365static struct apr_driver qcom_q6core_driver = {
 366        .probe = q6core_probe,
 367        .remove = q6core_exit,
 368        .callback = q6core_callback,
 369        .driver = {
 370                .name = "qcom-q6core",
 371                .of_match_table = of_match_ptr(q6core_device_id),
 372        },
 373};
 374
 375module_apr_driver(qcom_q6core_driver);
 376MODULE_DESCRIPTION("q6 core");
 377MODULE_LICENSE("GPL v2");
 378