linux/drivers/staging/media/atomisp/pci/atomisp2/css2400/sh_css_firmware.c
<<
>>
Prefs
   1/*
   2 * Support for Intel Camera Imaging ISP subsystem.
   3 * Copyright (c) 2015, Intel Corporation.
   4 *
   5 * This program is free software; you can redistribute it and/or modify it
   6 * under the terms and conditions of the GNU General Public License,
   7 * version 2, as published by the Free Software Foundation.
   8 *
   9 * This program is distributed in the hope it will be useful, but WITHOUT
  10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  12 * more details.
  13 */
  14
  15#include <linux/slab.h>
  16#include <linux/vmalloc.h>
  17
  18#include <math_support.h>
  19#include "platform_support.h"
  20#include "sh_css_firmware.h"
  21
  22#include "sh_css_defs.h"
  23#include "ia_css_debug.h"
  24#include "sh_css_internal.h"
  25#include "ia_css_isp_param.h"
  26
  27#include "memory_access.h"
  28#include "assert_support.h"
  29#include "string_support.h"
  30
  31#include "isp.h"                                /* PMEM_WIDTH_LOG2 */
  32
  33#include "ia_css_isp_params.h"
  34#include "ia_css_isp_configs.h"
  35#include "ia_css_isp_states.h"
  36
  37#define _STR(x) #x
  38#define STR(x) _STR(x)
  39
  40struct firmware_header {
  41        struct sh_css_fw_bi_file_h file_header;
  42        struct ia_css_fw_info      binary_header;
  43};
  44
  45struct fw_param {
  46        const char *name;
  47        const void *buffer;
  48};
  49
  50/* Warning: same order as SH_CSS_BINARY_ID_* */
  51static struct firmware_header *firmware_header;
  52
  53/* The string STR is a place holder
  54 * which will be replaced with the actual RELEASE_VERSION
  55 * during package generation. Please do not modify  */
  56#ifndef ISP2401
  57static const char *release_version = STR(irci_stable_candrpv_0415_20150521_0458);
  58#else
  59static const char *release_version = STR(irci_ecr-master_20150911_0724);
  60#endif
  61
  62#define MAX_FW_REL_VER_NAME     300
  63static char FW_rel_ver_name[MAX_FW_REL_VER_NAME] = "---";
  64
  65struct ia_css_fw_info     sh_css_sp_fw;
  66struct ia_css_blob_descr *sh_css_blob_info; /* Only ISP blob info (no SP) */
  67unsigned                  sh_css_num_binaries; /* This includes 1 SP binary */
  68
  69static struct fw_param *fw_minibuffer;
  70
  71
  72char *sh_css_get_fw_version(void)
  73{
  74        return FW_rel_ver_name;
  75}
  76
  77
  78/*
  79 * Split the loaded firmware into blobs
  80 */
  81
  82/* Setup sp/sp1 binary */
  83static enum ia_css_err
  84setup_binary(struct ia_css_fw_info *fw, const char *fw_data, struct ia_css_fw_info *sh_css_fw, unsigned binary_id)
  85{
  86        const char *blob_data;
  87
  88        if ((fw == NULL) || (fw_data == NULL))
  89                return IA_CSS_ERR_INVALID_ARGUMENTS;
  90
  91        blob_data = fw_data + fw->blob.offset;
  92
  93        *sh_css_fw = *fw;
  94
  95        sh_css_fw->blob.code = vmalloc(fw->blob.size);
  96        if (sh_css_fw->blob.code == NULL)
  97                return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
  98
  99        memcpy((void *)sh_css_fw->blob.code, blob_data, fw->blob.size);
 100        sh_css_fw->blob.data = (char *)sh_css_fw->blob.code + fw->blob.data_source;
 101        fw_minibuffer[binary_id].buffer = sh_css_fw->blob.code;
 102
 103        return IA_CSS_SUCCESS;
 104}
 105enum ia_css_err
 106sh_css_load_blob_info(const char *fw, const struct ia_css_fw_info *bi, struct ia_css_blob_descr *bd, unsigned index)
 107{
 108        const char *name;
 109        const unsigned char *blob;
 110
 111        if ((fw == NULL) || (bd == NULL))
 112                return IA_CSS_ERR_INVALID_ARGUMENTS;
 113
 114        /* Special case: only one binary in fw */
 115        if (bi == NULL) bi = (const struct ia_css_fw_info *)fw;
 116
 117        name = fw + bi->blob.prog_name_offset;
 118        blob = (const unsigned char *)fw + bi->blob.offset;
 119
 120        /* sanity check */
 121        if (bi->blob.size != bi->blob.text_size + bi->blob.icache_size + bi->blob.data_size + bi->blob.padding_size) {
 122                /* sanity check, note the padding bytes added for section to DDR alignment */
 123                return IA_CSS_ERR_INVALID_ARGUMENTS;
 124        }
 125
 126        if ((bi->blob.offset % (1UL<<(ISP_PMEM_WIDTH_LOG2-3))) != 0)
 127                return IA_CSS_ERR_INVALID_ARGUMENTS;
 128
 129        bd->blob = blob;
 130        bd->header = *bi;
 131
 132        if (bi->type == ia_css_isp_firmware || bi->type == ia_css_sp_firmware) {
 133                char *namebuffer;
 134
 135                namebuffer = kstrdup(name, GFP_KERNEL);
 136                if (!namebuffer)
 137                        return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
 138                bd->name = fw_minibuffer[index].name = namebuffer;
 139        } else {
 140                bd->name = name;
 141        }
 142
 143        if (bi->type == ia_css_isp_firmware) {
 144                size_t paramstruct_size = sizeof(struct ia_css_memory_offsets);
 145                size_t configstruct_size = sizeof(struct ia_css_config_memory_offsets);
 146                size_t statestruct_size = sizeof(struct ia_css_state_memory_offsets);
 147
 148                char *parambuf = (char *)kmalloc(paramstruct_size + configstruct_size + statestruct_size,
 149                                                        GFP_KERNEL);
 150                if (parambuf == NULL)
 151                        return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
 152
 153                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = NULL;
 154                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = NULL;
 155                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = NULL;
 156
 157                fw_minibuffer[index].buffer = parambuf;
 158
 159                /* copy ia_css_memory_offsets */
 160                memcpy(parambuf, (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_PARAM]),
 161                        paramstruct_size);
 162                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = parambuf;
 163
 164                /* copy ia_css_config_memory_offsets */
 165                memcpy(parambuf + paramstruct_size,
 166                                (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_CONFIG]),
 167                                configstruct_size);
 168                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = parambuf + paramstruct_size;
 169
 170                /* copy ia_css_state_memory_offsets */
 171                memcpy(parambuf + paramstruct_size + configstruct_size,
 172                                (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_STATE]),
 173                                statestruct_size);
 174                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = parambuf + paramstruct_size + configstruct_size;
 175        }
 176        return IA_CSS_SUCCESS;
 177}
 178
 179bool
 180sh_css_check_firmware_version(const char *fw_data)
 181{
 182        struct sh_css_fw_bi_file_h *file_header;
 183
 184        firmware_header = (struct firmware_header *)fw_data;
 185        file_header = &firmware_header->file_header;
 186
 187        if (strcmp(file_header->version, release_version) != 0) {
 188                return false;
 189        } else {
 190                /* firmware version matches */
 191                return true;
 192        }
 193}
 194
 195enum ia_css_err
 196sh_css_load_firmware(const char *fw_data,
 197                     unsigned int fw_size)
 198{
 199        unsigned i;
 200        struct ia_css_fw_info *binaries;
 201        struct sh_css_fw_bi_file_h *file_header;
 202        bool valid_firmware = false;
 203
 204        firmware_header = (struct firmware_header *)fw_data;
 205        file_header = &firmware_header->file_header;
 206        binaries = &firmware_header->binary_header;
 207        strncpy(FW_rel_ver_name, file_header->version, min(sizeof(FW_rel_ver_name), sizeof(file_header->version)) - 1);
 208        valid_firmware = sh_css_check_firmware_version(fw_data);
 209        if (!valid_firmware) {
 210#if !defined(HRT_RTL)
 211                IA_CSS_ERROR("CSS code version (%s) and firmware version (%s) mismatch!",
 212                                file_header->version, release_version);
 213                return IA_CSS_ERR_VERSION_MISMATCH;
 214#endif
 215        } else {
 216                IA_CSS_LOG("successfully load firmware version %s", release_version);
 217        }
 218
 219        /* some sanity checks */
 220        if (!fw_data || fw_size < sizeof(struct sh_css_fw_bi_file_h))
 221                return IA_CSS_ERR_INTERNAL_ERROR;
 222
 223        if (file_header->h_size != sizeof(struct sh_css_fw_bi_file_h))
 224                return IA_CSS_ERR_INTERNAL_ERROR;
 225
 226        sh_css_num_binaries = file_header->binary_nr;
 227        /* Only allocate memory for ISP blob info */
 228        if (sh_css_num_binaries > NUM_OF_SPS) {
 229                sh_css_blob_info = kmalloc(
 230                                        (sh_css_num_binaries - NUM_OF_SPS) *
 231                                        sizeof(*sh_css_blob_info), GFP_KERNEL);
 232                if (sh_css_blob_info == NULL)
 233                        return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
 234        } else {
 235                sh_css_blob_info = NULL;
 236        }
 237
 238        fw_minibuffer = kzalloc(sh_css_num_binaries * sizeof(struct fw_param), GFP_KERNEL);
 239        if (fw_minibuffer == NULL)
 240                return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
 241
 242        for (i = 0; i < sh_css_num_binaries; i++) {
 243                struct ia_css_fw_info *bi = &binaries[i];
 244                /* note: the var below is made static as it is quite large;
 245                   if it is not static it ends up on the stack which could
 246                   cause issues for drivers
 247                */
 248                static struct ia_css_blob_descr bd;
 249                enum ia_css_err err;
 250
 251                err = sh_css_load_blob_info(fw_data, bi, &bd, i);
 252
 253                if (err != IA_CSS_SUCCESS)
 254                        return IA_CSS_ERR_INTERNAL_ERROR;
 255
 256                if (bi->blob.offset + bi->blob.size > fw_size)
 257                        return IA_CSS_ERR_INTERNAL_ERROR;
 258
 259                if (bi->type == ia_css_sp_firmware) {
 260                        if (i != SP_FIRMWARE)
 261                                return IA_CSS_ERR_INTERNAL_ERROR;
 262                        err = setup_binary(bi, fw_data, &sh_css_sp_fw, i);
 263                        if (err != IA_CSS_SUCCESS)
 264                                return err;
 265                } else {
 266                        /* All subsequent binaries (including bootloaders) (i>NUM_OF_SPS) are ISP firmware */
 267                        if (i < NUM_OF_SPS)
 268                                return IA_CSS_ERR_INTERNAL_ERROR;
 269
 270                        if (bi->type != ia_css_isp_firmware)
 271                                return IA_CSS_ERR_INTERNAL_ERROR;
 272                        if (sh_css_blob_info == NULL) /* cannot happen but KW does not see this */
 273                                return IA_CSS_ERR_INTERNAL_ERROR;
 274                        sh_css_blob_info[i - NUM_OF_SPS] = bd;
 275                }
 276        }
 277
 278        return IA_CSS_SUCCESS;
 279}
 280
 281void sh_css_unload_firmware(void)
 282{
 283
 284        /* release firmware minibuffer */
 285        if (fw_minibuffer) {
 286                unsigned int i = 0;
 287                for (i = 0; i < sh_css_num_binaries; i++) {
 288                        if (fw_minibuffer[i].name)
 289                                kfree((void *)fw_minibuffer[i].name);
 290                        if (fw_minibuffer[i].buffer)
 291                                vfree((void *)fw_minibuffer[i].buffer);
 292                }
 293                kfree(fw_minibuffer);
 294                fw_minibuffer = NULL;
 295        }
 296
 297        memset(&sh_css_sp_fw, 0, sizeof(sh_css_sp_fw));
 298        if (sh_css_blob_info) {
 299                kfree(sh_css_blob_info);
 300                sh_css_blob_info = NULL;
 301        }
 302        sh_css_num_binaries = 0;
 303}
 304
 305hrt_vaddress
 306sh_css_load_blob(const unsigned char *blob, unsigned size)
 307{
 308        hrt_vaddress target_addr = mmgr_malloc(size);
 309        /* this will allocate memory aligned to a DDR word boundary which
 310           is required for the CSS DMA to read the instructions. */
 311
 312        assert(blob != NULL);
 313        if (target_addr) 
 314                mmgr_store(target_addr, blob, size);
 315        return target_addr;
 316}
 317