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 /* The string STR is a place holder
55  * which will be replaced with the actual RELEASE_VERSION
56  * during package generation. Please do not modify  */
57 static const char *isp2400_release_version = STR(irci_stable_candrpv_0415_20150521_0458);
58 static const char *isp2401_release_version = STR(irci_ecr - master_20150911_0724);
59 
60 #define MAX_FW_REL_VER_NAME	300
61 static char FW_rel_ver_name[MAX_FW_REL_VER_NAME] = "---";
62 
63 struct ia_css_fw_info	  sh_css_sp_fw;
64 struct ia_css_blob_descr *sh_css_blob_info; /* Only ISP blob info (no SP) */
65 unsigned int sh_css_num_binaries; /* This includes 1 SP binary */
66 
67 static struct fw_param *fw_minibuffer;
68 
69 char *sh_css_get_fw_version(void)
70 {
71 	return FW_rel_ver_name;
72 }
73 
74 /*
75  * Split the loaded firmware into blobs
76  */
77 
78 /* Setup sp/sp1 binary */
79 static int
80 setup_binary(struct ia_css_fw_info *fw, const char *fw_data,
81 	     struct ia_css_fw_info *sh_css_fw, unsigned int binary_id) {
82 	const char *blob_data;
83 
84 	if ((!fw) || (!fw_data))
85 		return -EINVAL;
86 
87 	blob_data = fw_data + fw->blob.offset;
88 
89 	*sh_css_fw = *fw;
90 
91 	sh_css_fw->blob.code = vmalloc(fw->blob.size);
92 	if (!sh_css_fw->blob.code)
93 		return -ENOMEM;
94 
95 	memcpy((void *)sh_css_fw->blob.code, blob_data, fw->blob.size);
96 	sh_css_fw->blob.data = (char *)sh_css_fw->blob.code + fw->blob.data_source;
97 	fw_minibuffer[binary_id].buffer = sh_css_fw->blob.code;
98 
99 	return 0;
100 }
101 
102 int
103 sh_css_load_blob_info(const char *fw, const struct ia_css_fw_info *bi,
104 		      struct ia_css_blob_descr *bd,
105 		      unsigned int index) {
106 	const char *name;
107 	const unsigned char *blob;
108 
109 	if ((!fw) || (!bd))
110 		return -EINVAL;
111 
112 	/* Special case: only one binary in fw */
113 	if (!bi) bi = (const struct ia_css_fw_info *)fw;
114 
115 	name = fw + bi->blob.prog_name_offset;
116 	blob = (const unsigned char *)fw + bi->blob.offset;
117 
118 	/* sanity check */
119 	if (bi->blob.size != bi->blob.text_size + bi->blob.icache_size + bi->blob.data_size + bi->blob.padding_size)
120 	{
121 		/* sanity check, note the padding bytes added for section to DDR alignment */
122 		return -EINVAL;
123 	}
124 
125 	if ((bi->blob.offset % (1UL << (ISP_PMEM_WIDTH_LOG2 - 3))) != 0)
126 		return -EINVAL;
127 
128 	bd->blob = blob;
129 	bd->header = *bi;
130 
131 	if (bi->type == ia_css_isp_firmware || bi->type == ia_css_sp_firmware)
132 	{
133 		char *namebuffer;
134 
135 		namebuffer = kstrdup(name, GFP_KERNEL);
136 		if (!namebuffer)
137 			return -ENOMEM;
138 		bd->name = fw_minibuffer[index].name = namebuffer;
139 	} else
140 	{
141 		bd->name = name;
142 	}
143 
144 	if (bi->type == ia_css_isp_firmware)
145 	{
146 		size_t paramstruct_size = sizeof(struct ia_css_memory_offsets);
147 		size_t configstruct_size = sizeof(struct ia_css_config_memory_offsets);
148 		size_t statestruct_size = sizeof(struct ia_css_state_memory_offsets);
149 
150 		char *parambuf = kmalloc(paramstruct_size + configstruct_size +
151 					 statestruct_size,
152 					 GFP_KERNEL);
153 		if (!parambuf)
154 			return -ENOMEM;
155 
156 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = NULL;
157 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = NULL;
158 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = NULL;
159 
160 		fw_minibuffer[index].buffer = parambuf;
161 
162 		/* copy ia_css_memory_offsets */
163 		memcpy(parambuf, (void *)(fw +
164 					  bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_PARAM]),
165 		       paramstruct_size);
166 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = parambuf;
167 
168 		/* copy ia_css_config_memory_offsets */
169 		memcpy(parambuf + paramstruct_size,
170 		       (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_CONFIG]),
171 		       configstruct_size);
172 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = parambuf +
173 		paramstruct_size;
174 
175 		/* copy ia_css_state_memory_offsets */
176 		memcpy(parambuf + paramstruct_size + configstruct_size,
177 		       (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_STATE]),
178 		       statestruct_size);
179 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = parambuf +
180 		paramstruct_size + configstruct_size;
181 	}
182 	return 0;
183 }
184 
185 bool
186 sh_css_check_firmware_version(struct device *dev, const char *fw_data)
187 {
188 	struct sh_css_fw_bi_file_h *file_header;
189 
190 	const char *release_version;
191 
192 	if (!IS_ISP2401)
193 		release_version = isp2400_release_version;
194 	else
195 		release_version = isp2401_release_version;
196 
197 	firmware_header = (struct firmware_header *)fw_data;
198 	file_header = &firmware_header->file_header;
199 
200 	if (strcmp(file_header->version, release_version) != 0) {
201 		dev_err(dev, "Firmware version may not be compatible with this driver\n");
202 		dev_err(dev, "Expecting version '%s', but firmware is '%s'.\n",
203 			release_version, file_header->version);
204 	}
205 
206 	/* For now, let's just accept a wrong version, even if wrong */
207 	return 0;
208 }
209 
210 static const char * const fw_type_name[] = {
211 	[ia_css_sp_firmware]		= "SP",
212 	[ia_css_isp_firmware]		= "ISP",
213 	[ia_css_bootloader_firmware]	= "BootLoader",
214 	[ia_css_acc_firmware]		= "accel",
215 };
216 
217 static const char * const fw_acc_type_name[] = {
218 	[IA_CSS_ACC_NONE] =		"Normal",
219 	[IA_CSS_ACC_OUTPUT] =		"Accel for output",
220 	[IA_CSS_ACC_VIEWFINDER] =	"Accel for viewfinder",
221 	[IA_CSS_ACC_STANDALONE] =	"Stand-alone accel",
222 };
223 
224 int
225 sh_css_load_firmware(struct device *dev, const char *fw_data,
226 		     unsigned int fw_size) {
227 	unsigned int i;
228 	struct ia_css_fw_info *binaries;
229 	struct sh_css_fw_bi_file_h *file_header;
230 	int ret;
231 	const char *release_version;
232 
233 	if (!IS_ISP2401)
234 		release_version = isp2400_release_version;
235 	else
236 		release_version = isp2401_release_version;
237 
238 	firmware_header = (struct firmware_header *)fw_data;
239 	file_header = &firmware_header->file_header;
240 	binaries = &firmware_header->binary_header;
241 	strscpy(FW_rel_ver_name, file_header->version, min(sizeof(FW_rel_ver_name), sizeof(file_header->version)));
242 	ret = sh_css_check_firmware_version(dev, fw_data);
243 	if (ret) {
244 		IA_CSS_ERROR("CSS code version (%s) and firmware version (%s) mismatch!",
245 			     file_header->version, release_version);
246 		return -EINVAL;
247 	} else {
248 		IA_CSS_LOG("successfully load firmware version %s", release_version);
249 	}
250 
251 	/* some sanity checks */
252 	if (!fw_data || fw_size < sizeof(struct sh_css_fw_bi_file_h))
253 		return -EINVAL;
254 
255 	if (file_header->h_size != sizeof(struct sh_css_fw_bi_file_h))
256 		return -EINVAL;
257 
258 	sh_css_num_binaries = file_header->binary_nr;
259 	/* Only allocate memory for ISP blob info */
260 	if (sh_css_num_binaries > NUM_OF_SPS)
261 	{
262 		sh_css_blob_info = kmalloc(
263 		    (sh_css_num_binaries - NUM_OF_SPS) *
264 		    sizeof(*sh_css_blob_info), GFP_KERNEL);
265 		if (!sh_css_blob_info)
266 			return -ENOMEM;
267 	} else {
268 		sh_css_blob_info = NULL;
269 	}
270 
271 	fw_minibuffer = kcalloc(sh_css_num_binaries, sizeof(struct fw_param),
272 				GFP_KERNEL);
273 	if (!fw_minibuffer)
274 		return -ENOMEM;
275 
276 	for (i = 0; i < sh_css_num_binaries; i++)
277 	{
278 		struct ia_css_fw_info *bi = &binaries[i];
279 		/* note: the var below is made static as it is quite large;
280 		   if it is not static it ends up on the stack which could
281 		   cause issues for drivers
282 		*/
283 		static struct ia_css_blob_descr bd;
284 		int err;
285 
286 		err = sh_css_load_blob_info(fw_data, bi, &bd, i);
287 
288 		if (err)
289 			return -EINVAL;
290 
291 		if (bi->blob.offset + bi->blob.size > fw_size)
292 			return -EINVAL;
293 
294 		switch (bd.header.type) {
295 		case ia_css_isp_firmware:
296 			if (bd.header.info.isp.type > IA_CSS_ACC_STANDALONE) {
297 				dev_err(dev, "binary #%2d: invalid SP type\n",
298 					i);
299 				return -EINVAL;
300 			}
301 
302 			dev_dbg(dev,
303 				"binary #%-2d type %s (%s), binary id is %2d: %s\n",
304 				i,
305 				fw_type_name[bd.header.type],
306 				fw_acc_type_name[bd.header.info.isp.type],
307 				bd.header.info.isp.sp.id,
308 				bd.name);
309 			break;
310 		case ia_css_sp_firmware:
311 		case ia_css_bootloader_firmware:
312 		case ia_css_acc_firmware:
313 			dev_dbg(dev,
314 				"binary #%-2d type %s: %s\n",
315 				i, fw_type_name[bd.header.type],
316 				bd.name);
317 			break;
318 		default:
319 			if (bd.header.info.isp.type > IA_CSS_ACC_STANDALONE) {
320 				dev_err(dev,
321 					"binary #%2d: invalid firmware type\n",
322 					i);
323 				return -EINVAL;
324 			}
325 			break;
326 		}
327 
328 		if (bi->type == ia_css_sp_firmware) {
329 			if (i != SP_FIRMWARE)
330 				return -EINVAL;
331 			err = setup_binary(bi, fw_data, &sh_css_sp_fw, i);
332 			if (err)
333 				return err;
334 
335 		} else {
336 			/* All subsequent binaries (including bootloaders) (i>NUM_OF_SPS) are ISP firmware */
337 			if (i < NUM_OF_SPS)
338 				return -EINVAL;
339 
340 			if (bi->type != ia_css_isp_firmware)
341 				return -EINVAL;
342 			if (!sh_css_blob_info) /* cannot happen but KW does not see this */
343 				return -EINVAL;
344 			sh_css_blob_info[i - NUM_OF_SPS] = bd;
345 		}
346 	}
347 
348 	return 0;
349 }
350 
351 void sh_css_unload_firmware(void)
352 {
353 	/* release firmware minibuffer */
354 	if (fw_minibuffer) {
355 		unsigned int i = 0;
356 
357 		for (i = 0; i < sh_css_num_binaries; i++) {
358 			if (fw_minibuffer[i].name)
359 				kfree((void *)fw_minibuffer[i].name);
360 			if (fw_minibuffer[i].buffer)
361 				vfree((void *)fw_minibuffer[i].buffer);
362 		}
363 		kfree(fw_minibuffer);
364 		fw_minibuffer = NULL;
365 	}
366 
367 	memset(&sh_css_sp_fw, 0, sizeof(sh_css_sp_fw));
368 	kfree(sh_css_blob_info);
369 	sh_css_blob_info = NULL;
370 	sh_css_num_binaries = 0;
371 }
372 
373 ia_css_ptr
374 sh_css_load_blob(const unsigned char *blob, unsigned int size)
375 {
376 	ia_css_ptr target_addr = hmm_alloc(size, HMM_BO_PRIVATE, 0, NULL, 0);
377 	/* this will allocate memory aligned to a DDR word boundary which
378 	   is required for the CSS DMA to read the instructions. */
379 
380 	assert(blob);
381 	if (target_addr)
382 		hmm_store(target_addr, blob, size);
383 	return target_addr;
384 }
385