xref: /linux/drivers/staging/media/atomisp/pci/sh_css_firmware.c (revision 32a92f8c89326985e05dce8b22d3f0aa07a3e1bd)
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 = kzalloc_objs(struct fw_param, sh_css_num_binaries);
266 	if (!fw_minibuffer)
267 		return -ENOMEM;
268 
269 	for (i = 0; i < sh_css_num_binaries; i++) {
270 		struct ia_css_fw_info *bi = &binaries[i];
271 		/*
272 		 * note: the var below is made static as it is quite large;
273 		 * if it is not static it ends up on the stack which could
274 		 * cause issues for drivers
275 		 */
276 		static struct ia_css_blob_descr bd;
277 		int err;
278 
279 		err = sh_css_load_blob_info(fw_data, bi, &bd, i);
280 
281 		if (err)
282 			return -EINVAL;
283 
284 		if (bi->blob.offset + bi->blob.size > fw_size)
285 			return -EINVAL;
286 
287 		switch (bd.header.type) {
288 		case ia_css_isp_firmware:
289 			if (bd.header.info.isp.type > IA_CSS_ACC_STANDALONE) {
290 				dev_err(dev, "binary #%2d: invalid SP type\n",
291 					i);
292 				return -EINVAL;
293 			}
294 
295 			dev_dbg(dev,
296 				"binary #%-2d type %s (%s), binary id is %2d: %s\n",
297 				i,
298 				fw_type_name[bd.header.type],
299 				fw_acc_type_name[bd.header.info.isp.type],
300 				bd.header.info.isp.sp.id,
301 				bd.name);
302 			break;
303 		case ia_css_sp_firmware:
304 		case ia_css_bootloader_firmware:
305 		case ia_css_acc_firmware:
306 			dev_dbg(dev,
307 				"binary #%-2d type %s: %s\n",
308 				i, fw_type_name[bd.header.type],
309 				bd.name);
310 			break;
311 		default:
312 			if (bd.header.info.isp.type > IA_CSS_ACC_STANDALONE) {
313 				dev_err(dev,
314 					"binary #%2d: invalid firmware type\n",
315 					i);
316 				return -EINVAL;
317 			}
318 			break;
319 		}
320 
321 		if (bi->type == ia_css_sp_firmware) {
322 			if (i != SP_FIRMWARE)
323 				return -EINVAL;
324 			err = setup_binary(bi, fw_data, &sh_css_sp_fw, i);
325 			if (err)
326 				return err;
327 
328 		} else {
329 			/*
330 			 * All subsequent binaries
331 			 * (including bootloaders) (i>NUM_OF_SPS)
332 			 * are ISP firmware
333 			 */
334 			if (i < NUM_OF_SPS)
335 				return -EINVAL;
336 
337 			if (bi->type != ia_css_isp_firmware)
338 				return -EINVAL;
339 			if (!sh_css_blob_info) /* cannot happen but KW does not see this */
340 				return -EINVAL;
341 			sh_css_blob_info[i - NUM_OF_SPS] = bd;
342 		}
343 	}
344 
345 	return 0;
346 }
347 
sh_css_unload_firmware(void)348 void sh_css_unload_firmware(void)
349 {
350 	/* release firmware minibuffer */
351 	if (fw_minibuffer) {
352 		unsigned int i = 0;
353 
354 		for (i = 0; i < sh_css_num_binaries; i++) {
355 			kfree(fw_minibuffer[i].name);
356 			kvfree(fw_minibuffer[i].buffer);
357 		}
358 		kfree(fw_minibuffer);
359 		fw_minibuffer = NULL;
360 	}
361 
362 	memset(&sh_css_sp_fw, 0, sizeof(sh_css_sp_fw));
363 	kfree(sh_css_blob_info);
364 	sh_css_blob_info = NULL;
365 	sh_css_num_binaries = 0;
366 }
367 
368 ia_css_ptr
sh_css_load_blob(const unsigned char * blob,unsigned int size)369 sh_css_load_blob(const unsigned char *blob, unsigned int size)
370 {
371 	ia_css_ptr target_addr = hmm_alloc(size);
372 	/*
373 	 * this will allocate memory aligned to a DDR word boundary which
374 	 * is required for the CSS DMA to read the instructions.
375 	 */
376 
377 	assert(blob);
378 	if (target_addr)
379 		hmm_store(target_addr, blob, size);
380 	return target_addr;
381 }
382