xref: /linux/drivers/staging/media/atomisp/pci/sh_css_firmware.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
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 *release_version_2401 = STR(irci_stable_candrpv_0415_20150521_0458);
60 static const char *release_version_2400 = STR(irci_stable_candrpv_0415_20150423_1753);
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 	const char *release_version;
192 	struct sh_css_fw_bi_file_h *file_header;
193 
194 	if (IS_ISP2401)
195 		release_version = release_version_2401;
196 	else
197 		release_version = release_version_2400;
198 
199 	firmware_header = (struct firmware_header *)fw_data;
200 	file_header = &firmware_header->file_header;
201 
202 	if (strcmp(file_header->version, release_version) != 0) {
203 		dev_err(dev, "Firmware version may not be compatible with this driver\n");
204 		dev_err(dev, "Expecting version '%s', but firmware is '%s'.\n",
205 			release_version, file_header->version);
206 	}
207 
208 	/* For now, let's just accept a wrong version, even if wrong */
209 	return false;
210 }
211 
212 static const char * const fw_type_name[] = {
213 	[ia_css_sp_firmware]		= "SP",
214 	[ia_css_isp_firmware]		= "ISP",
215 	[ia_css_bootloader_firmware]	= "BootLoader",
216 	[ia_css_acc_firmware]		= "accel",
217 };
218 
219 static const char * const fw_acc_type_name[] = {
220 	[IA_CSS_ACC_NONE] =		"Normal",
221 	[IA_CSS_ACC_OUTPUT] =		"Accel for output",
222 	[IA_CSS_ACC_VIEWFINDER] =	"Accel for viewfinder",
223 	[IA_CSS_ACC_STANDALONE] =	"Stand-alone accel",
224 };
225 
226 int
sh_css_load_firmware(struct device * dev,const char * fw_data,unsigned int fw_size)227 sh_css_load_firmware(struct device *dev, const char *fw_data,
228 		     unsigned int fw_size)
229 {
230 	unsigned int i;
231 	const char *release_version;
232 	struct ia_css_fw_info *binaries;
233 	struct sh_css_fw_bi_file_h *file_header;
234 	int ret;
235 
236 	/* some sanity checks */
237 	if (!fw_data || fw_size < sizeof(struct sh_css_fw_bi_file_h))
238 		return -EINVAL;
239 
240 	firmware_header = (struct firmware_header *)fw_data;
241 	file_header = &firmware_header->file_header;
242 
243 	if (file_header->h_size != sizeof(struct sh_css_fw_bi_file_h))
244 		return -EINVAL;
245 
246 	binaries = &firmware_header->binary_header;
247 	strscpy(FW_rel_ver_name, file_header->version,
248 		min(sizeof(FW_rel_ver_name), sizeof(file_header->version)));
249 	if (IS_ISP2401)
250 		release_version = release_version_2401;
251 	else
252 		release_version = release_version_2400;
253 	ret = sh_css_check_firmware_version(dev, fw_data);
254 	if (ret) {
255 		IA_CSS_ERROR("CSS code version (%s) and firmware version (%s) mismatch!",
256 			     file_header->version, release_version);
257 		return -EINVAL;
258 	} else {
259 		IA_CSS_LOG("successfully load firmware version %s", release_version);
260 	}
261 
262 	sh_css_num_binaries = file_header->binary_nr;
263 	/* Only allocate memory for ISP blob info */
264 	if (sh_css_num_binaries > NUM_OF_SPS) {
265 		sh_css_blob_info = kmalloc(
266 		    (sh_css_num_binaries - NUM_OF_SPS) *
267 		    sizeof(*sh_css_blob_info), GFP_KERNEL);
268 		if (!sh_css_blob_info)
269 			return -ENOMEM;
270 	} else {
271 		sh_css_blob_info = NULL;
272 	}
273 
274 	fw_minibuffer = kcalloc(sh_css_num_binaries, sizeof(struct fw_param),
275 				GFP_KERNEL);
276 	if (!fw_minibuffer)
277 		return -ENOMEM;
278 
279 	for (i = 0; i < sh_css_num_binaries; i++) {
280 		struct ia_css_fw_info *bi = &binaries[i];
281 		/*
282 		 * note: the var below is made static as it is quite large;
283 		 * if it is not static it ends up on the stack which could
284 		 * cause issues for drivers
285 		 */
286 		static struct ia_css_blob_descr bd;
287 		int err;
288 
289 		err = sh_css_load_blob_info(fw_data, bi, &bd, i);
290 
291 		if (err)
292 			return -EINVAL;
293 
294 		if (bi->blob.offset + bi->blob.size > fw_size)
295 			return -EINVAL;
296 
297 		switch (bd.header.type) {
298 		case ia_css_isp_firmware:
299 			if (bd.header.info.isp.type > IA_CSS_ACC_STANDALONE) {
300 				dev_err(dev, "binary #%2d: invalid SP type\n",
301 					i);
302 				return -EINVAL;
303 			}
304 
305 			dev_dbg(dev,
306 				"binary #%-2d type %s (%s), binary id is %2d: %s\n",
307 				i,
308 				fw_type_name[bd.header.type],
309 				fw_acc_type_name[bd.header.info.isp.type],
310 				bd.header.info.isp.sp.id,
311 				bd.name);
312 			break;
313 		case ia_css_sp_firmware:
314 		case ia_css_bootloader_firmware:
315 		case ia_css_acc_firmware:
316 			dev_dbg(dev,
317 				"binary #%-2d type %s: %s\n",
318 				i, fw_type_name[bd.header.type],
319 				bd.name);
320 			break;
321 		default:
322 			if (bd.header.info.isp.type > IA_CSS_ACC_STANDALONE) {
323 				dev_err(dev,
324 					"binary #%2d: invalid firmware type\n",
325 					i);
326 				return -EINVAL;
327 			}
328 			break;
329 		}
330 
331 		if (bi->type == ia_css_sp_firmware) {
332 			if (i != SP_FIRMWARE)
333 				return -EINVAL;
334 			err = setup_binary(bi, fw_data, &sh_css_sp_fw, i);
335 			if (err)
336 				return err;
337 
338 		} else {
339 			/*
340 			 * All subsequent binaries
341 			 * (including bootloaders) (i>NUM_OF_SPS)
342 			 * are ISP firmware
343 			 */
344 			if (i < NUM_OF_SPS)
345 				return -EINVAL;
346 
347 			if (bi->type != ia_css_isp_firmware)
348 				return -EINVAL;
349 			if (!sh_css_blob_info) /* cannot happen but KW does not see this */
350 				return -EINVAL;
351 			sh_css_blob_info[i - NUM_OF_SPS] = bd;
352 		}
353 	}
354 
355 	return 0;
356 }
357 
sh_css_unload_firmware(void)358 void sh_css_unload_firmware(void)
359 {
360 	/* release firmware minibuffer */
361 	if (fw_minibuffer) {
362 		unsigned int i = 0;
363 
364 		for (i = 0; i < sh_css_num_binaries; i++) {
365 			kfree(fw_minibuffer[i].name);
366 			kvfree(fw_minibuffer[i].buffer);
367 		}
368 		kfree(fw_minibuffer);
369 		fw_minibuffer = NULL;
370 	}
371 
372 	memset(&sh_css_sp_fw, 0, sizeof(sh_css_sp_fw));
373 	kfree(sh_css_blob_info);
374 	sh_css_blob_info = NULL;
375 	sh_css_num_binaries = 0;
376 }
377 
378 ia_css_ptr
sh_css_load_blob(const unsigned char * blob,unsigned int size)379 sh_css_load_blob(const unsigned char *blob, unsigned int size)
380 {
381 	ia_css_ptr target_addr = hmm_alloc(size);
382 	/*
383 	 * this will allocate memory aligned to a DDR word boundary which
384 	 * is required for the CSS DMA to read the instructions.
385 	 */
386 
387 	assert(blob);
388 	if (target_addr)
389 		hmm_store(target_addr, blob, size);
390 	return target_addr;
391 }
392