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 /* 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 */ 59 static const char *isp2400_release_version = STR(irci_stable_candrpv_0415_20150521_0458); 60 static const char *isp2401_release_version = STR(irci_ecr - master_20150911_0724); 61 62 #define MAX_FW_REL_VER_NAME 300 63 static char FW_rel_ver_name[MAX_FW_REL_VER_NAME] = "---"; 64 65 struct ia_css_fw_info sh_css_sp_fw; 66 struct ia_css_blob_descr *sh_css_blob_info; /* Only ISP blob info (no SP) */ 67 unsigned int sh_css_num_binaries; /* This includes 1 SP binary */ 68 69 static struct fw_param *fw_minibuffer; 70 71 char *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 */ 81 static int 82 setup_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 105 int 106 sh_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 188 bool 189 sh_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 0; 211 } 212 213 static 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 220 static 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 227 int 228 sh_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 359 void 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 vfree((void *)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 381 ia_css_ptr 382 sh_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