1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Support for Intel Camera Imaging ISP subsystem.
4 * Copyright (c) 2015, Intel Corporation.
5 */
6
7 #include <linux/string.h> /* for memcpy() */
8 #include <linux/slab.h>
9 #include <linux/vmalloc.h>
10
11 #include "hmm.h"
12
13 #include <math_support.h>
14 #include "platform_support.h"
15 #include "sh_css_firmware.h"
16
17 #include "sh_css_defs.h"
18 #include "ia_css_debug.h"
19 #include "sh_css_internal.h"
20 #include "ia_css_isp_param.h"
21
22 #include "assert_support.h"
23
24 #include "isp.h" /* PMEM_WIDTH_LOG2 */
25
26 #include "ia_css_isp_params.h"
27 #include "ia_css_isp_configs.h"
28 #include "ia_css_isp_states.h"
29
30 #define _STR(x) #x
31 #define STR(x) _STR(x)
32
33 struct firmware_header {
34 struct sh_css_fw_bi_file_h file_header;
35 struct ia_css_fw_info binary_header;
36 };
37
38 struct fw_param {
39 const char *name;
40 const void *buffer;
41 };
42
43 static struct firmware_header *firmware_header;
44
45 /*
46 * The string STR is a place holder
47 * which will be replaced with the actual RELEASE_VERSION
48 * during package generation. Please do not modify
49 */
50 static const char *release_version_2401 = STR(irci_stable_candrpv_0415_20150521_0458);
51 static const char *release_version_2400 = STR(irci_stable_candrpv_0415_20150423_1753);
52
53 #define MAX_FW_REL_VER_NAME 300
54 static char FW_rel_ver_name[MAX_FW_REL_VER_NAME] = "---";
55
56 struct ia_css_fw_info sh_css_sp_fw;
57 struct ia_css_blob_descr *sh_css_blob_info; /* Only ISP blob info (no SP) */
58 unsigned int sh_css_num_binaries; /* This includes 1 SP binary */
59
60 static struct fw_param *fw_minibuffer;
61
sh_css_get_fw_version(void)62 char *sh_css_get_fw_version(void)
63 {
64 return FW_rel_ver_name;
65 }
66
67 /*
68 * Split the loaded firmware into blobs
69 */
70
71 /* Setup sp/sp1 binary */
72 static int
setup_binary(struct ia_css_fw_info * fw,const char * fw_data,struct ia_css_fw_info * sh_css_fw,unsigned int binary_id)73 setup_binary(struct ia_css_fw_info *fw, const char *fw_data,
74 struct ia_css_fw_info *sh_css_fw, unsigned int binary_id)
75 {
76 const char *blob_data;
77
78 if ((!fw) || (!fw_data))
79 return -EINVAL;
80
81 blob_data = fw_data + fw->blob.offset;
82
83 *sh_css_fw = *fw;
84
85 sh_css_fw->blob.code = vmalloc(fw->blob.size);
86 if (!sh_css_fw->blob.code)
87 return -ENOMEM;
88
89 memcpy((void *)sh_css_fw->blob.code, blob_data, fw->blob.size);
90 sh_css_fw->blob.data = (char *)sh_css_fw->blob.code + fw->blob.data_source;
91 fw_minibuffer[binary_id].buffer = sh_css_fw->blob.code;
92
93 return 0;
94 }
95
96 int
sh_css_load_blob_info(const char * fw,const struct ia_css_fw_info * bi,struct ia_css_blob_descr * bd,unsigned int index)97 sh_css_load_blob_info(const char *fw, const struct ia_css_fw_info *bi,
98 struct ia_css_blob_descr *bd,
99 unsigned int index)
100 {
101 const char *name;
102 const unsigned char *blob;
103
104 if ((!fw) || (!bd))
105 return -EINVAL;
106
107 /* Special case: only one binary in fw */
108 if (!bi)
109 bi = (const struct ia_css_fw_info *)fw;
110
111 name = fw + bi->blob.prog_name_offset;
112 blob = (const unsigned char *)fw + bi->blob.offset;
113
114 /* sanity check */
115 if (bi->blob.size !=
116 bi->blob.text_size + bi->blob.icache_size +
117 bi->blob.data_size + bi->blob.padding_size) {
118 /* sanity check, note the padding bytes added for section to DDR alignment */
119 return -EINVAL;
120 }
121
122 if ((bi->blob.offset % (1UL << (ISP_PMEM_WIDTH_LOG2 - 3))) != 0)
123 return -EINVAL;
124
125 bd->blob = blob;
126 bd->header = *bi;
127
128 if (bi->type == ia_css_isp_firmware || bi->type == ia_css_sp_firmware) {
129 char *namebuffer;
130
131 namebuffer = kstrdup(name, GFP_KERNEL);
132 if (!namebuffer)
133 return -ENOMEM;
134 bd->name = fw_minibuffer[index].name = namebuffer;
135 } else {
136 bd->name = name;
137 }
138
139 if (bi->type == ia_css_isp_firmware) {
140 size_t paramstruct_size = sizeof(struct ia_css_memory_offsets);
141 size_t configstruct_size = sizeof(struct ia_css_config_memory_offsets);
142 size_t statestruct_size = sizeof(struct ia_css_state_memory_offsets);
143
144 char *parambuf = kmalloc(paramstruct_size + configstruct_size +
145 statestruct_size,
146 GFP_KERNEL);
147 if (!parambuf)
148 return -ENOMEM;
149
150 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = NULL;
151 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = NULL;
152 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = NULL;
153
154 fw_minibuffer[index].buffer = parambuf;
155
156 /* copy ia_css_memory_offsets */
157 memcpy(parambuf, (void *)(fw +
158 bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_PARAM]),
159 paramstruct_size);
160 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = parambuf;
161
162 /* copy ia_css_config_memory_offsets */
163 memcpy(parambuf + paramstruct_size,
164 (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_CONFIG]),
165 configstruct_size);
166 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = parambuf +
167 paramstruct_size;
168
169 /* copy ia_css_state_memory_offsets */
170 memcpy(parambuf + paramstruct_size + configstruct_size,
171 (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_STATE]),
172 statestruct_size);
173 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = parambuf +
174 paramstruct_size + configstruct_size;
175 }
176 return 0;
177 }
178
179 bool
sh_css_check_firmware_version(struct device * dev,const char * fw_data)180 sh_css_check_firmware_version(struct device *dev, const char *fw_data)
181 {
182 const char *release_version;
183 struct sh_css_fw_bi_file_h *file_header;
184
185 if (IS_ISP2401)
186 release_version = release_version_2401;
187 else
188 release_version = release_version_2400;
189
190 firmware_header = (struct firmware_header *)fw_data;
191 file_header = &firmware_header->file_header;
192
193 if (strcmp(file_header->version, release_version) != 0) {
194 dev_err(dev, "Firmware version may not be compatible with this driver\n");
195 dev_err(dev, "Expecting version '%s', but firmware is '%s'.\n",
196 release_version, file_header->version);
197 }
198
199 /* For now, let's just accept a wrong version, even if wrong */
200 return false;
201 }
202
203 static const char * const fw_type_name[] = {
204 [ia_css_sp_firmware] = "SP",
205 [ia_css_isp_firmware] = "ISP",
206 [ia_css_bootloader_firmware] = "BootLoader",
207 [ia_css_acc_firmware] = "accel",
208 };
209
210 static const char * const fw_acc_type_name[] = {
211 [IA_CSS_ACC_NONE] = "Normal",
212 [IA_CSS_ACC_OUTPUT] = "Accel for output",
213 [IA_CSS_ACC_VIEWFINDER] = "Accel for viewfinder",
214 [IA_CSS_ACC_STANDALONE] = "Stand-alone accel",
215 };
216
217 int
sh_css_load_firmware(struct device * dev,const char * fw_data,unsigned int fw_size)218 sh_css_load_firmware(struct device *dev, const char *fw_data,
219 unsigned int fw_size)
220 {
221 unsigned int i;
222 const char *release_version;
223 struct ia_css_fw_info *binaries;
224 struct sh_css_fw_bi_file_h *file_header;
225 int ret;
226
227 /* some sanity checks */
228 if (!fw_data || fw_size < sizeof(struct sh_css_fw_bi_file_h))
229 return -EINVAL;
230
231 firmware_header = (struct firmware_header *)fw_data;
232 file_header = &firmware_header->file_header;
233
234 if (file_header->h_size != sizeof(struct sh_css_fw_bi_file_h))
235 return -EINVAL;
236
237 binaries = &firmware_header->binary_header;
238 strscpy(FW_rel_ver_name, file_header->version,
239 min(sizeof(FW_rel_ver_name), sizeof(file_header->version)));
240 if (IS_ISP2401)
241 release_version = release_version_2401;
242 else
243 release_version = release_version_2400;
244 ret = sh_css_check_firmware_version(dev, fw_data);
245 if (ret) {
246 IA_CSS_ERROR("CSS code version (%s) and firmware version (%s) mismatch!",
247 file_header->version, release_version);
248 return -EINVAL;
249 } else {
250 IA_CSS_LOG("successfully load firmware version %s", release_version);
251 }
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
sh_css_unload_firmware(void)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
sh_css_load_blob(const unsigned char * blob,unsigned int size)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