GNU Linux-libre 4.14.251-gnu1
[releases.git] / drivers / staging / media / atomisp / pci / atomisp2 / css2400 / sh_css_firmware.c
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 <math_support.h>
19 #include "platform_support.h"
20 #include "sh_css_firmware.h"
21
22 #include "sh_css_defs.h"
23 #include "ia_css_debug.h"
24 #include "sh_css_internal.h"
25 #include "ia_css_isp_param.h"
26
27 #include "memory_access.h"
28 #include "assert_support.h"
29 #include "string_support.h"
30
31 #include "isp.h"                                /* PMEM_WIDTH_LOG2 */
32
33 #include "ia_css_isp_params.h"
34 #include "ia_css_isp_configs.h"
35 #include "ia_css_isp_states.h"
36
37 #define _STR(x) #x
38 #define STR(x) _STR(x)
39
40 struct firmware_header {
41         struct sh_css_fw_bi_file_h file_header;
42         struct ia_css_fw_info      binary_header;
43 };
44
45 struct fw_param {
46         const char *name;
47         const void *buffer;
48 };
49
50 /* Warning: same order as SH_CSS_BINARY_ID_* */
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 #ifndef ISP2401
57 static const char *release_version = STR(irci_stable_candrpv_0415_20150521_0458);
58 #else
59 static const char *release_version = STR(irci_ecr-master_20150911_0724);
60 #endif
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                  sh_css_num_binaries; /* This includes 1 SP binary */
68
69 static struct fw_param *fw_minibuffer;
70
71
72 char *sh_css_get_fw_version(void)
73 {
74         return FW_rel_ver_name;
75 }
76
77
78 /*
79  * Split the loaded firmware into blobs
80  */
81
82 /* Setup sp/sp1 binary */
83 static enum ia_css_err
84 setup_binary(struct ia_css_fw_info *fw, const char *fw_data, struct ia_css_fw_info *sh_css_fw, unsigned binary_id)
85 {
86         const char *blob_data;
87
88         if ((fw == NULL) || (fw_data == NULL))
89                 return IA_CSS_ERR_INVALID_ARGUMENTS;
90
91         blob_data = fw_data + fw->blob.offset;
92
93         *sh_css_fw = *fw;
94
95         sh_css_fw->blob.code = vmalloc(fw->blob.size);
96         if (sh_css_fw->blob.code == NULL)
97                 return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
98
99         memcpy((void *)sh_css_fw->blob.code, blob_data, fw->blob.size);
100         sh_css_fw->blob.data = (char *)sh_css_fw->blob.code + fw->blob.data_source;
101         fw_minibuffer[binary_id].buffer = sh_css_fw->blob.code;
102
103         return IA_CSS_SUCCESS;
104 }
105 enum ia_css_err
106 sh_css_load_blob_info(const char *fw, const struct ia_css_fw_info *bi, struct ia_css_blob_descr *bd, unsigned index)
107 {
108         const char *name;
109         const unsigned char *blob;
110
111         if ((fw == NULL) || (bd == NULL))
112                 return IA_CSS_ERR_INVALID_ARGUMENTS;
113
114         /* Special case: only one binary in fw */
115         if (bi == NULL) bi = (const struct ia_css_fw_info *)fw;
116
117         name = fw + bi->blob.prog_name_offset;
118         blob = (const unsigned char *)fw + bi->blob.offset;
119
120         /* sanity check */
121         if (bi->blob.size != bi->blob.text_size + bi->blob.icache_size + bi->blob.data_size + bi->blob.padding_size) {
122                 /* sanity check, note the padding bytes added for section to DDR alignment */
123                 return IA_CSS_ERR_INVALID_ARGUMENTS;
124         }
125
126         if ((bi->blob.offset % (1UL<<(ISP_PMEM_WIDTH_LOG2-3))) != 0)
127                 return IA_CSS_ERR_INVALID_ARGUMENTS;
128
129         bd->blob = blob;
130         bd->header = *bi;
131
132         if (bi->type == ia_css_isp_firmware || bi->type == ia_css_sp_firmware) {
133                 char *namebuffer;
134
135                 namebuffer = kstrdup(name, GFP_KERNEL);
136                 if (!namebuffer)
137                         return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
138                 bd->name = fw_minibuffer[index].name = namebuffer;
139         } else {
140                 bd->name = name;
141         }
142
143         if (bi->type == ia_css_isp_firmware) {
144                 size_t paramstruct_size = sizeof(struct ia_css_memory_offsets);
145                 size_t configstruct_size = sizeof(struct ia_css_config_memory_offsets);
146                 size_t statestruct_size = sizeof(struct ia_css_state_memory_offsets);
147
148                 char *parambuf = (char *)kmalloc(paramstruct_size + configstruct_size + statestruct_size,
149                                                         GFP_KERNEL);
150                 if (parambuf == NULL)
151                         return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
152
153                 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = NULL;
154                 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = NULL;
155                 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = NULL;
156
157                 fw_minibuffer[index].buffer = parambuf;
158
159                 /* copy ia_css_memory_offsets */
160                 memcpy(parambuf, (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_PARAM]),
161                         paramstruct_size);
162                 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_PARAM].ptr = parambuf;
163
164                 /* copy ia_css_config_memory_offsets */
165                 memcpy(parambuf + paramstruct_size,
166                                 (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_CONFIG]),
167                                 configstruct_size);
168                 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_CONFIG].ptr = parambuf + paramstruct_size;
169
170                 /* copy ia_css_state_memory_offsets */
171                 memcpy(parambuf + paramstruct_size + configstruct_size,
172                                 (void *)(fw + bi->blob.memory_offsets.offsets[IA_CSS_PARAM_CLASS_STATE]),
173                                 statestruct_size);
174                 bd->mem_offsets.array[IA_CSS_PARAM_CLASS_STATE].ptr = parambuf + paramstruct_size + configstruct_size;
175         }
176         return IA_CSS_SUCCESS;
177 }
178
179 bool
180 sh_css_check_firmware_version(const char *fw_data)
181 {
182         struct sh_css_fw_bi_file_h *file_header;
183
184         firmware_header = (struct firmware_header *)fw_data;
185         file_header = &firmware_header->file_header;
186
187         if (strcmp(file_header->version, release_version) != 0) {
188                 return false;
189         } else {
190                 /* firmware version matches */
191                 return true;
192         }
193 }
194
195 enum ia_css_err
196 sh_css_load_firmware(const char *fw_data,
197                      unsigned int fw_size)
198 {
199         unsigned i;
200         struct ia_css_fw_info *binaries;
201         struct sh_css_fw_bi_file_h *file_header;
202         bool valid_firmware = false;
203
204         firmware_header = (struct firmware_header *)fw_data;
205         file_header = &firmware_header->file_header;
206         binaries = &firmware_header->binary_header;
207         strncpy(FW_rel_ver_name, file_header->version, min(sizeof(FW_rel_ver_name), sizeof(file_header->version)) - 1);
208         valid_firmware = sh_css_check_firmware_version(fw_data);
209         if (!valid_firmware) {
210 #if !defined(HRT_RTL)
211                 IA_CSS_ERROR("CSS code version (%s) and firmware version (%s) mismatch!",
212                                 file_header->version, release_version);
213                 return IA_CSS_ERR_VERSION_MISMATCH;
214 #endif
215         } else {
216                 IA_CSS_LOG("successfully load firmware version %s", release_version);
217         }
218
219         /* some sanity checks */
220         if (!fw_data || fw_size < sizeof(struct sh_css_fw_bi_file_h))
221                 return IA_CSS_ERR_INTERNAL_ERROR;
222
223         if (file_header->h_size != sizeof(struct sh_css_fw_bi_file_h))
224                 return IA_CSS_ERR_INTERNAL_ERROR;
225
226         sh_css_num_binaries = file_header->binary_nr;
227         /* Only allocate memory for ISP blob info */
228         if (sh_css_num_binaries > NUM_OF_SPS) {
229                 sh_css_blob_info = kmalloc(
230                                         (sh_css_num_binaries - NUM_OF_SPS) *
231                                         sizeof(*sh_css_blob_info), GFP_KERNEL);
232                 if (sh_css_blob_info == NULL)
233                         return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
234         } else {
235                 sh_css_blob_info = NULL;
236         }
237
238         fw_minibuffer = kzalloc(sh_css_num_binaries * sizeof(struct fw_param), GFP_KERNEL);
239         if (fw_minibuffer == NULL)
240                 return IA_CSS_ERR_CANNOT_ALLOCATE_MEMORY;
241
242         for (i = 0; i < sh_css_num_binaries; i++) {
243                 struct ia_css_fw_info *bi = &binaries[i];
244                 /* note: the var below is made static as it is quite large;
245                    if it is not static it ends up on the stack which could
246                    cause issues for drivers
247                 */
248                 static struct ia_css_blob_descr bd;
249                 enum ia_css_err err;
250
251                 err = sh_css_load_blob_info(fw_data, bi, &bd, i);
252
253                 if (err != IA_CSS_SUCCESS)
254                         return IA_CSS_ERR_INTERNAL_ERROR;
255
256                 if (bi->blob.offset + bi->blob.size > fw_size)
257                         return IA_CSS_ERR_INTERNAL_ERROR;
258
259                 if (bi->type == ia_css_sp_firmware) {
260                         if (i != SP_FIRMWARE)
261                                 return IA_CSS_ERR_INTERNAL_ERROR;
262                         err = setup_binary(bi, fw_data, &sh_css_sp_fw, i);
263                         if (err != IA_CSS_SUCCESS)
264                                 return err;
265                 } else {
266                         /* All subsequent binaries (including bootloaders) (i>NUM_OF_SPS) are ISP firmware */
267                         if (i < NUM_OF_SPS)
268                                 return IA_CSS_ERR_INTERNAL_ERROR;
269
270                         if (bi->type != ia_css_isp_firmware)
271                                 return IA_CSS_ERR_INTERNAL_ERROR;
272                         if (sh_css_blob_info == NULL) /* cannot happen but KW does not see this */
273                                 return IA_CSS_ERR_INTERNAL_ERROR;
274                         sh_css_blob_info[i - NUM_OF_SPS] = bd;
275                 }
276         }
277
278         return IA_CSS_SUCCESS;
279 }
280
281 void sh_css_unload_firmware(void)
282 {
283
284         /* release firmware minibuffer */
285         if (fw_minibuffer) {
286                 unsigned int i = 0;
287                 for (i = 0; i < sh_css_num_binaries; i++) {
288                         if (fw_minibuffer[i].name)
289                                 kfree((void *)fw_minibuffer[i].name);
290                         if (fw_minibuffer[i].buffer)
291                                 vfree((void *)fw_minibuffer[i].buffer);
292                 }
293                 kfree(fw_minibuffer);
294                 fw_minibuffer = NULL;
295         }
296
297         memset(&sh_css_sp_fw, 0, sizeof(sh_css_sp_fw));
298         if (sh_css_blob_info) {
299                 kfree(sh_css_blob_info);
300                 sh_css_blob_info = NULL;
301         }
302         sh_css_num_binaries = 0;
303 }
304
305 hrt_vaddress
306 sh_css_load_blob(const unsigned char *blob, unsigned size)
307 {
308         hrt_vaddress target_addr = mmgr_malloc(size);
309         /* this will allocate memory aligned to a DDR word boundary which
310            is required for the CSS DMA to read the instructions. */
311
312         assert(blob != NULL);
313         if (target_addr) 
314                 mmgr_store(target_addr, blob, size);
315         return target_addr;
316 }