media: atomisp: provide more details about the firmware binaries
[linux-2.6-microblaze.git] / drivers / staging / media / atomisp / pci / 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 "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 int
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 -EINVAL;
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 -ENOMEM;
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 0;
99 }
100
101 int
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 -EINVAL;
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 -EINVAL;
122         }
123
124         if ((bi->blob.offset % (1UL << (ISP_PMEM_WIDTH_LOG2 - 3))) != 0)
125                 return -EINVAL;
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 -ENOMEM;
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 -ENOMEM;
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 0;
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 0;
207 }
208
209 int
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         int ret;
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         ret = sh_css_check_firmware_version(dev, fw_data);
228         if (ret) {
229                 IA_CSS_ERROR("CSS code version (%s) and firmware version (%s) mismatch!",
230                              file_header->version, release_version);
231                 return -EINVAL;
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 -EINVAL;
239
240         if (file_header->h_size != sizeof(struct sh_css_fw_bi_file_h))
241                 return -EINVAL;
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 -ENOMEM;
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 -ENOMEM;
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                 int err;
270
271                 err = sh_css_load_blob_info(fw_data, bi, &bd, i);
272
273                 if (err)
274                         return -EINVAL;
275
276                 if (bi->blob.offset + bi->blob.size > fw_size)
277                         return -EINVAL;
278
279                 if (bi->type == ia_css_sp_firmware) {
280                         if (i != SP_FIRMWARE)
281                                 return -EINVAL;
282                         err = setup_binary(bi, fw_data, &sh_css_sp_fw, i);
283                         if (err)
284                                 return err;
285                         dev_dbg(dev, "firmware #%d (SP), name %s\n", i, bd.name);
286
287                 } else {
288                         /* All subsequent binaries (including bootloaders) (i>NUM_OF_SPS) are ISP firmware */
289                         if (i < NUM_OF_SPS)
290                                 return -EINVAL;
291
292                         if (bi->type != ia_css_isp_firmware)
293                                 return -EINVAL;
294                         if (!sh_css_blob_info) /* cannot happen but KW does not see this */
295                                 return -EINVAL;
296                         sh_css_blob_info[i - NUM_OF_SPS] = bd;
297                 }
298         }
299
300         return 0;
301 }
302
303 void sh_css_unload_firmware(void)
304 {
305         /* release firmware minibuffer */
306         if (fw_minibuffer) {
307                 unsigned int i = 0;
308
309                 for (i = 0; i < sh_css_num_binaries; i++) {
310                         if (fw_minibuffer[i].name)
311                                 kfree((void *)fw_minibuffer[i].name);
312                         if (fw_minibuffer[i].buffer)
313                                 vfree((void *)fw_minibuffer[i].buffer);
314                 }
315                 kfree(fw_minibuffer);
316                 fw_minibuffer = NULL;
317         }
318
319         memset(&sh_css_sp_fw, 0, sizeof(sh_css_sp_fw));
320         kfree(sh_css_blob_info);
321         sh_css_blob_info = NULL;
322         sh_css_num_binaries = 0;
323 }
324
325 ia_css_ptr
326 sh_css_load_blob(const unsigned char *blob, unsigned int size)
327 {
328         ia_css_ptr target_addr = hmm_alloc(size, HMM_BO_PRIVATE, 0, NULL, 0);
329         /* this will allocate memory aligned to a DDR word boundary which
330            is required for the CSS DMA to read the instructions. */
331
332         assert(blob);
333         if (target_addr)
334                 hmm_store(target_addr, blob, size);
335         return target_addr;
336 }