1 /*
2  * Support for Intel Camera Imaging ISP subsystem.
3  * Copyright (c) 2015, Intel Corporation.
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms and conditions of the GNU General Public License,
7  * version 2, as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  */
14 
15 #include <linux/slab.h>
16 #include <linux/vmalloc.h>
17 
18 #include "hmm.h"
19 
20 #include <math_support.h>
21 #include "platform_support.h"
22 #include "sh_css_firmware.h"
23 
24 #include "sh_css_defs.h"
25 #include "ia_css_debug.h"
26 #include "sh_css_internal.h"
27 #include "ia_css_isp_param.h"
28 
29 #include "assert_support.h"
30 #include "string_support.h"
31 
32 #include "isp.h"				/* PMEM_WIDTH_LOG2 */
33 
34 #include "ia_css_isp_params.h"
35 #include "ia_css_isp_configs.h"
36 #include "ia_css_isp_states.h"
37 
38 #define _STR(x) #x
39 #define STR(x) _STR(x)
40 
41 struct firmware_header {
42 	struct sh_css_fw_bi_file_h file_header;
43 	struct ia_css_fw_info      binary_header;
44 };
45 
46 struct fw_param {
47 	const char *name;
48 	const void *buffer;
49 };
50 
51 static struct firmware_header *firmware_header;
52 
53 /* The string STR is a place holder
54  * which will be replaced with the actual RELEASE_VERSION
55  * during package generation. Please do not modify  */
56 static const char *isp2400_release_version = STR(irci_stable_candrpv_0415_20150521_0458);
57 static const char *isp2401_release_version = STR(irci_ecr - master_20150911_0724);
58 
59 #define MAX_FW_REL_VER_NAME	300
60 static char FW_rel_ver_name[MAX_FW_REL_VER_NAME] = "---";
61 
62 struct ia_css_fw_info	  sh_css_sp_fw;
63 struct ia_css_blob_descr *sh_css_blob_info; /* Only ISP blob info (no SP) */
64 unsigned int sh_css_num_binaries; /* This includes 1 SP binary */
65 
66 static struct fw_param *fw_minibuffer;
67 
68 char *sh_css_get_fw_version(void)
69 {
70 	return FW_rel_ver_name;
71 }
72 
73 /*
74  * Split the loaded firmware into blobs
75  */
76 
77 /* Setup sp/sp1 binary */
78 static enum ia_css_err
79 setup_binary(struct ia_css_fw_info *fw, const char *fw_data,
80 	     struct ia_css_fw_info *sh_css_fw, unsigned int binary_id) {
81 	const char *blob_data;
82 
83 	if ((!fw) || (!fw_data))
84 		return IA_CSS_ERR_INVALID_ARGUMENTS;
85 
86 	blob_data = fw_data + fw->blob.offset;
87 
88 	*sh_css_fw = *fw;
89 
90 	sh_css_fw->blob.code = vmalloc(fw->blob.size);
91 	if (!sh_css_fw->blob.code)
92 		return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
93 
94 	memcpy((void *)sh_css_fw->blob.code, blob_data, fw->blob.size);
95 	sh_css_fw->blob.data = (char *)sh_css_fw->blob.code + fw->blob.data_source;
96 	fw_minibuffer[binary_id].buffer = sh_css_fw->blob.code;
97 
98 	return IA_CSS_SUCCESS;
99 }
100 
101 enum ia_css_err
102 sh_css_load_blob_info(const char *fw, const struct ia_css_fw_info *bi,
103 		      struct ia_css_blob_descr *bd,
104 		      unsigned int index) {
105 	const char *name;
106 	const unsigned char *blob;
107 
108 	if ((!fw) || (!bd))
109 		return IA_CSS_ERR_INVALID_ARGUMENTS;
110 
111 	/* Special case: only one binary in fw */
112 	if (!bi) bi = (const struct ia_css_fw_info *)fw;
113 
114 	name = fw + bi->blob.prog_name_offset;
115 	blob = (const unsigned char *)fw + bi->blob.offset;
116 
117 	/* sanity check */
118 	if (bi->blob.size != bi->blob.text_size + bi->blob.icache_size + bi->blob.data_size + bi->blob.padding_size)
119 	{
120 		/* sanity check, note the padding bytes added for section to DDR alignment */
121 		return IA_CSS_ERR_INVALID_ARGUMENTS;
122 	}
123 
124 	if ((bi->blob.offset % (1UL << (ISP_PMEM_WIDTH_LOG2 - 3))) != 0)
125 		return IA_CSS_ERR_INVALID_ARGUMENTS;
126 
127 	bd->blob = blob;
128 	bd->header = *bi;
129 
130 	if (bi->type == ia_css_isp_firmware || bi->type == ia_css_sp_firmware)
131 	{
132 		char *namebuffer;
133 
134 		namebuffer = kstrdup(name, GFP_KERNEL);
135 		if (!namebuffer)
136 			return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
137 		bd->name = fw_minibuffer[index].name = namebuffer;
138 	} else
139 	{
140 		bd->name = name;
141 	}
142 
143 	if (bi->type == ia_css_isp_firmware)
144 	{
145 		size_t paramstruct_size = sizeof(struct ia_css_memory_offsets);
146 		size_t configstruct_size = sizeof(struct ia_css_config_memory_offsets);
147 		size_t statestruct_size = sizeof(struct ia_css_state_memory_offsets);
148 
149 		char *parambuf = kmalloc(paramstruct_size + configstruct_size +
150 					 statestruct_size,
151 					 GFP_KERNEL);
152 		if (!parambuf)
153 			return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
154 
155 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = NULL;
156 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = NULL;
157 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = NULL;
158 
159 		fw_minibuffer[index].buffer = parambuf;
160 
161 		/* copy ia_css_memory_offsets */
162 		memcpy(parambuf, (void *)(fw +
163 					  bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_PARAM]),
164 		       paramstruct_size);
165 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = parambuf;
166 
167 		/* copy ia_css_config_memory_offsets */
168 		memcpy(parambuf + paramstruct_size,
169 		       (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_CONFIG]),
170 		       configstruct_size);
171 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = parambuf +
172 		paramstruct_size;
173 
174 		/* copy ia_css_state_memory_offsets */
175 		memcpy(parambuf + paramstruct_size + configstruct_size,
176 		       (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_STATE]),
177 		       statestruct_size);
178 		bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = parambuf +
179 		paramstruct_size + configstruct_size;
180 	}
181 	return IA_CSS_SUCCESS;
182 }
183 
184 bool
185 sh_css_check_firmware_version(struct device *dev, const char *fw_data)
186 {
187 	struct sh_css_fw_bi_file_h *file_header;
188 
189 	const char *release_version;
190 
191 	if (!atomisp_hw_is_isp2401)
192 		release_version = isp2400_release_version;
193 	else
194 		release_version = isp2401_release_version;
195 
196 	firmware_header = (struct firmware_header *)fw_data;
197 	file_header = &firmware_header->file_header;
198 
199 	if (strcmp(file_header->version, release_version) != 0) {
200 		dev_err(dev, "Firmware version may not be compatible with this driver\n");
201 		dev_err(dev, "Expecting version '%s', but firmware is '%s'.\n",
202 			release_version, file_header->version);
203 	}
204 
205 	/* For now, let's just accept a wrong version, even if wrong */
206 	return true;
207 }
208 
209 enum ia_css_err
210 sh_css_load_firmware(struct device *dev, const char *fw_data,
211 		     unsigned int fw_size) {
212 	unsigned int i;
213 	struct ia_css_fw_info *binaries;
214 	struct sh_css_fw_bi_file_h *file_header;
215 	bool valid_firmware = false;
216 	const char *release_version;
217 
218 	if (!atomisp_hw_is_isp2401)
219 		release_version = isp2400_release_version;
220 	else
221 		release_version = isp2401_release_version;
222 
223 	firmware_header = (struct firmware_header *)fw_data;
224 	file_header = &firmware_header->file_header;
225 	binaries = &firmware_header->binary_header;
226 	strscpy(FW_rel_ver_name, file_header->version, min(sizeof(FW_rel_ver_name), sizeof(file_header->version)));
227 	valid_firmware = sh_css_check_firmware_version(dev, fw_data);
228 	if (!valid_firmware) {
229 		IA_CSS_ERROR("CSS code version (%s) and firmware version (%s) mismatch!",
230 			     file_header->version, release_version);
231 		return IA_CSS_ERR_VERSION_MISMATCH;
232 	} else {
233 		IA_CSS_LOG("successfully load firmware version %s", release_version);
234 	}
235 
236 	/* some sanity checks */
237 	if (!fw_data || fw_size < sizeof(struct sh_css_fw_bi_file_h))
238 		return IA_CSS_ERR_INTERNAL_ERROR;
239 
240 	if (file_header->h_size != sizeof(struct sh_css_fw_bi_file_h))
241 		return IA_CSS_ERR_INTERNAL_ERROR;
242 
243 	sh_css_num_binaries = file_header->binary_nr;
244 	/* Only allocate memory for ISP blob info */
245 	if (sh_css_num_binaries > NUM_OF_SPS)
246 	{
247 		sh_css_blob_info = kmalloc(
248 		    (sh_css_num_binaries - NUM_OF_SPS) *
249 		    sizeof(*sh_css_blob_info), GFP_KERNEL);
250 		if (!sh_css_blob_info)
251 			return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
252 	} else {
253 		sh_css_blob_info = NULL;
254 	}
255 
256 	fw_minibuffer = kcalloc(sh_css_num_binaries, sizeof(struct fw_param),
257 				GFP_KERNEL);
258 	if (!fw_minibuffer)
259 		return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
260 
261 	for (i = 0; i < sh_css_num_binaries; i++)
262 	{
263 		struct ia_css_fw_info *bi = &binaries[i];
264 		/* note: the var below is made static as it is quite large;
265 		   if it is not static it ends up on the stack which could
266 		   cause issues for drivers
267 		*/
268 		static struct ia_css_blob_descr bd;
269 		enum ia_css_err err;
270 
271 		err = sh_css_load_blob_info(fw_data, bi, &bd, i);
272 
273 		if (err != IA_CSS_SUCCESS)
274 			return IA_CSS_ERR_INTERNAL_ERROR;
275 
276 		if (bi->blob.offset + bi->blob.size > fw_size)
277 			return IA_CSS_ERR_INTERNAL_ERROR;
278 
279 		if (bi->type == ia_css_sp_firmware) {
280 			if (i != SP_FIRMWARE)
281 				return IA_CSS_ERR_INTERNAL_ERROR;
282 			err = setup_binary(bi, fw_data, &sh_css_sp_fw, i);
283 			if (err != IA_CSS_SUCCESS)
284 				return err;
285 		} else {
286 			/* All subsequent binaries (including bootloaders) (i>NUM_OF_SPS) are ISP firmware */
287 			if (i < NUM_OF_SPS)
288 				return IA_CSS_ERR_INTERNAL_ERROR;
289 
290 			if (bi->type != ia_css_isp_firmware)
291 				return IA_CSS_ERR_INTERNAL_ERROR;
292 			if (!sh_css_blob_info) /* cannot happen but KW does not see this */
293 				return IA_CSS_ERR_INTERNAL_ERROR;
294 			sh_css_blob_info[i - NUM_OF_SPS] = bd;
295 		}
296 	}
297 
298 	return IA_CSS_SUCCESS;
299 }
300 
301 void sh_css_unload_firmware(void)
302 {
303 	/* release firmware minibuffer */
304 	if (fw_minibuffer) {
305 		unsigned int i = 0;
306 
307 		for (i = 0; i < sh_css_num_binaries; i++) {
308 			if (fw_minibuffer[i].name)
309 				kfree((void *)fw_minibuffer[i].name);
310 			if (fw_minibuffer[i].buffer)
311 				vfree((void *)fw_minibuffer[i].buffer);
312 		}
313 		kfree(fw_minibuffer);
314 		fw_minibuffer = NULL;
315 	}
316 
317 	memset(&sh_css_sp_fw, 0, sizeof(sh_css_sp_fw));
318 	kfree(sh_css_blob_info);
319 	sh_css_blob_info = NULL;
320 	sh_css_num_binaries = 0;
321 }
322 
323 ia_css_ptr
324 sh_css_load_blob(const unsigned char *blob, unsigned int size)
325 {
326 	ia_css_ptr target_addr = hmm_alloc(size, HMM_BO_PRIVATE, 0, NULL, 0);
327 	/* this will allocate memory aligned to a DDR word boundary which
328 	   is required for the CSS DMA to read the instructions. */
329 
330 	assert(blob);
331 	if (target_addr)
332 		hmm_store(target_addr, blob, size);
333 	return target_addr;
334 }
335