blob: fa21f0c39dce13e708eabc06ff9a9ff4576211a1
1 | /* |
2 | * drivers/amlogic/media/common/firmware/firmware.c |
3 | * |
4 | * Copyright (C) 2016 Amlogic, Inc. All rights reserved. |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
14 | * more details. |
15 | * |
16 | */ |
17 | #include <linux/kernel.h> |
18 | #include <linux/module.h> |
19 | #include <linux/types.h> |
20 | #include <linux/fs.h> |
21 | #include <linux/init.h> |
22 | #include <linux/device.h> |
23 | #include <linux/vmalloc.h> |
24 | #include <linux/mm.h> |
25 | #include <linux/slab.h> |
26 | |
27 | #include <linux/amlogic/media/utils/vformat.h> |
28 | #include <linux/amlogic/cpu_version.h> |
29 | #include "../../stream_input/amports/amports_priv.h" |
30 | #include "../../frame_provider/decoder/utils/vdec.h" |
31 | #include "firmware_priv.h" |
32 | #include "../chips/chips.h" |
33 | #include <linux/string.h> |
34 | #include <linux/amlogic/media/utils/log.h> |
35 | #include <linux/firmware.h> |
36 | #include <linux/amlogic/tee.h> |
37 | #include <linux/amlogic/major.h> |
38 | #include <linux/cdev.h> |
39 | #include <linux/crc32.h> |
40 | #include "../chips/decoder_cpu_ver_info.h" |
41 | |
42 | /* major.minor */ |
43 | #define PACK_VERS "v0.2" |
44 | |
45 | #define CLASS_NAME "firmware_codec" |
46 | #define DEV_NAME "firmware_vdec" |
47 | #define DIR "video" |
48 | #define FRIMWARE_SIZE (64 * 1024) /*64k*/ |
49 | #define BUFF_SIZE (1024 * 1024 * 2) |
50 | |
51 | #define FW_LOAD_FORCE (0x1) |
52 | #define FW_LOAD_TRY (0X2) |
53 | |
54 | /*the first 256 bytes are signature data*/ |
55 | #define SEC_OFFSET (256) |
56 | |
57 | #define TRY_PARSE_MAX (256) |
58 | |
59 | #define PACK ('P' << 24 | 'A' << 16 | 'C' << 8 | 'K') |
60 | #define CODE ('C' << 24 | 'O' << 16 | 'D' << 8 | 'E') |
61 | |
62 | #ifndef FIRMWARE_MAJOR |
63 | #define FIRMWARE_MAJOR AMSTREAM_MAJOR |
64 | #endif |
65 | |
66 | static DEFINE_MUTEX(mutex); |
67 | |
68 | static struct ucode_file_info_s ucode_info[] = { |
69 | #include "firmware_cfg.h" |
70 | }; |
71 | |
72 | static const struct file_operations fw_fops = { |
73 | .owner = THIS_MODULE |
74 | }; |
75 | |
76 | struct fw_mgr_s *g_mgr; |
77 | struct fw_dev_s *g_dev; |
78 | |
79 | static u32 debug; |
80 | static u32 detail; |
81 | |
82 | int get_firmware_data(unsigned int format, char *buf) |
83 | { |
84 | int data_len, ret = -1; |
85 | struct fw_mgr_s *mgr = g_mgr; |
86 | struct fw_info_s *info; |
87 | |
88 | pr_info("[%s], the fw (%s) will be loaded.\n", |
89 | tee_enabled() ? "TEE" : "LOCAL", |
90 | get_fw_format_name(format)); |
91 | |
92 | if (tee_enabled()) |
93 | return 0; |
94 | |
95 | mutex_lock(&mutex); |
96 | |
97 | if (list_empty(&mgr->fw_head)) { |
98 | pr_info("the info list is empty.\n"); |
99 | goto out; |
100 | } |
101 | |
102 | list_for_each_entry(info, &mgr->fw_head, node) { |
103 | if (format != info->format) |
104 | continue; |
105 | |
106 | data_len = info->data->head.data_size; |
107 | memcpy(buf, info->data->data, data_len); |
108 | ret = data_len; |
109 | |
110 | break; |
111 | } |
112 | out: |
113 | mutex_unlock(&mutex); |
114 | |
115 | return ret; |
116 | } |
117 | EXPORT_SYMBOL(get_firmware_data); |
118 | |
119 | int get_data_from_name(const char *name, char *buf) |
120 | { |
121 | int data_len, ret = -1; |
122 | struct fw_mgr_s *mgr = g_mgr; |
123 | struct fw_info_s *info; |
124 | char *fw_name = __getname(); |
125 | |
126 | if (fw_name == NULL) |
127 | return -ENOMEM; |
128 | |
129 | strcat(fw_name, name); |
130 | strcat(fw_name, ".bin"); |
131 | |
132 | mutex_lock(&mutex); |
133 | |
134 | if (list_empty(&mgr->fw_head)) { |
135 | pr_info("the info list is empty.\n"); |
136 | goto out; |
137 | } |
138 | |
139 | list_for_each_entry(info, &mgr->fw_head, node) { |
140 | if (strcmp(fw_name, info->name)) |
141 | continue; |
142 | |
143 | data_len = info->data->head.data_size; |
144 | memcpy(buf, info->data->data, data_len); |
145 | ret = data_len; |
146 | |
147 | break; |
148 | } |
149 | out: |
150 | mutex_unlock(&mutex); |
151 | |
152 | __putname(fw_name); |
153 | |
154 | return ret; |
155 | } |
156 | EXPORT_SYMBOL(get_data_from_name); |
157 | |
158 | static int fw_probe(char *buf) |
159 | { |
160 | int magic = 0; |
161 | |
162 | memcpy(&magic, buf, sizeof(int)); |
163 | return magic; |
164 | } |
165 | |
166 | static int request_firmware_from_sys(const char *file_name, |
167 | char *buf, int size) |
168 | { |
169 | int ret = -1; |
170 | const struct firmware *fw; |
171 | int magic, offset = 0; |
172 | |
173 | pr_info("Try to load %s ...\n", file_name); |
174 | |
175 | ret = request_firmware(&fw, file_name, g_dev->dev); |
176 | if (ret < 0) { |
177 | pr_info("Error : %d can't load the %s.\n", ret, file_name); |
178 | goto err; |
179 | } |
180 | |
181 | if (fw->size > size) { |
182 | pr_info("Not enough memory size for ucode.\n"); |
183 | ret = -ENOMEM; |
184 | goto release; |
185 | } |
186 | |
187 | magic = fw_probe((char *)fw->data); |
188 | if (magic != PACK && magic != CODE) { |
189 | if (fw->size < SEC_OFFSET) { |
190 | pr_info("This is an invalid firmware file.\n"); |
191 | goto release; |
192 | } |
193 | |
194 | magic = fw_probe((char *)fw->data + SEC_OFFSET); |
195 | if (magic != PACK) { |
196 | pr_info("The firmware file is not packet.\n"); |
197 | goto release; |
198 | } |
199 | |
200 | offset = SEC_OFFSET; |
201 | } |
202 | |
203 | memcpy(buf, (char *)fw->data + offset, fw->size - offset); |
204 | |
205 | pr_info("load firmware size : %zd, Name : %s.\n", |
206 | fw->size, file_name); |
207 | ret = fw->size; |
208 | release: |
209 | release_firmware(fw); |
210 | err: |
211 | return ret; |
212 | } |
213 | |
214 | int request_decoder_firmware_on_sys(enum vformat_e format, |
215 | const char *file_name, char *buf, int size) |
216 | { |
217 | int ret; |
218 | |
219 | ret = get_data_from_name(file_name, buf); |
220 | if (ret < 0) |
221 | pr_info("Get firmware fail.\n"); |
222 | |
223 | if (ret > size) { |
224 | pr_info("Not enough memory.\n"); |
225 | return -ENOMEM; |
226 | } |
227 | |
228 | return ret; |
229 | } |
230 | int get_decoder_firmware_data(enum vformat_e format, |
231 | const char *file_name, char *buf, int size) |
232 | { |
233 | int ret; |
234 | |
235 | ret = request_decoder_firmware_on_sys(format, file_name, buf, size); |
236 | if (ret < 0) |
237 | pr_info("get_decoder_firmware_data %s for format %d failed!\n", |
238 | file_name, format); |
239 | |
240 | return ret; |
241 | } |
242 | EXPORT_SYMBOL(get_decoder_firmware_data); |
243 | |
244 | static unsigned long fw_mgr_lock(struct fw_mgr_s *mgr) |
245 | { |
246 | unsigned long flags; |
247 | |
248 | spin_lock_irqsave(&mgr->lock, flags); |
249 | return flags; |
250 | } |
251 | |
252 | static void fw_mgr_unlock(struct fw_mgr_s *mgr, unsigned long flags) |
253 | { |
254 | spin_unlock_irqrestore(&mgr->lock, flags); |
255 | } |
256 | |
257 | static void fw_add_info(struct fw_info_s *info) |
258 | { |
259 | unsigned long flags; |
260 | struct fw_mgr_s *mgr = g_mgr; |
261 | |
262 | flags = fw_mgr_lock(mgr); |
263 | list_add(&info->node, &mgr->fw_head); |
264 | fw_mgr_unlock(mgr, flags); |
265 | } |
266 | |
267 | static void fw_del_info(struct fw_info_s *info) |
268 | { |
269 | unsigned long flags; |
270 | struct fw_mgr_s *mgr = g_mgr; |
271 | |
272 | flags = fw_mgr_lock(mgr); |
273 | list_del(&info->node); |
274 | kfree(info); |
275 | fw_mgr_unlock(mgr, flags); |
276 | } |
277 | |
278 | static void fw_info_walk(void) |
279 | { |
280 | struct fw_mgr_s *mgr = g_mgr; |
281 | struct fw_info_s *info; |
282 | |
283 | if (list_empty(&mgr->fw_head)) { |
284 | pr_info("the info list is empty.\n"); |
285 | return; |
286 | } |
287 | |
288 | list_for_each_entry(info, &mgr->fw_head, node) { |
289 | if (IS_ERR_OR_NULL(info->data)) |
290 | continue; |
291 | |
292 | pr_info("name : %s.\n", info->name); |
293 | pr_info("ver : %s.\n", |
294 | info->data->head.version); |
295 | pr_info("crc : 0x%x.\n", |
296 | info->data->head.checksum); |
297 | pr_info("size : %d.\n", |
298 | info->data->head.data_size); |
299 | pr_info("maker: %s.\n", |
300 | info->data->head.maker); |
301 | pr_info("from : %s.\n", info->src_from); |
302 | pr_info("date : %s.\n", |
303 | info->data->head.date); |
304 | if (info->data->head.duplicate) |
305 | pr_info("NOTE : Dup from %s.\n", |
306 | info->data->head.dup_from); |
307 | pr_info("\n"); |
308 | } |
309 | } |
310 | |
311 | static void fw_files_info_walk(void) |
312 | { |
313 | struct fw_mgr_s *mgr = g_mgr; |
314 | struct fw_files_s *files; |
315 | |
316 | if (list_empty(&mgr->files_head)) { |
317 | pr_info("the file list is empty.\n"); |
318 | return; |
319 | } |
320 | |
321 | list_for_each_entry(files, &mgr->files_head, node) { |
322 | pr_info("type : %s.\n", !files->fw_type ? |
323 | "VIDEO_DECODE" : files->fw_type == 1 ? |
324 | "VIDEO_ENCODE" : "VIDEO_MISC"); |
325 | pr_info("from : %s.\n", !files->file_type ? |
326 | "VIDEO_PACKAGE" : "VIDEO_FW_FILE"); |
327 | pr_info("path : %s.\n", files->path); |
328 | pr_info("name : %s.\n\n", files->name); |
329 | } |
330 | } |
331 | |
332 | static ssize_t info_show(struct class *class, |
333 | struct class_attribute *attr, char *buf) |
334 | { |
335 | char *pbuf = buf; |
336 | struct fw_mgr_s *mgr = g_mgr; |
337 | struct fw_info_s *info; |
338 | unsigned int secs = 0; |
339 | struct tm tm; |
340 | |
341 | mutex_lock(&mutex); |
342 | |
343 | if (list_empty(&mgr->fw_head)) { |
344 | pbuf += sprintf(pbuf, "No firmware.\n"); |
345 | goto out; |
346 | } |
347 | |
348 | /* shows version of driver. */ |
349 | pr_info("The driver version is %s\n", PACK_VERS); |
350 | |
351 | list_for_each_entry(info, &mgr->fw_head, node) { |
352 | if (IS_ERR_OR_NULL(info->data)) |
353 | continue; |
354 | |
355 | if (detail) { |
356 | pr_info("%-5s: %s\n", "name", info->name); |
357 | pr_info("%-5s: %s\n", "ver", |
358 | info->data->head.version); |
359 | pr_info("%-5s: 0x%x\n", "sum", |
360 | info->data->head.checksum); |
361 | pr_info("%-5s: %d\n", "size", |
362 | info->data->head.data_size); |
363 | pr_info("%-5s: %s\n", "maker", |
364 | info->data->head.maker); |
365 | pr_info("%-5s: %s\n", "from", |
366 | info->src_from); |
367 | pr_info("%-5s: %s\n\n", "date", |
368 | info->data->head.date); |
369 | continue; |
370 | } |
371 | |
372 | secs = info->data->head.time |
373 | - sys_tz.tz_minuteswest * 60; |
374 | time_to_tm(secs, 0, &tm); |
375 | |
376 | pr_info("%s %-16s, %02d:%02d:%02d %d/%d/%ld, %s %-8s, %s %-8s, %s %s\n", |
377 | "fmt:", info->data->head.format, |
378 | tm.tm_hour, tm.tm_min, tm.tm_sec, |
379 | tm.tm_mon + 1, tm.tm_mday, tm.tm_year + 1900, |
380 | "cmtid:", info->data->head.commit, |
381 | "chgid:", info->data->head.change_id, |
382 | "mk:", info->data->head.maker); |
383 | } |
384 | out: |
385 | mutex_unlock(&mutex); |
386 | |
387 | return pbuf - buf; |
388 | } |
389 | |
390 | static ssize_t info_store(struct class *cls, |
391 | struct class_attribute *attr, const char *buf, size_t count) |
392 | { |
393 | if (kstrtoint(buf, 0, &detail) < 0) |
394 | return -EINVAL; |
395 | |
396 | return count; |
397 | } |
398 | |
399 | static int fw_info_fill(void) |
400 | { |
401 | int ret = 0, i, len; |
402 | struct fw_mgr_s *mgr = g_mgr; |
403 | struct fw_files_s *files; |
404 | int info_size = ARRAY_SIZE(ucode_info); |
405 | char *path = __getname(); |
406 | const char *name; |
407 | |
408 | if (path == NULL) |
409 | return -ENOMEM; |
410 | |
411 | for (i = 0; i < info_size; i++) { |
412 | name = ucode_info[i].name; |
413 | if (IS_ERR_OR_NULL(name)) |
414 | break; |
415 | |
416 | len = snprintf(path, PATH_MAX, "%s/%s", DIR, |
417 | ucode_info[i].name); |
418 | if (len >= PATH_MAX) |
419 | continue; |
420 | |
421 | files = kzalloc(sizeof(struct fw_files_s), GFP_KERNEL); |
422 | if (files == NULL) { |
423 | __putname(path); |
424 | return -ENOMEM; |
425 | } |
426 | |
427 | files->file_type = ucode_info[i].file_type; |
428 | files->fw_type = ucode_info[i].fw_type; |
429 | strncpy(files->path, path, sizeof(files->path)); |
430 | files->path[sizeof(files->path) - 1] = '\0'; |
431 | strncpy(files->name, name, sizeof(files->name)); |
432 | files->name[sizeof(files->name) - 1] = '\0'; |
433 | |
434 | list_add(&files->node, &mgr->files_head); |
435 | } |
436 | |
437 | __putname(path); |
438 | |
439 | if (debug) |
440 | fw_files_info_walk(); |
441 | |
442 | return ret; |
443 | } |
444 | |
445 | static int fw_data_check_sum(struct firmware_s *fw) |
446 | { |
447 | unsigned int crc; |
448 | |
449 | crc = crc32_le(~0U, fw->data, fw->head.data_size); |
450 | |
451 | /*pr_info("firmware crc result : 0x%x\n", crc ^ ~0U);*/ |
452 | |
453 | return fw->head.checksum != (crc ^ ~0U) ? 0 : 1; |
454 | } |
455 | |
456 | static int fw_data_filter(struct firmware_s *fw, |
457 | struct fw_info_s *fw_info) |
458 | { |
459 | struct fw_mgr_s *mgr = g_mgr; |
460 | struct fw_info_s *info, *tmp; |
461 | int cpu = fw_get_cpu(fw->head.cpu); |
462 | |
463 | if (mgr->cur_cpu < cpu) { |
464 | kfree(fw_info); |
465 | kfree(fw); |
466 | return -1; |
467 | } |
468 | |
469 | /* the encode fw need to ignoring filtering rules. */ |
470 | if (fw_info->format == FIRMWARE_MAX) |
471 | return 0; |
472 | |
473 | list_for_each_entry_safe(info, tmp, &mgr->fw_head, node) { |
474 | if (info->format != fw_info->format) |
475 | continue; |
476 | |
477 | if (IS_ERR_OR_NULL(info->data)) { |
478 | fw_del_info(info); |
479 | return 0; |
480 | } |
481 | |
482 | /* high priority of VIDEO_FW_FILE */ |
483 | if (info->file_type == VIDEO_FW_FILE) { |
484 | pr_info("the %s need to priority proc.\n",info->name); |
485 | kfree(fw_info); |
486 | kfree(fw); |
487 | return 1; |
488 | } |
489 | |
490 | /* the cpu ver is lower and needs to be filtered */ |
491 | if (cpu < fw_get_cpu(info->data->head.cpu)) { |
492 | if (debug) |
493 | pr_info("keep the newer fw (%s) and ignore the older fw (%s).\n", |
494 | info->name, fw_info->name); |
495 | kfree(fw_info); |
496 | kfree(fw); |
497 | return 1; |
498 | } |
499 | |
500 | /* removes not match fw from info list */ |
501 | if (debug) |
502 | pr_info("drop the old fw (%s) will be load the newer fw (%s).\n", |
503 | info->name, fw_info->name); |
504 | kfree(info->data); |
505 | fw_del_info(info); |
506 | } |
507 | |
508 | return 0; |
509 | } |
510 | |
511 | static int fw_replace_dup_data(char *buf) |
512 | { |
513 | int ret = 0; |
514 | struct fw_mgr_s *mgr = g_mgr; |
515 | struct package_s *pkg = |
516 | (struct package_s *) buf; |
517 | struct package_info_s *pinfo = |
518 | (struct package_info_s *) pkg->data; |
519 | struct fw_info_s *info = NULL; |
520 | char *pdata = pkg->data; |
521 | int try_cnt = TRY_PARSE_MAX; |
522 | |
523 | do { |
524 | if (!pinfo->head.length) |
525 | break; |
526 | list_for_each_entry(info, &mgr->fw_head, node) { |
527 | struct firmware_s *comp = NULL; |
528 | struct firmware_s *data = NULL; |
529 | int len = 0; |
530 | |
531 | comp = (struct firmware_s *)pinfo->data; |
532 | if (comp->head.duplicate) |
533 | break; |
534 | |
535 | if (!info->data->head.duplicate || |
536 | comp->head.checksum != |
537 | info->data->head.checksum) |
538 | continue; |
539 | |
540 | len = pinfo->head.length; |
541 | data = kzalloc(len, GFP_KERNEL); |
542 | if (data == NULL) { |
543 | ret = -ENOMEM; |
544 | goto out; |
545 | } |
546 | |
547 | memcpy(data, pinfo->data, len); |
548 | memcpy(data, info->data, sizeof(*data)); |
549 | |
550 | kfree(info->data); |
551 | info->data = data; |
552 | } |
553 | pdata += (pinfo->head.length + sizeof(*pinfo)); |
554 | pinfo = (struct package_info_s *)pdata; |
555 | } while (try_cnt--); |
556 | out: |
557 | return ret; |
558 | } |
559 | |
560 | static int fw_check_pack_version(char *buf) |
561 | { |
562 | struct package_s *pack = NULL; |
563 | int major, minor, major_fw, minor_fw; |
564 | int ret; |
565 | |
566 | pack = (struct package_s *) buf; |
567 | ret = sscanf(PACK_VERS, "v%x.%x", &major, &minor); |
568 | if (ret != 2) |
569 | return -1; |
570 | |
571 | major_fw = (pack->head.version >> 16) & 0xff; |
572 | minor_fw = pack->head.version & 0xff; |
573 | |
574 | if (major < major_fw) { |
575 | pr_info("the pack ver v%d.%d too higher to unsupport.\n", |
576 | major_fw, minor_fw); |
577 | return -1; |
578 | } |
579 | |
580 | if (minor < minor_fw) { |
581 | pr_info("The fw driver version (v%d.%d) is lower than the pkg version (v%d.%d).\n", |
582 | major, minor, major_fw, minor_fw); |
583 | pr_info("The driver version is too low that may affect the work please update asap.\n"); |
584 | } |
585 | |
586 | if (debug) { |
587 | pr_info("The package has %d fws totally.\n", pack->head.total); |
588 | pr_info("The driver ver is v%d.%d\n", major, minor); |
589 | pr_info("The firmware ver is v%d.%d\n", major_fw, minor_fw); |
590 | } |
591 | |
592 | return 0; |
593 | } |
594 | |
595 | static int fw_package_parse(struct fw_files_s *files, |
596 | char *buf, int size) |
597 | { |
598 | int ret = 0; |
599 | struct package_info_s *pack_info; |
600 | struct fw_info_s *info; |
601 | struct firmware_s *data; |
602 | char *pack_data; |
603 | int info_len, len; |
604 | int try_cnt = TRY_PARSE_MAX; |
605 | char *path = __getname(); |
606 | |
607 | if (path == NULL) |
608 | return -ENOMEM; |
609 | |
610 | pack_data = ((struct package_s *)buf)->data; |
611 | pack_info = (struct package_info_s *)pack_data; |
612 | info_len = sizeof(struct package_info_s); |
613 | |
614 | do { |
615 | if (!pack_info->head.length) |
616 | break; |
617 | |
618 | len = snprintf(path, PATH_MAX, "%s/%s", DIR, |
619 | pack_info->head.name); |
620 | if (len >= PATH_MAX) |
621 | continue; |
622 | |
623 | info = kzalloc(sizeof(struct fw_info_s), GFP_KERNEL); |
624 | if (info == NULL) { |
625 | ret = -ENOMEM; |
626 | goto out; |
627 | } |
628 | |
629 | data = kzalloc(FRIMWARE_SIZE, GFP_KERNEL); |
630 | if (data == NULL) { |
631 | kfree(info); |
632 | ret = -ENOMEM; |
633 | goto out; |
634 | } |
635 | |
636 | info->file_type = files->file_type; |
637 | strncpy(info->src_from, files->name, |
638 | sizeof(info->src_from)); |
639 | info->src_from[sizeof(info->src_from) - 1] = '\0'; |
640 | strncpy(info->name, pack_info->head.name, |
641 | sizeof(info->name)); |
642 | info->name[sizeof(info->name) - 1] = '\0'; |
643 | info->format = get_fw_format(pack_info->head.format); |
644 | |
645 | len = pack_info->head.length; |
646 | memcpy(data, pack_info->data, len); |
647 | |
648 | pack_data += (pack_info->head.length + info_len); |
649 | pack_info = (struct package_info_s *)pack_data; |
650 | |
651 | if (!data->head.duplicate && |
652 | !fw_data_check_sum(data)) { |
653 | pr_info("check sum fail !\n"); |
654 | kfree(data); |
655 | kfree(info); |
656 | goto out; |
657 | } |
658 | |
659 | if (fw_data_filter(data, info)) |
660 | continue; |
661 | |
662 | if (debug) |
663 | pr_info("adds %s to the fw list.\n", info->name); |
664 | |
665 | info->data = data; |
666 | fw_add_info(info); |
667 | } while (try_cnt--); |
668 | |
669 | /* process the fw of dup attribute. */ |
670 | ret = fw_replace_dup_data(buf); |
671 | if (ret) |
672 | pr_err("replace dup fw failed.\n"); |
673 | out: |
674 | __putname(path); |
675 | |
676 | return ret; |
677 | } |
678 | |
679 | static int fw_code_parse(struct fw_files_s *files, |
680 | char *buf, int size) |
681 | { |
682 | struct fw_info_s *info; |
683 | |
684 | info = kzalloc(sizeof(struct fw_info_s), GFP_KERNEL); |
685 | if (info == NULL) |
686 | return -ENOMEM; |
687 | |
688 | info->data = kzalloc(FRIMWARE_SIZE, GFP_KERNEL); |
689 | if (info->data == NULL) { |
690 | kfree(info); |
691 | return -ENOMEM; |
692 | } |
693 | |
694 | info->file_type = files->file_type; |
695 | strncpy(info->src_from, files->name, |
696 | sizeof(info->src_from)); |
697 | info->src_from[sizeof(info->src_from) - 1] = '\0'; |
698 | memcpy(info->data, buf, size); |
699 | |
700 | if (!fw_data_check_sum(info->data)) { |
701 | pr_info("check sum fail !\n"); |
702 | kfree(info->data); |
703 | kfree(info); |
704 | return -1; |
705 | } |
706 | |
707 | if (debug) |
708 | pr_info("adds %s to the fw list.\n", info->name); |
709 | |
710 | fw_add_info(info); |
711 | |
712 | return 0; |
713 | } |
714 | |
715 | static int get_firmware_from_sys(const char *path, |
716 | char *buf, int size) |
717 | { |
718 | int len = 0; |
719 | |
720 | len = request_firmware_from_sys(path, buf, size); |
721 | if (len < 0) |
722 | pr_info("get data from fsys fail.\n"); |
723 | |
724 | return len; |
725 | } |
726 | |
727 | static int fw_data_binding(void) |
728 | { |
729 | int ret = 0, magic = 0; |
730 | struct fw_mgr_s *mgr = g_mgr; |
731 | struct fw_files_s *files, *tmp; |
732 | char *buf = NULL; |
733 | int size; |
734 | |
735 | if (list_empty(&mgr->files_head)) { |
736 | pr_info("the file list is empty.\n"); |
737 | return 0; |
738 | } |
739 | |
740 | buf = vmalloc(BUFF_SIZE); |
741 | if (IS_ERR_OR_NULL(buf)) |
742 | return -ENOMEM; |
743 | |
744 | memset(buf, 0, BUFF_SIZE); |
745 | |
746 | list_for_each_entry_safe(files, tmp, &mgr->files_head, node) { |
747 | size = get_firmware_from_sys(files->path, buf, BUFF_SIZE); |
748 | magic = fw_probe(buf); |
749 | |
750 | if (files->file_type == VIDEO_PACKAGE && magic == PACK) { |
751 | if (!fw_check_pack_version(buf)) |
752 | ret = fw_package_parse(files, buf, size); |
753 | } else if (files->file_type == VIDEO_FW_FILE && magic == CODE) { |
754 | ret = fw_code_parse(files, buf, size); |
755 | } else { |
756 | list_del(&files->node); |
757 | kfree(files); |
758 | pr_info("invaild file type.\n"); |
759 | } |
760 | |
761 | memset(buf, 0, BUFF_SIZE); |
762 | } |
763 | |
764 | if (debug) |
765 | fw_info_walk(); |
766 | |
767 | vfree(buf); |
768 | |
769 | return ret; |
770 | } |
771 | |
772 | static int fw_pre_load(void) |
773 | { |
774 | if (fw_info_fill() < 0) { |
775 | pr_info("Get path fail.\n"); |
776 | return -1; |
777 | } |
778 | |
779 | if (fw_data_binding() < 0) { |
780 | pr_info("Set data fail.\n"); |
781 | return -1; |
782 | } |
783 | |
784 | return 0; |
785 | } |
786 | |
787 | static int fw_mgr_init(void) |
788 | { |
789 | g_mgr = kzalloc(sizeof(struct fw_mgr_s), GFP_KERNEL); |
790 | if (IS_ERR_OR_NULL(g_mgr)) |
791 | return -ENOMEM; |
792 | |
793 | g_mgr->cur_cpu = get_cpu_major_id(); |
794 | INIT_LIST_HEAD(&g_mgr->files_head); |
795 | INIT_LIST_HEAD(&g_mgr->fw_head); |
796 | spin_lock_init(&g_mgr->lock); |
797 | |
798 | return 0; |
799 | } |
800 | |
801 | static void fw_ctx_clean(void) |
802 | { |
803 | struct fw_mgr_s *mgr = g_mgr; |
804 | struct fw_files_s *files; |
805 | struct fw_info_s *info; |
806 | unsigned long flags; |
807 | |
808 | flags = fw_mgr_lock(mgr); |
809 | while (!list_empty(&mgr->files_head)) { |
810 | files = list_entry(mgr->files_head.next, |
811 | struct fw_files_s, node); |
812 | list_del(&files->node); |
813 | kfree(files); |
814 | } |
815 | |
816 | while (!list_empty(&mgr->fw_head)) { |
817 | info = list_entry(mgr->fw_head.next, |
818 | struct fw_info_s, node); |
819 | list_del(&info->node); |
820 | kfree(info->data); |
821 | kfree(info); |
822 | } |
823 | fw_mgr_unlock(mgr, flags); |
824 | } |
825 | |
826 | int video_fw_reload(int mode) |
827 | { |
828 | int ret = 0; |
829 | struct fw_mgr_s *mgr = g_mgr; |
830 | |
831 | if (tee_enabled()) |
832 | return 0; |
833 | |
834 | mutex_lock(&mutex); |
835 | |
836 | if (mode & FW_LOAD_FORCE) { |
837 | fw_ctx_clean(); |
838 | |
839 | ret = fw_pre_load(); |
840 | if (ret < 0) |
841 | pr_err("The fw reload fail.\n"); |
842 | } else if (mode & FW_LOAD_TRY) { |
843 | if (!list_empty(&mgr->fw_head)) { |
844 | pr_info("The fw has been loaded.\n"); |
845 | goto out; |
846 | } |
847 | |
848 | ret = fw_pre_load(); |
849 | if (ret < 0) |
850 | pr_err("The fw try to reload fail.\n"); |
851 | } |
852 | out: |
853 | mutex_unlock(&mutex); |
854 | |
855 | return ret; |
856 | } |
857 | EXPORT_SYMBOL(video_fw_reload); |
858 | |
859 | static ssize_t reload_show(struct class *class, |
860 | struct class_attribute *attr, char *buf) |
861 | { |
862 | char *pbuf = buf; |
863 | |
864 | pbuf += sprintf(pbuf, "The fw reload usage.\n"); |
865 | pbuf += sprintf(pbuf, "> set 1 means that the fw is forced to update\n"); |
866 | pbuf += sprintf(pbuf, "> set 2 means that the fw is try to reload\n"); |
867 | |
868 | return pbuf - buf; |
869 | } |
870 | |
871 | static ssize_t reload_store(struct class *class, |
872 | struct class_attribute *attr, |
873 | const char *buf, size_t size) |
874 | { |
875 | int ret = -1; |
876 | unsigned int val; |
877 | |
878 | ret = kstrtoint(buf, 0, &val); |
879 | if (ret != 0) |
880 | return -EINVAL; |
881 | |
882 | ret = video_fw_reload(val); |
883 | if (ret < 0) |
884 | pr_err("fw reload fail.\n"); |
885 | |
886 | return size; |
887 | } |
888 | |
889 | static ssize_t debug_show(struct class *cls, |
890 | struct class_attribute *attr, char *buf) |
891 | { |
892 | return sprintf(buf, "%x\n", debug); |
893 | } |
894 | |
895 | static ssize_t debug_store(struct class *cls, |
896 | struct class_attribute *attr, const char *buf, size_t count) |
897 | { |
898 | if (kstrtoint(buf, 0, &debug) < 0) |
899 | return -EINVAL; |
900 | |
901 | return count; |
902 | } |
903 | |
904 | static struct class_attribute fw_class_attrs[] = { |
905 | __ATTR(info, 0664, info_show, info_store), |
906 | __ATTR(reload, 0664, reload_show, reload_store), |
907 | __ATTR(debug, 0664, debug_show, debug_store), |
908 | __ATTR_NULL |
909 | }; |
910 | |
911 | static struct class fw_class = { |
912 | .name = CLASS_NAME, |
913 | .class_attrs = fw_class_attrs, |
914 | }; |
915 | |
916 | static int fw_driver_init(void) |
917 | { |
918 | int ret = -1; |
919 | |
920 | g_dev = kzalloc(sizeof(struct fw_dev_s), GFP_KERNEL); |
921 | if (IS_ERR_OR_NULL(g_dev)) |
922 | return -ENOMEM; |
923 | |
924 | g_dev->dev_no = MKDEV(FIRMWARE_MAJOR, 100); |
925 | |
926 | ret = register_chrdev_region(g_dev->dev_no, 1, DEV_NAME); |
927 | if (ret < 0) { |
928 | pr_info("Can't get major number %d.\n", FIRMWARE_MAJOR); |
929 | goto err; |
930 | } |
931 | |
932 | cdev_init(&g_dev->cdev, &fw_fops); |
933 | g_dev->cdev.owner = THIS_MODULE; |
934 | |
935 | ret = cdev_add(&g_dev->cdev, g_dev->dev_no, 1); |
936 | if (ret) { |
937 | pr_info("Error %d adding cdev fail.\n", ret); |
938 | goto err; |
939 | } |
940 | |
941 | ret = class_register(&fw_class); |
942 | if (ret < 0) { |
943 | pr_info("Failed in creating class.\n"); |
944 | goto err; |
945 | } |
946 | |
947 | g_dev->dev = device_create(&fw_class, NULL, |
948 | g_dev->dev_no, NULL, DEV_NAME); |
949 | if (IS_ERR_OR_NULL(g_dev->dev)) { |
950 | pr_info("Create device failed.\n"); |
951 | ret = -ENODEV; |
952 | goto err; |
953 | } |
954 | |
955 | pr_info("Registered firmware driver success.\n"); |
956 | err: |
957 | return ret; |
958 | } |
959 | |
960 | static void fw_driver_exit(void) |
961 | { |
962 | cdev_del(&g_dev->cdev); |
963 | device_destroy(&fw_class, g_dev->dev_no); |
964 | class_unregister(&fw_class); |
965 | unregister_chrdev_region(g_dev->dev_no, 1); |
966 | kfree(g_dev); |
967 | kfree(g_mgr); |
968 | } |
969 | |
970 | static int __init fw_module_init(void) |
971 | { |
972 | int ret = -1; |
973 | |
974 | ret = fw_driver_init(); |
975 | if (ret) { |
976 | pr_info("Error %d firmware driver init fail.\n", ret); |
977 | goto err; |
978 | } |
979 | |
980 | ret = fw_mgr_init(); |
981 | if (ret) { |
982 | pr_info("Error %d firmware mgr init fail.\n", ret); |
983 | goto err; |
984 | } |
985 | |
986 | ret = fw_pre_load(); |
987 | if (ret) { |
988 | pr_info("Error %d firmware pre load fail.\n", ret); |
989 | goto err; |
990 | } |
991 | err: |
992 | return ret; |
993 | } |
994 | |
995 | static void __exit fw_module_exit(void) |
996 | { |
997 | fw_ctx_clean(); |
998 | fw_driver_exit(); |
999 | pr_info("Firmware driver cleaned up.\n"); |
1000 | } |
1001 | |
1002 | module_init(fw_module_init); |
1003 | module_exit(fw_module_exit); |
1004 | |
1005 | MODULE_LICENSE("GPL"); |
1006 | MODULE_AUTHOR("Nanxin Qin <nanxin.qin@amlogic.com>"); |
1007 |