linux/drivers/staging/media/atomisp/pci/sh_css_firmware.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Support for Intel Camera Imaging ISP subsystem.
   4 * Copyright (c) 2015, Intel Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms and conditions of the GNU General Public License,
   8 * version 2, as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 */
  15
  16#include <linux/string.h> /* for memcpy() */
  17#include <linux/slab.h>
  18#include <linux/vmalloc.h>
  19
  20#include "hmm.h"
  21
  22#include <math_support.h>
  23#include "platform_support.h"
  24#include "sh_css_firmware.h"
  25
  26#include "sh_css_defs.h"
  27#include "ia_css_debug.h"
  28#include "sh_css_internal.h"
  29#include "ia_css_isp_param.h"
  30
  31#include "assert_support.h"
  32
  33#include "isp.h"                                /* PMEM_WIDTH_LOG2 */
  34
  35#include "ia_css_isp_params.h"
  36#include "ia_css_isp_configs.h"
  37#include "ia_css_isp_states.h"
  38
  39#define _STR(x) #x
  40#define STR(x) _STR(x)
  41
  42struct firmware_header {
  43        struct sh_css_fw_bi_file_h file_header;
  44        struct ia_css_fw_info      binary_header;
  45};
  46
  47struct fw_param {
  48        const char *name;
  49        const void *buffer;
  50};
  51
  52static struct firmware_header *firmware_header;
  53
  54/*
  55 * The string STR is a place holder
  56 * which will be replaced with the actual RELEASE_VERSION
  57 * during package generation. Please do not modify
  58 */
  59static const char *isp2400_release_version = STR(irci_stable_candrpv_0415_20150521_0458);
  60static const char *isp2401_release_version = STR(irci_ecr - master_20150911_0724);
  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 int sh_css_num_binaries; /* This includes 1 SP binary */
  68
  69static struct fw_param *fw_minibuffer;
  70
  71char *sh_css_get_fw_version(void)
  72{
  73        return FW_rel_ver_name;
  74}
  75
  76/*
  77 * Split the loaded firmware into blobs
  78 */
  79
  80/* Setup sp/sp1 binary */
  81static int
  82setup_binary(struct ia_css_fw_info *fw, const char *fw_data,
  83             struct ia_css_fw_info *sh_css_fw, unsigned int binary_id)
  84{
  85        const char *blob_data;
  86
  87        if ((!fw) || (!fw_data))
  88                return -EINVAL;
  89
  90        blob_data = fw_data + fw->blob.offset;
  91
  92        *sh_css_fw = *fw;
  93
  94        sh_css_fw->blob.code = vmalloc(fw->blob.size);
  95        if (!sh_css_fw->blob.code)
  96                return -ENOMEM;
  97
  98        memcpy((void *)sh_css_fw->blob.code, blob_data, fw->blob.size);
  99        sh_css_fw->blob.data = (char *)sh_css_fw->blob.code + fw->blob.data_source;
 100        fw_minibuffer[binary_id].buffer = sh_css_fw->blob.code;
 101
 102        return 0;
 103}
 104
 105int
 106sh_css_load_blob_info(const char *fw, const struct ia_css_fw_info *bi,
 107                      struct ia_css_blob_descr *bd,
 108                      unsigned int index)
 109{
 110        const char *name;
 111        const unsigned char *blob;
 112
 113        if ((!fw) || (!bd))
 114                return -EINVAL;
 115
 116        /* Special case: only one binary in fw */
 117        if (!bi)
 118                bi = (const struct ia_css_fw_info *)fw;
 119
 120        name = fw + bi->blob.prog_name_offset;
 121        blob = (const unsigned char *)fw + bi->blob.offset;
 122
 123        /* sanity check */
 124        if (bi->blob.size !=
 125                bi->blob.text_size + bi->blob.icache_size +
 126                        bi->blob.data_size + bi->blob.padding_size) {
 127                /* sanity check, note the padding bytes added for section to DDR alignment */
 128                return -EINVAL;
 129        }
 130
 131        if ((bi->blob.offset % (1UL << (ISP_PMEM_WIDTH_LOG2 - 3))) != 0)
 132                return -EINVAL;
 133
 134        bd->blob = blob;
 135        bd->header = *bi;
 136
 137        if (bi->type == ia_css_isp_firmware || bi->type == ia_css_sp_firmware) {
 138                char *namebuffer;
 139
 140                namebuffer = kstrdup(name, GFP_KERNEL);
 141                if (!namebuffer)
 142                        return -ENOMEM;
 143                bd->name = fw_minibuffer[index].name = namebuffer;
 144        } else {
 145                bd->name = name;
 146        }
 147
 148        if (bi->type == ia_css_isp_firmware) {
 149                size_t paramstruct_size = sizeof(struct ia_css_memory_offsets);
 150                size_t configstruct_size = sizeof(struct ia_css_config_memory_offsets);
 151                size_t statestruct_size = sizeof(struct ia_css_state_memory_offsets);
 152
 153                char *parambuf = kmalloc(paramstruct_size + configstruct_size +
 154                                         statestruct_size,
 155                                         GFP_KERNEL);
 156                if (!parambuf)
 157                        return -ENOMEM;
 158
 159                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = NULL;
 160                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = NULL;
 161                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = NULL;
 162
 163                fw_minibuffer[index].buffer = parambuf;
 164
 165                /* copy ia_css_memory_offsets */
 166                memcpy(parambuf, (void *)(fw +
 167                                          bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_PARAM]),
 168                       paramstruct_size);
 169                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = parambuf;
 170
 171                /* copy ia_css_config_memory_offsets */
 172                memcpy(parambuf + paramstruct_size,
 173                       (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_CONFIG]),
 174                       configstruct_size);
 175                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = parambuf +
 176                paramstruct_size;
 177
 178                /* copy ia_css_state_memory_offsets */
 179                memcpy(parambuf + paramstruct_size + configstruct_size,
 180                       (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_STATE]),
 181                       statestruct_size);
 182                bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = parambuf +
 183                paramstruct_size + configstruct_size;
 184        }
 185        return 0;
 186}
 187
 188bool
 189sh_css_check_firmware_version(struct device *dev, const char *fw_data)
 190{
 191        struct sh_css_fw_bi_file_h *file_header;
 192
 193        const char *release_version;
 194
 195        if (!IS_ISP2401)
 196                release_version = isp2400_release_version;
 197        else
 198                release_version = isp2401_release_version;
 199
 200        firmware_header = (struct firmware_header *)fw_data;
 201        file_header = &firmware_header->file_header;
 202
 203        if (strcmp(file_header->version, release_version) != 0) {
 204                dev_err(dev, "Firmware version may not be compatible with this driver\n");
 205                dev_err(dev, "Expecting version '%s', but firmware is '%s'.\n",
 206                        release_version, file_header->version);
 207        }
 208
 209        /* For now, let's just accept a wrong version, even if wrong */
 210        return false;
 211}
 212
 213static const char * const fw_type_name[] = {
 214        [ia_css_sp_firmware]            = "SP",
 215        [ia_css_isp_firmware]           = "ISP",
 216        [ia_css_bootloader_firmware]    = "BootLoader",
 217        [ia_css_acc_firmware]           = "accel",
 218};
 219
 220static const char * const fw_acc_type_name[] = {
 221        [IA_CSS_ACC_NONE] =             "Normal",
 222        [IA_CSS_ACC_OUTPUT] =           "Accel for output",
 223        [IA_CSS_ACC_VIEWFINDER] =       "Accel for viewfinder",
 224        [IA_CSS_ACC_STANDALONE] =       "Stand-alone accel",
 225};
 226
 227int
 228sh_css_load_firmware(struct device *dev, const char *fw_data,
 229                     unsigned int fw_size)
 230{
 231        unsigned int i;
 232        struct ia_css_fw_info *binaries;
 233        struct sh_css_fw_bi_file_h *file_header;
 234        int ret;
 235        const char *release_version;
 236
 237        if (!IS_ISP2401)
 238                release_version = isp2400_release_version;
 239        else
 240                release_version = isp2401_release_version;
 241
 242        firmware_header = (struct firmware_header *)fw_data;
 243        file_header = &firmware_header->file_header;
 244        binaries = &firmware_header->binary_header;
 245        strscpy(FW_rel_ver_name, file_header->version,
 246                min(sizeof(FW_rel_ver_name), sizeof(file_header->version)));
 247        ret = sh_css_check_firmware_version(dev, fw_data);
 248        if (ret) {
 249                IA_CSS_ERROR("CSS code version (%s) and firmware version (%s) mismatch!",
 250                             file_header->version, release_version);
 251                return -EINVAL;
 252        } else {
 253                IA_CSS_LOG("successfully load firmware version %s", release_version);
 254        }
 255
 256        /* some sanity checks */
 257        if (!fw_data || fw_size < sizeof(struct sh_css_fw_bi_file_h))
 258                return -EINVAL;
 259
 260        if (file_header->h_size != sizeof(struct sh_css_fw_bi_file_h))
 261                return -EINVAL;
 262
 263        sh_css_num_binaries = file_header->binary_nr;
 264        /* Only allocate memory for ISP blob info */
 265        if (sh_css_num_binaries > NUM_OF_SPS) {
 266                sh_css_blob_info = kmalloc(
 267                    (sh_css_num_binaries - NUM_OF_SPS) *
 268                    sizeof(*sh_css_blob_info), GFP_KERNEL);
 269                if (!sh_css_blob_info)
 270                        return -ENOMEM;
 271        } else {
 272                sh_css_blob_info = NULL;
 273        }
 274
 275        fw_minibuffer = kcalloc(sh_css_num_binaries, sizeof(struct fw_param),
 276                                GFP_KERNEL);
 277        if (!fw_minibuffer)
 278                return -ENOMEM;
 279
 280        for (i = 0; i < sh_css_num_binaries; i++) {
 281                struct ia_css_fw_info *bi = &binaries[i];
 282                /*
 283                 * note: the var below is made static as it is quite large;
 284                 * if it is not static it ends up on the stack which could
 285                 * cause issues for drivers
 286                 */
 287                static struct ia_css_blob_descr bd;
 288                int err;
 289
 290                err = sh_css_load_blob_info(fw_data, bi, &bd, i);
 291
 292                if (err)
 293                        return -EINVAL;
 294
 295                if (bi->blob.offset + bi->blob.size > fw_size)
 296                        return -EINVAL;
 297
 298                switch (bd.header.type) {
 299                case ia_css_isp_firmware:
 300                        if (bd.header.info.isp.type > IA_CSS_ACC_STANDALONE) {
 301                                dev_err(dev, "binary #%2d: invalid SP type\n",
 302                                        i);
 303                                return -EINVAL;
 304                        }
 305
 306                        dev_dbg(dev,
 307                                "binary #%-2d type %s (%s), binary id is %2d: %s\n",
 308                                i,
 309                                fw_type_name[bd.header.type],
 310                                fw_acc_type_name[bd.header.info.isp.type],
 311                                bd.header.info.isp.sp.id,
 312                                bd.name);
 313                        break;
 314                case ia_css_sp_firmware:
 315                case ia_css_bootloader_firmware:
 316                case ia_css_acc_firmware:
 317                        dev_dbg(dev,
 318                                "binary #%-2d type %s: %s\n",
 319                                i, fw_type_name[bd.header.type],
 320                                bd.name);
 321                        break;
 322                default:
 323                        if (bd.header.info.isp.type > IA_CSS_ACC_STANDALONE) {
 324                                dev_err(dev,
 325                                        "binary #%2d: invalid firmware type\n",
 326                                        i);
 327                                return -EINVAL;
 328                        }
 329                        break;
 330                }
 331
 332                if (bi->type == ia_css_sp_firmware) {
 333                        if (i != SP_FIRMWARE)
 334                                return -EINVAL;
 335                        err = setup_binary(bi, fw_data, &sh_css_sp_fw, i);
 336                        if (err)
 337                                return err;
 338
 339                } else {
 340                        /*
 341                         * All subsequent binaries
 342                         * (including bootloaders) (i>NUM_OF_SPS)
 343                         * are ISP firmware
 344                         */
 345                        if (i < NUM_OF_SPS)
 346                                return -EINVAL;
 347
 348                        if (bi->type != ia_css_isp_firmware)
 349                                return -EINVAL;
 350                        if (!sh_css_blob_info) /* cannot happen but KW does not see this */
 351                                return -EINVAL;
 352                        sh_css_blob_info[i - NUM_OF_SPS] = bd;
 353                }
 354        }
 355
 356        return 0;
 357}
 358
 359void sh_css_unload_firmware(void)
 360{
 361        /* release firmware minibuffer */
 362        if (fw_minibuffer) {
 363                unsigned int i = 0;
 364
 365                for (i = 0; i < sh_css_num_binaries; i++) {
 366                        if (fw_minibuffer[i].name)
 367                                kfree((void *)fw_minibuffer[i].name);
 368                        if (fw_minibuffer[i].buffer)
 369                                kvfree(fw_minibuffer[i].buffer);
 370                }
 371                kfree(fw_minibuffer);
 372                fw_minibuffer = NULL;
 373        }
 374
 375        memset(&sh_css_sp_fw, 0, sizeof(sh_css_sp_fw));
 376        kfree(sh_css_blob_info);
 377        sh_css_blob_info = NULL;
 378        sh_css_num_binaries = 0;
 379}
 380
 381ia_css_ptr
 382sh_css_load_blob(const unsigned char *blob, unsigned int size)
 383{
 384        ia_css_ptr target_addr = hmm_alloc(size, HMM_BO_PRIVATE, 0, NULL, 0);
 385        /*
 386         * this will allocate memory aligned to a DDR word boundary which
 387         * is required for the CSS DMA to read the instructions.
 388         */
 389
 390        assert(blob);
 391        if (target_addr)
 392                hmm_store(target_addr, blob, size);
 393        return target_addr;
 394}
 395