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