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