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