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