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 "hmm.h" 19 20 #include <math_support.h> 21 #include "platform_support.h" 22 #include "sh_css_firmware.h" 23 24 #include "sh_css_defs.h" 25 #include "ia_css_debug.h" 26 #include "sh_css_internal.h" 27 #include "ia_css_isp_param.h" 28 29 #include "assert_support.h" 30 #include "string_support.h" 31 32 #include "isp.h" /* PMEM_WIDTH_LOG2 */ 33 34 #include "ia_css_isp_params.h" 35 #include "ia_css_isp_configs.h" 36 #include "ia_css_isp_states.h" 37 38 #define _STR(x) #x 39 #define STR(x) _STR(x) 40 41 struct firmware_header { 42 struct sh_css_fw_bi_file_h file_header; 43 struct ia_css_fw_info binary_header; 44 }; 45 46 struct fw_param { 47 const char *name; 48 const void *buffer; 49 }; 50 51 static 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 static const char *isp2400_release_version = STR(irci_stable_candrpv_0415_20150521_0458); 57 static const char *isp2401_release_version = STR(irci_ecr - master_20150911_0724); 58 59 #define MAX_FW_REL_VER_NAME 300 60 static char FW_rel_ver_name[MAX_FW_REL_VER_NAME] = "---"; 61 62 struct ia_css_fw_info sh_css_sp_fw; 63 struct ia_css_blob_descr *sh_css_blob_info; /* Only ISP blob info (no SP) */ 64 unsigned int sh_css_num_binaries; /* This includes 1 SP binary */ 65 66 static struct fw_param *fw_minibuffer; 67 68 char *sh_css_get_fw_version(void) 69 { 70 return FW_rel_ver_name; 71 } 72 73 /* 74 * Split the loaded firmware into blobs 75 */ 76 77 /* Setup sp/sp1 binary */ 78 static enum ia_css_err 79 setup_binary(struct ia_css_fw_info *fw, const char *fw_data, 80 struct ia_css_fw_info *sh_css_fw, unsigned int binary_id) { 81 const char *blob_data; 82 83 if ((!fw) || (!fw_data)) 84 return IA_CSS_ERR_INVALID_ARGUMENTS; 85 86 blob_data = fw_data + fw->blob.offset; 87 88 *sh_css_fw = *fw; 89 90 sh_css_fw->blob.code = vmalloc(fw->blob.size); 91 if (!sh_css_fw->blob.code) 92 return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY; 93 94 memcpy((void *)sh_css_fw->blob.code, blob_data, fw->blob.size); 95 sh_css_fw->blob.data = (char *)sh_css_fw->blob.code + fw->blob.data_source; 96 fw_minibuffer[binary_id].buffer = sh_css_fw->blob.code; 97 98 return IA_CSS_SUCCESS; 99 } 100 101 enum ia_css_err 102 sh_css_load_blob_info(const char *fw, const struct ia_css_fw_info *bi, 103 struct ia_css_blob_descr *bd, 104 unsigned int index) { 105 const char *name; 106 const unsigned char *blob; 107 108 if ((!fw) || (!bd)) 109 return IA_CSS_ERR_INVALID_ARGUMENTS; 110 111 /* Special case: only one binary in fw */ 112 if (!bi) bi = (const struct ia_css_fw_info *)fw; 113 114 name = fw + bi->blob.prog_name_offset; 115 blob = (const unsigned char *)fw + bi->blob.offset; 116 117 /* sanity check */ 118 if (bi->blob.size != bi->blob.text_size + bi->blob.icache_size + bi->blob.data_size + bi->blob.padding_size) 119 { 120 /* sanity check, note the padding bytes added for section to DDR alignment */ 121 return IA_CSS_ERR_INVALID_ARGUMENTS; 122 } 123 124 if ((bi->blob.offset % (1UL << (ISP_PMEM_WIDTH_LOG2 - 3))) != 0) 125 return IA_CSS_ERR_INVALID_ARGUMENTS; 126 127 bd->blob = blob; 128 bd->header = *bi; 129 130 if (bi->type == ia_css_isp_firmware || bi->type == ia_css_sp_firmware) 131 { 132 char *namebuffer; 133 134 namebuffer = kstrdup(name, GFP_KERNEL); 135 if (!namebuffer) 136 return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY; 137 bd->name = fw_minibuffer[index].name = namebuffer; 138 } else 139 { 140 bd->name = name; 141 } 142 143 if (bi->type == ia_css_isp_firmware) 144 { 145 size_t paramstruct_size = sizeof(struct ia_css_memory_offsets); 146 size_t configstruct_size = sizeof(struct ia_css_config_memory_offsets); 147 size_t statestruct_size = sizeof(struct ia_css_state_memory_offsets); 148 149 char *parambuf = kmalloc(paramstruct_size + configstruct_size + 150 statestruct_size, 151 GFP_KERNEL); 152 if (!parambuf) 153 return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY; 154 155 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = NULL; 156 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = NULL; 157 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = NULL; 158 159 fw_minibuffer[index].buffer = parambuf; 160 161 /* copy ia_css_memory_offsets */ 162 memcpy(parambuf, (void *)(fw + 163 bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_PARAM]), 164 paramstruct_size); 165 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = parambuf; 166 167 /* copy ia_css_config_memory_offsets */ 168 memcpy(parambuf + paramstruct_size, 169 (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_CONFIG]), 170 configstruct_size); 171 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = parambuf + 172 paramstruct_size; 173 174 /* copy ia_css_state_memory_offsets */ 175 memcpy(parambuf + paramstruct_size + configstruct_size, 176 (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_STATE]), 177 statestruct_size); 178 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = parambuf + 179 paramstruct_size + configstruct_size; 180 } 181 return IA_CSS_SUCCESS; 182 } 183 184 bool 185 sh_css_check_firmware_version(struct device *dev, const char *fw_data) 186 { 187 struct sh_css_fw_bi_file_h *file_header; 188 189 const char *release_version; 190 191 if (!atomisp_hw_is_isp2401) 192 release_version = isp2400_release_version; 193 else 194 release_version = isp2401_release_version; 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 true; 207 } 208 209 enum ia_css_err 210 sh_css_load_firmware(struct device *dev, const char *fw_data, 211 unsigned int fw_size) { 212 unsigned int i; 213 struct ia_css_fw_info *binaries; 214 struct sh_css_fw_bi_file_h *file_header; 215 bool valid_firmware = false; 216 const char *release_version; 217 218 if (!atomisp_hw_is_isp2401) 219 release_version = isp2400_release_version; 220 else 221 release_version = isp2401_release_version; 222 223 firmware_header = (struct firmware_header *)fw_data; 224 file_header = &firmware_header->file_header; 225 binaries = &firmware_header->binary_header; 226 strscpy(FW_rel_ver_name, file_header->version, min(sizeof(FW_rel_ver_name), sizeof(file_header->version))); 227 valid_firmware = sh_css_check_firmware_version(dev, fw_data); 228 if (!valid_firmware) { 229 IA_CSS_ERROR("CSS code version (%s) and firmware version (%s) mismatch!", 230 file_header->version, release_version); 231 return IA_CSS_ERR_VERSION_MISMATCH; 232 } else { 233 IA_CSS_LOG("successfully load firmware version %s", release_version); 234 } 235 236 /* some sanity checks */ 237 if (!fw_data || fw_size < sizeof(struct sh_css_fw_bi_file_h)) 238 return IA_CSS_ERR_INTERNAL_ERROR; 239 240 if (file_header->h_size != sizeof(struct sh_css_fw_bi_file_h)) 241 return IA_CSS_ERR_INTERNAL_ERROR; 242 243 sh_css_num_binaries = file_header->binary_nr; 244 /* Only allocate memory for ISP blob info */ 245 if (sh_css_num_binaries > NUM_OF_SPS) 246 { 247 sh_css_blob_info = kmalloc( 248 (sh_css_num_binaries - NUM_OF_SPS) * 249 sizeof(*sh_css_blob_info), GFP_KERNEL); 250 if (!sh_css_blob_info) 251 return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY; 252 } else { 253 sh_css_blob_info = NULL; 254 } 255 256 fw_minibuffer = kcalloc(sh_css_num_binaries, sizeof(struct fw_param), 257 GFP_KERNEL); 258 if (!fw_minibuffer) 259 return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY; 260 261 for (i = 0; i < sh_css_num_binaries; i++) 262 { 263 struct ia_css_fw_info *bi = &binaries[i]; 264 /* note: the var below is made static as it is quite large; 265 if it is not static it ends up on the stack which could 266 cause issues for drivers 267 */ 268 static struct ia_css_blob_descr bd; 269 enum ia_css_err err; 270 271 err = sh_css_load_blob_info(fw_data, bi, &bd, i); 272 273 if (err != IA_CSS_SUCCESS) 274 return IA_CSS_ERR_INTERNAL_ERROR; 275 276 if (bi->blob.offset + bi->blob.size > fw_size) 277 return IA_CSS_ERR_INTERNAL_ERROR; 278 279 if (bi->type == ia_css_sp_firmware) { 280 if (i != SP_FIRMWARE) 281 return IA_CSS_ERR_INTERNAL_ERROR; 282 err = setup_binary(bi, fw_data, &sh_css_sp_fw, i); 283 if (err != IA_CSS_SUCCESS) 284 return err; 285 } else { 286 /* All subsequent binaries (including bootloaders) (i>NUM_OF_SPS) are ISP firmware */ 287 if (i < NUM_OF_SPS) 288 return IA_CSS_ERR_INTERNAL_ERROR; 289 290 if (bi->type != ia_css_isp_firmware) 291 return IA_CSS_ERR_INTERNAL_ERROR; 292 if (!sh_css_blob_info) /* cannot happen but KW does not see this */ 293 return IA_CSS_ERR_INTERNAL_ERROR; 294 sh_css_blob_info[i - NUM_OF_SPS] = bd; 295 } 296 } 297 298 return IA_CSS_SUCCESS; 299 } 300 301 void sh_css_unload_firmware(void) 302 { 303 /* release firmware minibuffer */ 304 if (fw_minibuffer) { 305 unsigned int i = 0; 306 307 for (i = 0; i < sh_css_num_binaries; i++) { 308 if (fw_minibuffer[i].name) 309 kfree((void *)fw_minibuffer[i].name); 310 if (fw_minibuffer[i].buffer) 311 vfree((void *)fw_minibuffer[i].buffer); 312 } 313 kfree(fw_minibuffer); 314 fw_minibuffer = NULL; 315 } 316 317 memset(&sh_css_sp_fw, 0, sizeof(sh_css_sp_fw)); 318 kfree(sh_css_blob_info); 319 sh_css_blob_info = NULL; 320 sh_css_num_binaries = 0; 321 } 322 323 ia_css_ptr 324 sh_css_load_blob(const unsigned char *blob, unsigned int size) 325 { 326 ia_css_ptr target_addr = hmm_alloc(size, HMM_BO_PRIVATE, 0, NULL, 0); 327 /* this will allocate memory aligned to a DDR word boundary which 328 is required for the CSS DMA to read the instructions. */ 329 330 assert(blob); 331 if (target_addr) 332 hmm_store(target_addr, blob, size); 333 return target_addr; 334 } 335