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 * @mconfig: module configuration data
 182 *
 183 * This generates a 128 bit private unique id for a module TYPE so that
 184 * module instance is unique
 185 */
 186int skl_get_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int instance_id)
 187{
 188        struct uuid_module *module;
 189        int pvt_id;
 190
 191        list_for_each_entry(module, &ctx->uuid_list, list) {
 192                if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
 193
 194                        pvt_id = skl_pvtid_128(module);
 195                        if (pvt_id >= 0) {
 196                                module->instance_id[pvt_id] = instance_id;
 197
 198                                return pvt_id;
 199                        }
 200                }
 201        }
 202
 203        return -EINVAL;
 204}
 205EXPORT_SYMBOL_GPL(skl_get_pvt_id);
 206
 207/**
 208 * skl_put_pvt_id: free up the private id allocated
 209 *
 210 * @ctx: driver context
 211 * @mconfig: module configuration data
 212 *
 213 * This frees a 128 bit private unique id previously generated
 214 */
 215int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id)
 216{
 217        int i;
 218        struct uuid_module *module;
 219
 220        list_for_each_entry(module, &ctx->uuid_list, list) {
 221                if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
 222
 223                        if (*pvt_id != 0)
 224                                i = (*pvt_id) / 64;
 225                        else
 226                                i = 0;
 227
 228                        module->pvt_id[i] &= ~(1 << (*pvt_id));
 229                        *pvt_id = -1;
 230                        return 0;
 231                }
 232        }
 233
 234        return -EINVAL;
 235}
 236EXPORT_SYMBOL_GPL(skl_put_pvt_id);
 237
 238/*
 239 * Parse the firmware binary to get the UUID, module id
 240 * and loadable flags
 241 */
 242int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw,
 243                        unsigned int offset, int index)
 244{
 245        struct adsp_fw_hdr *adsp_hdr;
 246        struct adsp_module_entry *mod_entry;
 247        int i, num_entry, size;
 248        uuid_le *uuid_bin;
 249        const char *buf;
 250        struct skl_sst *skl = ctx->thread_context;
 251        struct uuid_module *module;
 252        struct firmware stripped_fw;
 253        unsigned int safe_file;
 254
 255        /* Get the FW pointer to derive ADSP header */
 256        stripped_fw.data = fw->data;
 257        stripped_fw.size = fw->size;
 258
 259        skl_dsp_strip_extended_manifest(&stripped_fw);
 260
 261        buf = stripped_fw.data;
 262
 263        /* check if we have enough space in file to move to header */
 264        safe_file = sizeof(*adsp_hdr) + offset;
 265        if (stripped_fw.size <= safe_file) {
 266                dev_err(ctx->dev, "Small fw file size, No space for hdr\n");
 267                return -EINVAL;
 268        }
 269
 270        adsp_hdr = (struct adsp_fw_hdr *)(buf + offset);
 271
 272        /* check 1st module entry is in file */
 273        safe_file += adsp_hdr->len + sizeof(*mod_entry);
 274        if (stripped_fw.size <= safe_file) {
 275                dev_err(ctx->dev, "Small fw file size, No module entry\n");
 276                return -EINVAL;
 277        }
 278
 279        mod_entry = (struct adsp_module_entry *)
 280                (buf + offset + adsp_hdr->len);
 281
 282        num_entry = adsp_hdr->num_modules;
 283
 284        /* check all entries are in file */
 285        safe_file += num_entry * sizeof(*mod_entry);
 286        if (stripped_fw.size <= safe_file) {
 287                dev_err(ctx->dev, "Small fw file size, No modules\n");
 288                return -EINVAL;
 289        }
 290
 291
 292        /*
 293         * Read the UUID(GUID) from FW Manifest.
 294         *
 295         * The 16 byte UUID format is: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX
 296         * Populate the UUID table to store module_id and loadable flags
 297         * for the module.
 298         */
 299
 300        for (i = 0; i < num_entry; i++, mod_entry++) {
 301                module = kzalloc(sizeof(*module), GFP_KERNEL);
 302                if (!module)
 303                        return -ENOMEM;
 304
 305                uuid_bin = (uuid_le *)mod_entry->uuid.id;
 306                memcpy(&module->uuid, uuid_bin, sizeof(module->uuid));
 307
 308                module->id = (i | (index << 12));
 309                module->is_loadable = mod_entry->type.load_type;
 310                module->max_instance = mod_entry->instance_max_count;
 311                size = sizeof(int) * mod_entry->instance_max_count;
 312                module->instance_id = devm_kzalloc(ctx->dev, size, GFP_KERNEL);
 313                if (!module->instance_id) {
 314                        kfree(module);
 315                        return -ENOMEM;
 316                }
 317
 318                list_add_tail(&module->list, &skl->uuid_list);
 319
 320                dev_dbg(ctx->dev,
 321                        "Adding uuid :%pUL   mod id: %d  Loadable: %d\n",
 322                        &module->uuid, module->id, module->is_loadable);
 323        }
 324
 325        return 0;
 326}
 327
 328void skl_freeup_uuid_list(struct skl_sst *ctx)
 329{
 330        struct uuid_module *uuid, *_uuid;
 331
 332        list_for_each_entry_safe(uuid, _uuid, &ctx->uuid_list, list) {
 333                list_del(&uuid->list);
 334                kfree(uuid);
 335        }
 336}
 337
 338/*
 339 * some firmware binary contains some extended manifest. This needs
 340 * to be stripped in that case before we load and use that image.
 341 *
 342 * Get the module id for the module by checking
 343 * the table for the UUID for the module
 344 */
 345int skl_dsp_strip_extended_manifest(struct firmware *fw)
 346{
 347        struct skl_ext_manifest_hdr *hdr;
 348
 349        /* check if fw file is greater than header we are looking */
 350        if (fw->size < sizeof(hdr)) {
 351                pr_err("%s: Firmware file small, no hdr\n", __func__);
 352                return -EINVAL;
 353        }
 354
 355        hdr = (struct skl_ext_manifest_hdr *)fw->data;
 356
 357        if (hdr->id == SKL_EXT_MANIFEST_HEADER_MAGIC) {
 358                fw->size -= hdr->len;
 359                fw->data += hdr->len;
 360        }
 361
 362        return 0;
 363}
 364
 365int skl_sst_ctx_init(struct device *dev, int irq, const char *fw_name,
 366        struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp,
 367        struct sst_dsp_device *skl_dev)
 368{
 369        struct skl_sst *skl;
 370        struct sst_dsp *sst;
 371        int ret;
 372
 373        skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
 374        if (skl == NULL)
 375                return -ENOMEM;
 376
 377        skl->dev = dev;
 378        skl_dev->thread_context = skl;
 379        INIT_LIST_HEAD(&skl->uuid_list);
 380        skl->dsp = skl_dsp_ctx_init(dev, skl_dev, irq);
 381        if (!skl->dsp) {
 382                dev_err(skl->dev, "%s: no device\n", __func__);
 383                return -ENODEV;
 384        }
 385
 386        sst = skl->dsp;
 387        sst->fw_name = fw_name;
 388        sst->dsp_ops = dsp_ops;
 389        init_waitqueue_head(&skl->mod_load_wait);
 390        INIT_LIST_HEAD(&sst->module_list);
 391        ret = skl_ipc_init(dev, skl);
 392        if (ret)
 393                return ret;
 394
 395        skl->is_first_boot = true;
 396        if (dsp)
 397                *dsp = skl;
 398
 399        return ret;
 400}
 401
 402int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo,
 403                struct firmware *stripped_fw,
 404                unsigned int hdr_offset, int index)
 405{
 406        int ret;
 407        struct sst_dsp *dsp = skl->dsp;
 408
 409        if (linfo->fw == NULL) {
 410                ret = request_firmware(&linfo->fw, linfo->name,
 411                                        skl->dev);
 412                if (ret < 0) {
 413                        dev_err(skl->dev, "Request lib %s failed:%d\n",
 414                                linfo->name, ret);
 415                        return ret;
 416                }
 417        }
 418
 419        if (skl->is_first_boot) {
 420                ret = snd_skl_parse_uuids(dsp, linfo->fw, hdr_offset, index);
 421                if (ret < 0)
 422                        return ret;
 423        }
 424
 425        stripped_fw->data = linfo->fw->data;
 426        stripped_fw->size = linfo->fw->size;
 427        skl_dsp_strip_extended_manifest(stripped_fw);
 428
 429        return 0;
 430}
 431
 432void skl_release_library(struct skl_lib_info *linfo, int lib_count)
 433{
 434        int i;
 435
 436        /* library indices start from 1 to N. 0 represents base FW */
 437        for (i = 1; i < lib_count; i++) {
 438                if (linfo[i].fw) {
 439                        release_firmware(linfo[i].fw);
 440                        linfo[i].fw = NULL;
 441                }
 442        }
 443}
 444