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 skl_dfw_module_mod {
  32        char name[100];
  33        struct skl_dfw_module skl_dfw_mod;
  34};
  35
  36struct UUID {
  37        u8 id[16];
  38};
  39
  40union seg_flags {
  41        u32 ul;
  42        struct {
  43                u32 contents : 1;
  44                u32 alloc    : 1;
  45                u32 load     : 1;
  46                u32 read_only : 1;
  47                u32 code     : 1;
  48                u32 data     : 1;
  49                u32 _rsvd0   : 2;
  50                u32 type     : 4;
  51                u32 _rsvd1   : 4;
  52                u32 length   : 16;
  53        } r;
  54} __packed;
  55
  56struct segment_desc {
  57        union seg_flags flags;
  58        u32 v_base_addr;
  59        u32 file_offset;
  60};
  61
  62struct module_type {
  63        u32 load_type  : 4;
  64        u32 auto_start : 1;
  65        u32 domain_ll  : 1;
  66        u32 domain_dp  : 1;
  67        u32 rsvd       : 25;
  68} __packed;
  69
  70struct adsp_module_entry {
  71        u32 struct_id;
  72        u8  name[8];
  73        struct UUID uuid;
  74        struct module_type type;
  75        u8  hash1[DEFAULT_HASH_SHA256_LEN];
  76        u32 entry_point;
  77        u16 cfg_offset;
  78        u16 cfg_count;
  79        u32 affinity_mask;
  80        u16 instance_max_count;
  81        u16 instance_bss_size;
  82        struct segment_desc segments[3];
  83} __packed;
  84
  85struct adsp_fw_hdr {
  86        u32 id;
  87        u32 len;
  88        u8  name[8];
  89        u32 preload_page_count;
  90        u32 fw_image_flags;
  91        u32 feature_mask;
  92        u16 major;
  93        u16 minor;
  94        u16 hotfix;
  95        u16 build;
  96        u32 num_modules;
  97        u32 hw_buf_base;
  98        u32 hw_buf_length;
  99        u32 load_offset;
 100} __packed;
 101
 102struct uuid_module {
 103        uuid_le uuid;
 104        int id;
 105        int is_loadable;
 106
 107        struct list_head list;
 108};
 109
 110struct skl_ext_manifest_hdr {
 111        u32 id;
 112        u32 len;
 113        u16 version_major;
 114        u16 version_minor;
 115        u32 entries;
 116};
 117
 118int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
 119                        struct skl_dfw_module *dfw_config)
 120{
 121        struct uuid_module *module;
 122        uuid_le *uuid_mod;
 123
 124        uuid_mod = (uuid_le *)uuid;
 125
 126        if (list_empty(&ctx->uuid_list)) {
 127                dev_err(ctx->dev, "Module list is empty\n");
 128                return -EINVAL;
 129        }
 130
 131        list_for_each_entry(module, &ctx->uuid_list, list) {
 132                if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
 133                        dfw_config->module_id = module->id;
 134                        dfw_config->is_loadable = module->is_loadable;
 135
 136                        return 0;
 137                }
 138        }
 139
 140        return -EINVAL;
 141}
 142EXPORT_SYMBOL_GPL(snd_skl_get_module_info);
 143
 144/*
 145 * Parse the firmware binary to get the UUID, module id
 146 * and loadable flags
 147 */
 148int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset)
 149{
 150        struct adsp_fw_hdr *adsp_hdr;
 151        struct adsp_module_entry *mod_entry;
 152        int i, num_entry;
 153        uuid_le *uuid_bin;
 154        const char *buf;
 155        struct skl_sst *skl = ctx->thread_context;
 156        struct uuid_module *module;
 157        struct firmware stripped_fw;
 158        unsigned int safe_file;
 159
 160        /* Get the FW pointer to derive ADSP header */
 161        stripped_fw.data = ctx->fw->data;
 162        stripped_fw.size = ctx->fw->size;
 163
 164        skl_dsp_strip_extended_manifest(&stripped_fw);
 165
 166        buf = stripped_fw.data;
 167
 168        /* check if we have enough space in file to move to header */
 169        safe_file = sizeof(*adsp_hdr) + offset;
 170        if (stripped_fw.size <= safe_file) {
 171                dev_err(ctx->dev, "Small fw file size, No space for hdr\n");
 172                return -EINVAL;
 173        }
 174
 175        adsp_hdr = (struct adsp_fw_hdr *)(buf + offset);
 176
 177        /* check 1st module entry is in file */
 178        safe_file += adsp_hdr->len + sizeof(*mod_entry);
 179        if (stripped_fw.size <= safe_file) {
 180                dev_err(ctx->dev, "Small fw file size, No module entry\n");
 181                return -EINVAL;
 182        }
 183
 184        mod_entry = (struct adsp_module_entry *)
 185                (buf + offset + adsp_hdr->len);
 186
 187        num_entry = adsp_hdr->num_modules;
 188
 189        /* check all entries are in file */
 190        safe_file += num_entry * sizeof(*mod_entry);
 191        if (stripped_fw.size <= safe_file) {
 192                dev_err(ctx->dev, "Small fw file size, No modules\n");
 193                return -EINVAL;
 194        }
 195
 196
 197        /*
 198         * Read the UUID(GUID) from FW Manifest.
 199         *
 200         * The 16 byte UUID format is: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX
 201         * Populate the UUID table to store module_id and loadable flags
 202         * for the module.
 203         */
 204
 205        for (i = 0; i < num_entry; i++, mod_entry++) {
 206                module = kzalloc(sizeof(*module), GFP_KERNEL);
 207                if (!module)
 208                        return -ENOMEM;
 209
 210                uuid_bin = (uuid_le *)mod_entry->uuid.id;
 211                memcpy(&module->uuid, uuid_bin, sizeof(module->uuid));
 212
 213                module->id = i;
 214                module->is_loadable = mod_entry->type.load_type;
 215
 216                list_add_tail(&module->list, &skl->uuid_list);
 217
 218                dev_dbg(ctx->dev,
 219                        "Adding uuid :%pUL   mod id: %d  Loadable: %d\n",
 220                        &module->uuid, module->id, module->is_loadable);
 221        }
 222
 223        return 0;
 224}
 225
 226void skl_freeup_uuid_list(struct skl_sst *ctx)
 227{
 228        struct uuid_module *uuid, *_uuid;
 229
 230        list_for_each_entry_safe(uuid, _uuid, &ctx->uuid_list, list) {
 231                list_del(&uuid->list);
 232                kfree(uuid);
 233        }
 234}
 235
 236/*
 237 * some firmware binary contains some extended manifest. This needs
 238 * to be stripped in that case before we load and use that image.
 239 *
 240 * Get the module id for the module by checking
 241 * the table for the UUID for the module
 242 */
 243int skl_dsp_strip_extended_manifest(struct firmware *fw)
 244{
 245        struct skl_ext_manifest_hdr *hdr;
 246
 247        /* check if fw file is greater than header we are looking */
 248        if (fw->size < sizeof(hdr)) {
 249                pr_err("%s: Firmware file small, no hdr\n", __func__);
 250                return -EINVAL;
 251        }
 252
 253        hdr = (struct skl_ext_manifest_hdr *)fw->data;
 254
 255        if (hdr->id == SKL_EXT_MANIFEST_HEADER_MAGIC) {
 256                fw->size -= hdr->len;
 257                fw->data += hdr->len;
 258        }
 259
 260        return 0;
 261}
 262