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