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
sh_css_get_fw_version(void)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
setup_binary(struct ia_css_fw_info * fw,const char * fw_data,struct ia_css_fw_info * sh_css_fw,unsigned int binary_id)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
sh_css_load_blob_info(const char * fw,const struct ia_css_fw_info * bi,struct ia_css_blob_descr * bd,unsigned int index)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
sh_css_check_firmware_version(struct device * dev,const char * fw_data)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 false;
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
sh_css_load_firmware(struct device * dev,const char * fw_data,unsigned int fw_size)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
sh_css_unload_firmware(void)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 kfree(fw_minibuffer[i].name);
367 kvfree(fw_minibuffer[i].buffer);
368 }
369 kfree(fw_minibuffer);
370 fw_minibuffer = NULL;
371 }
372
373 memset(&sh_css_sp_fw, 0, sizeof(sh_css_sp_fw));
374 kfree(sh_css_blob_info);
375 sh_css_blob_info = NULL;
376 sh_css_num_binaries = 0;
377 }
378
379 ia_css_ptr
sh_css_load_blob(const unsigned char * blob,unsigned int size)380 sh_css_load_blob(const unsigned char *blob, unsigned int size)
381 {
382 ia_css_ptr target_addr = hmm_alloc(size, HMM_BO_PRIVATE, 0, NULL, 0);
383 /*
384 * this will allocate memory aligned to a DDR word boundary which
385 * is required for the CSS DMA to read the instructions.
386 */
387
388 assert(blob);
389 if (target_addr)
390 hmm_store(target_addr, blob, size);
391 return target_addr;
392 }
393