linux/sound/soc/intel/skylake/skl-sst-utils.c
<<
>>
Prefs
   1/*
   2 *  skl-sst-utils.c - SKL sst utils functions
   3 *
   4 *  Copyright (C) 2016 Intel Corp
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as version 2, as
   8 * published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13 * General Public License for more details.
  14 */
  15
  16#include <linux/device.h>
  17#include <linux/slab.h>
  18#include <linux/uuid.h>
  19#include "skl-sst-dsp.h"
  20#include "../common/sst-dsp.h"
  21#include "../common/sst-dsp-priv.h"
  22#include "skl-sst-ipc.h"
  23
  24
  25#define UUID_STR_SIZE 37
  26#define DEFAULT_HASH_SHA256_LEN 32
  27
  28/* FW Extended Manifest Header id = $AE1 */
  29#define SKL_EXT_MANIFEST_HEADER_MAGIC   0x31454124
  30
  31struct UUID {
  32        u8 id[16];
  33};
  34
  35union seg_flags {
  36        u32 ul;
  37        struct {
  38                u32 contents : 1;
  39                u32 alloc    : 1;
  40                u32 load     : 1;
  41                u32 read_only : 1;
  42                u32 code     : 1;
  43                u32 data     : 1;
  44                u32 _rsvd0   : 2;
  45                u32 type     : 4;
  46                u32 _rsvd1   : 4;
  47                u32 length   : 16;
  48        } r;
  49} __packed;
  50
  51struct segment_desc {
  52        union seg_flags flags;
  53        u32 v_base_addr;
  54        u32 file_offset;
  55};
  56
  57struct module_type {
  58        u32 load_type  : 4;
  59        u32 auto_start : 1;
  60        u32 domain_ll  : 1;
  61        u32 domain_dp  : 1;
  62        u32 rsvd       : 25;
  63} __packed;
  64
  65struct adsp_module_entry {
  66        u32 struct_id;
  67        u8  name[8];
  68        struct UUID uuid;
  69        struct module_type type;
  70        u8  hash1[DEFAULT_HASH_SHA256_LEN];
  71        u32 entry_point;
  72        u16 cfg_offset;
  73        u16 cfg_count;
  74        u32 affinity_mask;
  75        u16 instance_max_count;
  76        u16 instance_bss_size;
  77        struct segment_desc segments[3];
  78} __packed;
  79
  80struct adsp_fw_hdr {
  81        u32 id;
  82        u32 len;
  83        u8  name[8];
  84        u32 preload_page_count;
  85        u32 fw_image_flags;
  86        u32 feature_mask;
  87        u16 major;
  88        u16 minor;
  89        u16 hotfix;
  90        u16 build;
  91        u32 num_modules;
  92        u32 hw_buf_base;
  93        u32 hw_buf_length;
  94        u32 load_offset;
  95} __packed;
  96
  97struct skl_ext_manifest_hdr {
  98        u32 id;
  99        u32 len;
 100        u16 version_major;
 101        u16 version_minor;
 102        u32 entries;
 103};
 104
 105static int skl_get_pvtid_map(struct uuid_module *module, int instance_id)
 106{
 107        int pvt_id;
 108
 109        for (pvt_id = 0; pvt_id < module->max_instance; pvt_id++) {
 110                if (module->instance_id[pvt_id] == instance_id)
 111                        return pvt_id;
 112        }
 113        return -EINVAL;
 114}
 115
 116int skl_get_pvt_instance_id_map(struct skl_sst *ctx,
 117                                int module_id, int instance_id)
 118{
 119        struct uuid_module *module;
 120
 121        list_for_each_entry(module, &ctx->uuid_list, list) {
 122                if (module->id == module_id)
 123                        return skl_get_pvtid_map(module, instance_id);
 124        }
 125
 126        return -EINVAL;
 127}
 128EXPORT_SYMBOL_GPL(skl_get_pvt_instance_id_map);
 129
 130static inline int skl_getid_32(struct uuid_module *module, u64 *val,
 131                                int word1_mask, int word2_mask)
 132{
 133        int index, max_inst, pvt_id;
 134        u32 mask_val;
 135
 136        max_inst =  module->max_instance;
 137        mask_val = (u32)(*val >> word1_mask);
 138
 139        if (mask_val != 0xffffffff) {
 140                index = ffz(mask_val);
 141                pvt_id = index + word1_mask + word2_mask;
 142                if (pvt_id <= (max_inst - 1)) {
 143                        *val |= 1ULL << (index + word1_mask);
 144                        return pvt_id;
 145                }
 146        }
 147
 148        return -EINVAL;
 149}
 150
 151static inline int skl_pvtid_128(struct uuid_module *module)
 152{
 153        int j, i, word1_mask, word2_mask = 0, pvt_id;
 154
 155        for (j = 0; j < MAX_INSTANCE_BUFF; j++) {
 156                word1_mask = 0;
 157
 158                for (i = 0; i < 2; i++) {
 159                        pvt_id = skl_getid_32(module, &module->pvt_id[j],
 160                                                word1_mask, word2_mask);
 161                        if (pvt_id >= 0)
 162                                return pvt_id;
 163
 164                        word1_mask += 32;
 165                        if ((word1_mask + word2_mask) >= module->max_instance)
 166                                return -EINVAL;
 167                }
 168
 169                word2_mask += 64;
 170                if (word2_mask >= module->max_instance)
 171                        return -EINVAL;
 172        }
 173
 174        return -EINVAL;
 175}
 176
 177/**
 178 * skl_get_pvt_id: generate a private id for use as module id
 179 *
 180 * @ctx: driver context
 181 * @uuid_mod: module's uuid
 182 * @instance_id: module's instance id
 183 *
 184 * This generates a 128 bit private unique id for a module TYPE so that
 185 * module instance is unique
 186 */
 187int skl_get_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int instance_id)
 188{
 189        struct uuid_module *module;
 190        int pvt_id;
 191
 192        list_for_each_entry(module, &ctx->uuid_list, list) {
 193                if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
 194
 195                        pvt_id = skl_pvtid_128(module);
 196                        if (pvt_id >= 0) {
 197                                module->instance_id[pvt_id] = instance_id;
 198
 199                                return pvt_id;
 200                        }
 201                }
 202        }
 203
 204        return -EINVAL;
 205}
 206EXPORT_SYMBOL_GPL(skl_get_pvt_id);
 207
 208/**
 209 * skl_put_pvt_id: free up the private id allocated
 210 *
 211 * @ctx: driver context
 212 * @uuid_mod: module's uuid
 213 * @pvt_id: module pvt id
 214 *
 215 * This frees a 128 bit private unique id previously generated
 216 */
 217int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id)
 218{
 219        int i;
 220        struct uuid_module *module;
 221
 222        list_for_each_entry(module, &ctx->uuid_list, list) {
 223                if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
 224
 225                        if (*pvt_id != 0)
 226                                i = (*pvt_id) / 64;
 227                        else
 228                                i = 0;
 229
 230                        module->pvt_id[i] &= ~(1 << (*pvt_id));
 231                        *pvt_id = -1;
 232                        return 0;
 233                }
 234        }
 235
 236        return -EINVAL;
 237}
 238EXPORT_SYMBOL_GPL(skl_put_pvt_id);
 239
 240/*
 241 * Parse the firmware binary to get the UUID, module id
 242 * and loadable flags
 243 */
 244int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw,
 245                        unsigned int offset, int index)
 246{
 247        struct adsp_fw_hdr *adsp_hdr;
 248        struct adsp_module_entry *mod_entry;
 249        int i, num_entry, size;
 250        uuid_le *uuid_bin;
 251        const char *buf;
 252        struct skl_sst *skl = ctx->thread_context;
 253        struct uuid_module *module;
 254        struct firmware stripped_fw;
 255        unsigned int safe_file;
 256        int ret = 0;
 257
 258        /* Get the FW pointer to derive ADSP header */
 259        stripped_fw.data = fw->data;
 260        stripped_fw.size = fw->size;
 261
 262        skl_dsp_strip_extended_manifest(&stripped_fw);
 263
 264        buf = stripped_fw.data;
 265
 266        /* check if we have enough space in file to move to header */
 267        safe_file = sizeof(*adsp_hdr) + offset;
 268        if (stripped_fw.size <= safe_file) {
 269                dev_err(ctx->dev, "Small fw file size, No space for hdr\n");
 270                return -EINVAL;
 271        }
 272
 273        adsp_hdr = (struct adsp_fw_hdr *)(buf + offset);
 274
 275        /* check 1st module entry is in file */
 276        safe_file += adsp_hdr->len + sizeof(*mod_entry);
 277        if (stripped_fw.size <= safe_file) {
 278                dev_err(ctx->dev, "Small fw file size, No module entry\n");
 279                return -EINVAL;
 280        }
 281
 282        mod_entry = (struct adsp_module_entry *)
 283                (buf + offset + adsp_hdr->len);
 284
 285        num_entry = adsp_hdr->num_modules;
 286
 287        /* check all entries are in file */
 288        safe_file += num_entry * sizeof(*mod_entry);
 289        if (stripped_fw.size <= safe_file) {
 290                dev_err(ctx->dev, "Small fw file size, No modules\n");
 291                return -EINVAL;
 292        }
 293
 294
 295        /*
 296         * Read the UUID(GUID) from FW Manifest.
 297         *
 298         * The 16 byte UUID format is: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX
 299         * Populate the UUID table to store module_id and loadable flags
 300         * for the module.
 301         */
 302
 303        for (i = 0; i < num_entry; i++, mod_entry++) {
 304                module = kzalloc(sizeof(*module), GFP_KERNEL);
 305                if (!module) {
 306                        ret = -ENOMEM;
 307                        goto free_uuid_list;
 308                }
 309
 310                uuid_bin = (uuid_le *)mod_entry->uuid.id;
 311                memcpy(&module->uuid, uuid_bin, sizeof(module->uuid));
 312
 313                module->id = (i | (index << 12));
 314                module->is_loadable = mod_entry->type.load_type;
 315                module->max_instance = mod_entry->instance_max_count;
 316                size = sizeof(int) * mod_entry->instance_max_count;
 317                module->instance_id = devm_kzalloc(ctx->dev, size, GFP_KERNEL);
 318                if (!module->instance_id) {
 319                        ret = -ENOMEM;
 320                        goto free_uuid_list;
 321                }
 322
 323                list_add_tail(&module->list, &skl->uuid_list);
 324
 325                dev_dbg(ctx->dev,
 326                        "Adding uuid :%pUL   mod id: %d  Loadable: %d\n",
 327                        &module->uuid, module->id, module->is_loadable);
 328        }
 329
 330        return 0;
 331
 332free_uuid_list:
 333        skl_freeup_uuid_list(skl);
 334        return ret;
 335}
 336
 337void skl_freeup_uuid_list(struct skl_sst *ctx)
 338{
 339        struct uuid_module *uuid, *_uuid;
 340
 341        list_for_each_entry_safe(uuid, _uuid, &ctx->uuid_list, list) {
 342                list_del(&uuid->list);
 343                kfree(uuid);
 344        }
 345}
 346
 347/*
 348 * some firmware binary contains some extended manifest. This needs
 349 * to be stripped in that case before we load and use that image.
 350 *
 351 * Get the module id for the module by checking
 352 * the table for the UUID for the module
 353 */
 354int skl_dsp_strip_extended_manifest(struct firmware *fw)
 355{
 356        struct skl_ext_manifest_hdr *hdr;
 357
 358        /* check if fw file is greater than header we are looking */
 359        if (fw->size < sizeof(hdr)) {
 360                pr_err("%s: Firmware file small, no hdr\n", __func__);
 361                return -EINVAL;
 362        }
 363
 364        hdr = (struct skl_ext_manifest_hdr *)fw->data;
 365
 366        if (hdr->id == SKL_EXT_MANIFEST_HEADER_MAGIC) {
 367                fw->size -= hdr->len;
 368                fw->data += hdr->len;
 369        }
 370
 371        return 0;
 372}
 373
 374int skl_sst_ctx_init(struct device *dev, int irq, const char *fw_name,
 375        struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp,
 376        struct sst_dsp_device *skl_dev)
 377{
 378        struct skl_sst *skl;
 379        struct sst_dsp *sst;
 380
 381        skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
 382        if (skl == NULL)
 383                return -ENOMEM;
 384
 385        skl->dev = dev;
 386        skl_dev->thread_context = skl;
 387        INIT_LIST_HEAD(&skl->uuid_list);
 388        skl->dsp = skl_dsp_ctx_init(dev, skl_dev, irq);
 389        if (!skl->dsp) {
 390                dev_err(skl->dev, "%s: no device\n", __func__);
 391                return -ENODEV;
 392        }
 393
 394        sst = skl->dsp;
 395        sst->fw_name = fw_name;
 396        sst->dsp_ops = dsp_ops;
 397        init_waitqueue_head(&skl->mod_load_wait);
 398        INIT_LIST_HEAD(&sst->module_list);
 399
 400        skl->is_first_boot = true;
 401        if (dsp)
 402                *dsp = skl;
 403
 404        return 0;
 405}
 406
 407int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo,
 408                struct firmware *stripped_fw,
 409                unsigned int hdr_offset, int index)
 410{
 411        int ret;
 412        struct sst_dsp *dsp = skl->dsp;
 413
 414        if (linfo->fw == NULL) {
 415                ret = request_firmware(&linfo->fw, linfo->name,
 416                                        skl->dev);
 417                if (ret < 0) {
 418                        dev_err(skl->dev, "Request lib %s failed:%d\n",
 419                                linfo->name, ret);
 420                        return ret;
 421                }
 422        }
 423
 424        if (skl->is_first_boot) {
 425                ret = snd_skl_parse_uuids(dsp, linfo->fw, hdr_offset, index);
 426                if (ret < 0)
 427                        return ret;
 428        }
 429
 430        stripped_fw->data = linfo->fw->data;
 431        stripped_fw->size = linfo->fw->size;
 432        skl_dsp_strip_extended_manifest(stripped_fw);
 433
 434        return 0;
 435}
 436
 437void skl_release_library(struct skl_lib_info *linfo, int lib_count)
 438{
 439        int i;
 440
 441        /* library indices start from 1 to N. 0 represents base FW */
 442        for (i = 1; i < lib_count; i++) {
 443                if (linfo[i].fw) {
 444                        release_firmware(linfo[i].fw);
 445                        linfo[i].fw = NULL;
 446                }
 447        }
 448}
 449