blob: 9f6f940b7ca1230940a1fcb66c8a47a56ad4a2fb [file] [log] [blame]
Lakshmi Narayana Kalavala85c40352017-05-15 16:19:13 -07001/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define pr_fmt(fmt) "A5-CORE %s:%d " fmt, __func__, __LINE__
14
15#include <linux/slab.h>
16#include <linux/of.h>
17#include <linux/debugfs.h>
18#include <linux/videodev2.h>
19#include <linux/uaccess.h>
20#include <linux/platform_device.h>
21#include <linux/firmware.h>
22#include <linux/delay.h>
23#include <linux/timer.h>
24#include <linux/elf.h>
25#include <media/cam_icp.h>
26#include "cam_io_util.h"
27#include "cam_a5_hw_intf.h"
28#include "cam_hw.h"
29#include "cam_hw_intf.h"
30#include "a5_core.h"
31#include "a5_soc.h"
32#include "cam_soc_util.h"
33#include "cam_io_util.h"
34#include "hfi_intf.h"
35#include "hfi_sys_defs.h"
36#include "cam_icp_hw_mgr_intf.h"
37#include "cam_cpas_api.h"
38
39static int cam_a5_cpas_vote(struct cam_a5_device_core_info *core_info,
40 struct cam_icp_cpas_vote *cpas_vote)
41{
42 int rc = 0;
43
44 if (cpas_vote->ahb_vote_valid)
45 rc = cam_cpas_update_ahb_vote(core_info->cpas_handle,
46 &cpas_vote->ahb_vote);
47
48 if (cpas_vote->axi_vote_valid)
49 rc = cam_cpas_update_axi_vote(core_info->cpas_handle,
50 &cpas_vote->axi_vote);
51
52 if (rc)
53 pr_err("cpas vote is failed: %d\n", rc);
54
55 return rc;
56}
57
58static int32_t cam_icp_validate_fw(const uint8_t *elf)
59{
60 struct elf32_hdr *elf_hdr;
61
62 if (!elf) {
63 pr_err("Invalid params\n");
64 return -EINVAL;
65 }
66
67 elf_hdr = (struct elf32_hdr *)elf;
68
69 if (memcmp(elf_hdr->e_ident, ELFMAG, SELFMAG)) {
70 pr_err("ICP elf identifier is failed\n");
71 return -EINVAL;
72 }
73
74 /* check architecture */
75 if (elf_hdr->e_machine != EM_ARM) {
76 pr_err("unsupported arch\n");
77 return -EINVAL;
78 }
79
80 /* check elf bit format */
81 if (elf_hdr->e_ident[EI_CLASS] != ELFCLASS32) {
82 pr_err("elf doesn't support 32 bit format\n");
83 return -EINVAL;
84 }
85
86 return 0;
87}
88
89static int32_t cam_icp_get_fw_size(const uint8_t *elf, uint32_t *fw_size)
90{
91 int32_t rc = 0;
92 int32_t i = 0;
93 uint32_t num_prg_hdrs;
94 unsigned char *icp_prg_hdr_tbl;
95 uint32_t seg_mem_size = 0;
96 struct elf32_hdr *elf_hdr;
97 struct elf32_phdr *prg_hdr;
98
99 if (!elf || !fw_size) {
100 pr_err("invalid args\n");
101 return -EINVAL;
102 }
103
104 *fw_size = 0;
105
106 elf_hdr = (struct elf32_hdr *)elf;
107 num_prg_hdrs = elf_hdr->e_phnum;
108 icp_prg_hdr_tbl = (unsigned char *)elf + elf_hdr->e_phoff;
109 prg_hdr = (struct elf32_phdr *)&icp_prg_hdr_tbl[0];
110
111 if (!prg_hdr) {
112 pr_err("failed to get elf program header attr\n");
113 return -EINVAL;
114 }
115
116 pr_debug("num_prg_hdrs = %d\n", num_prg_hdrs);
117 for (i = 0; i < num_prg_hdrs; i++, prg_hdr++) {
118 if (prg_hdr->p_flags == 0)
119 continue;
120
121 seg_mem_size = (prg_hdr->p_memsz + prg_hdr->p_align - 1) &
122 ~(prg_hdr->p_align - 1);
123 seg_mem_size += prg_hdr->p_vaddr;
124 pr_debug("p_memsz = %x p_align = %x p_vaddr = %x seg_mem_size = %x\n",
125 (int)prg_hdr->p_memsz, (int)prg_hdr->p_align,
126 (int)prg_hdr->p_vaddr, (int)seg_mem_size);
127 if (*fw_size < seg_mem_size)
128 *fw_size = seg_mem_size;
129
130 }
131
132 if (*fw_size == 0) {
133 pr_err("invalid elf fw file\n");
134 return -EINVAL;
135 }
136
137 return rc;
138}
139
140static int32_t cam_icp_program_fw(const uint8_t *elf,
141 struct cam_a5_device_core_info *core_info)
142{
143 int32_t rc = 0;
144 uint32_t num_prg_hdrs;
145 unsigned char *icp_prg_hdr_tbl;
146 int32_t i = 0;
147 u8 *dest;
148 u8 *src;
149 struct elf32_hdr *elf_hdr;
150 struct elf32_phdr *prg_hdr;
151
152 elf_hdr = (struct elf32_hdr *)elf;
153 num_prg_hdrs = elf_hdr->e_phnum;
154 icp_prg_hdr_tbl = (unsigned char *)elf + elf_hdr->e_phoff;
155 prg_hdr = (struct elf32_phdr *)&icp_prg_hdr_tbl[0];
156
157 if (!prg_hdr) {
158 pr_err("failed to get elf program header attr\n");
159 return -EINVAL;
160 }
161
162 for (i = 0; i < num_prg_hdrs; i++, prg_hdr++) {
163 if (prg_hdr->p_flags == 0)
164 continue;
165
166 pr_debug("Loading FW header size: %u\n", prg_hdr->p_filesz);
167 if (prg_hdr->p_filesz != 0) {
168 src = (u8 *)((u8 *)elf + prg_hdr->p_offset);
169 dest = (u8 *)(((u8 *)core_info->fw_kva_addr) +
170 prg_hdr->p_vaddr);
171
172 memcpy_toio(dest, src, prg_hdr->p_filesz);
173 pr_debug("fw kva: %pK, p_vaddr: 0x%x\n",
174 dest, prg_hdr->p_vaddr);
175 }
176 }
177
178 return rc;
179}
180
181static int32_t cam_a5_download_fw(void *device_priv)
182{
183 int32_t rc = 0;
184 uint32_t fw_size;
185 const uint8_t *fw_start = NULL;
186 struct cam_hw_info *a5_dev = device_priv;
187 struct cam_hw_soc_info *soc_info = NULL;
188 struct cam_a5_device_core_info *core_info = NULL;
189 struct cam_a5_device_hw_info *hw_info = NULL;
190 struct platform_device *pdev = NULL;
191 struct a5_soc_info *cam_a5_soc_info = NULL;
192
193 if (!device_priv) {
194 pr_err("Invalid cam_dev_info\n");
195 return -EINVAL;
196 }
197
198 soc_info = &a5_dev->soc_info;
199 core_info = (struct cam_a5_device_core_info *)a5_dev->core_info;
200 hw_info = core_info->a5_hw_info;
201 pdev = soc_info->pdev;
202 cam_a5_soc_info = soc_info->soc_private;
203
204 rc = request_firmware(&core_info->fw_elf, "CAMERA_ICP.elf", &pdev->dev);
205 pr_debug("request_firmware: %d\n", rc);
206 if (rc < 0) {
207 pr_err("Failed to locate fw\n");
208 return rc;
209 }
210
211 if (!core_info->fw_elf) {
212 pr_err("request_firmware is failed\n");
213 return -EINVAL;
214 }
215
216 fw_start = core_info->fw_elf->data;
217 rc = cam_icp_validate_fw(fw_start);
218 if (rc < 0) {
219 pr_err("fw elf validation failed\n");
220 return -EINVAL;
221 }
222
223 rc = cam_icp_get_fw_size(fw_start, &fw_size);
224 if (rc < 0) {
225 pr_err("unable to get fw file size\n");
226 return rc;
227 }
228 pr_debug("cam_icp_get_fw_size: %u\n", fw_size);
229
230 /* Check FW firmware memory allocation is OK or not */
231 pr_debug("cam_icp_get_fw_size: %u %llu\n",
232 fw_size, core_info->fw_buf_len);
233
234 if (core_info->fw_buf_len < fw_size) {
235 pr_err("fw allocation failed\n");
236 goto fw_alloc_failed;
237 }
238
239 /* download fw */
240 rc = cam_icp_program_fw(fw_start, core_info);
241 if (rc < 0) {
242 pr_err("fw program is failed\n");
243 goto fw_program_failed;
244 }
245
246 return 0;
247fw_program_failed:
248fw_alloc_failed:
249 return rc;
250}
251
252int cam_a5_init_hw(void *device_priv,
253 void *init_hw_args, uint32_t arg_size)
254{
255 struct cam_hw_info *a5_dev = device_priv;
256 struct cam_hw_soc_info *soc_info = NULL;
257 struct cam_a5_device_core_info *core_info = NULL;
258 struct cam_icp_cpas_vote cpas_vote;
259 int rc = 0;
260
261 if (!device_priv) {
262 pr_err("Invalid cam_dev_info\n");
263 return -EINVAL;
264 }
265
266 soc_info = &a5_dev->soc_info;
267 core_info = (struct cam_a5_device_core_info *)a5_dev->core_info;
268
269 if ((!soc_info) || (!core_info)) {
270 pr_err("soc_info = %pK core_info = %pK\n", soc_info, core_info);
271 return -EINVAL;
272 }
273
274 cpas_vote.ahb_vote.type = CAM_VOTE_ABSOLUTE;
Pavan Kumar Chilamkurthi7e7607b2017-06-22 20:02:50 -0700275 cpas_vote.ahb_vote.vote.level = CAM_SVS_VOTE;
Lakshmi Narayana Kalavala85c40352017-05-15 16:19:13 -0700276 cpas_vote.axi_vote.compressed_bw = ICP_TURBO_VOTE;
277 cpas_vote.axi_vote.uncompressed_bw = ICP_TURBO_VOTE;
278
279 rc = cam_cpas_start(core_info->cpas_handle,
280 &cpas_vote.ahb_vote, &cpas_vote.axi_vote);
Lakshmi Narayana Kalavalac2fac452017-06-12 12:38:07 -0700281 if (rc) {
Lakshmi Narayana Kalavala85c40352017-05-15 16:19:13 -0700282 pr_err("cpass start failed: %d\n", rc);
283 return rc;
284 }
Lakshmi Narayana Kalavalac2fac452017-06-12 12:38:07 -0700285 core_info->cpas_start = true;
Lakshmi Narayana Kalavala85c40352017-05-15 16:19:13 -0700286
287 rc = cam_a5_enable_soc_resources(soc_info);
Lakshmi Narayana Kalavalac2fac452017-06-12 12:38:07 -0700288 if (rc) {
289 pr_err("soc enable is failed: %d\n", rc);
290 if (cam_cpas_stop(core_info->cpas_handle))
291 pr_err("cpas stop is failed\n");
292 else
293 core_info->cpas_start = false;
Lakshmi Narayana Kalavala85c40352017-05-15 16:19:13 -0700294 }
295
Lakshmi Narayana Kalavalac2fac452017-06-12 12:38:07 -0700296 return rc;
Lakshmi Narayana Kalavala85c40352017-05-15 16:19:13 -0700297}
298
299int cam_a5_deinit_hw(void *device_priv,
300 void *init_hw_args, uint32_t arg_size)
301{
302 struct cam_hw_info *a5_dev = device_priv;
303 struct cam_hw_soc_info *soc_info = NULL;
304 struct cam_a5_device_core_info *core_info = NULL;
305 int rc = 0;
306
307 if (!device_priv) {
308 pr_err("Invalid cam_dev_info\n");
309 return -EINVAL;
310 }
311
312 soc_info = &a5_dev->soc_info;
313 core_info = (struct cam_a5_device_core_info *)a5_dev->core_info;
314 if ((!soc_info) || (!core_info)) {
315 pr_err("soc_info = %pK core_info = %pK\n", soc_info, core_info);
316 return -EINVAL;
317 }
318
319 rc = cam_a5_disable_soc_resources(soc_info);
Lakshmi Narayana Kalavalac2fac452017-06-12 12:38:07 -0700320 if (rc)
321 pr_err("soc disable is failed: %d\n", rc);
Lakshmi Narayana Kalavala85c40352017-05-15 16:19:13 -0700322
Lakshmi Narayana Kalavalac2fac452017-06-12 12:38:07 -0700323 if (core_info->cpas_start) {
324 if (cam_cpas_stop(core_info->cpas_handle))
325 pr_err("cpas stop is failed\n");
326 else
327 core_info->cpas_start = false;
328 }
Lakshmi Narayana Kalavala85c40352017-05-15 16:19:13 -0700329
Lakshmi Narayana Kalavalac2fac452017-06-12 12:38:07 -0700330 return rc;
Lakshmi Narayana Kalavala85c40352017-05-15 16:19:13 -0700331}
332
333irqreturn_t cam_a5_irq(int irq_num, void *data)
334{
335 struct cam_hw_info *a5_dev = data;
336 struct cam_hw_soc_info *soc_info = NULL;
337 struct cam_a5_device_core_info *core_info = NULL;
338 struct cam_a5_device_hw_info *hw_info = NULL;
339 uint32_t irq_status = 0;
340
341 if (!data) {
342 pr_err("Invalid cam_dev_info or query_cap args\n");
343 return IRQ_HANDLED;
344 }
345
346 soc_info = &a5_dev->soc_info;
347 core_info = (struct cam_a5_device_core_info *)a5_dev->core_info;
348 hw_info = core_info->a5_hw_info;
349
350 irq_status = cam_io_r_mb(soc_info->reg_map[A5_SIERRA_BASE].mem_base +
351 core_info->a5_hw_info->a5_host_int_status);
352
353 cam_io_w_mb(irq_status,
354 soc_info->reg_map[A5_SIERRA_BASE].mem_base +
355 core_info->a5_hw_info->a5_host_int_clr);
356
357 pr_debug("irq_status = %x\n", irq_status);
358 if (irq_status & A5_HOST_INT)
359 pr_debug("A5 to Host interrupt, read msg Q\n");
360
361 if ((irq_status & A5_WDT_0) ||
362 (irq_status & A5_WDT_1)) {
363 pr_err_ratelimited("watch dog interrupt from A5\n");
364 }
365
366 if (core_info->irq_cb.icp_hw_mgr_cb)
367 core_info->irq_cb.icp_hw_mgr_cb(irq_status,
368 core_info->irq_cb.data);
369 return IRQ_HANDLED;
370}
371
372int cam_a5_process_cmd(void *device_priv, uint32_t cmd_type,
373 void *cmd_args, uint32_t arg_size)
374{
375 struct cam_hw_info *a5_dev = device_priv;
376 struct cam_hw_soc_info *soc_info = NULL;
377 struct cam_a5_device_core_info *core_info = NULL;
378 struct cam_a5_device_hw_info *hw_info = NULL;
379 int rc = 0;
380
381 if (!device_priv) {
382 pr_err("Invalid arguments\n");
383 return -EINVAL;
384 }
385
386 if (cmd_type >= CAM_ICP_A5_CMD_MAX) {
387 pr_err("Invalid command : %x\n", cmd_type);
388 return -EINVAL;
389 }
390
391 soc_info = &a5_dev->soc_info;
392 core_info = (struct cam_a5_device_core_info *)a5_dev->core_info;
393 hw_info = core_info->a5_hw_info;
394
395 switch (cmd_type) {
396 case CAM_ICP_A5_CMD_FW_DOWNLOAD:
397 rc = cam_a5_download_fw(device_priv);
398
399 break;
400 case CAM_ICP_A5_CMD_SET_FW_BUF: {
401 struct cam_icp_a5_set_fw_buf_info *fw_buf_info = cmd_args;
402
403 if (!cmd_args) {
404 pr_err("cmd args NULL\n");
405 return -EINVAL;
406 }
407
408 core_info->fw_buf = fw_buf_info->iova;
409 core_info->fw_kva_addr = fw_buf_info->kva;
410 core_info->fw_buf_len = fw_buf_info->len;
411
412 pr_debug("fw buf info = %x %llx %lld\n", core_info->fw_buf,
413 core_info->fw_kva_addr, core_info->fw_buf_len);
414 break;
415 }
416 case CAM_ICP_A5_SET_IRQ_CB: {
417 struct cam_icp_a5_set_irq_cb *irq_cb = cmd_args;
418
419 if (!cmd_args) {
420 pr_err("cmd args NULL\n");
421 return -EINVAL;
422 }
423
424 core_info->irq_cb.icp_hw_mgr_cb = irq_cb->icp_hw_mgr_cb;
425 core_info->irq_cb.data = irq_cb->data;
426 break;
427 }
428
429 case CAM_ICP_A5_SEND_INIT:
430 hfi_send_system_cmd(HFI_CMD_SYS_INIT, 0, 0);
431 break;
432 case CAM_ICP_A5_CMD_VOTE_CPAS: {
433 struct cam_icp_cpas_vote *cpas_vote = cmd_args;
434
435 if (!cmd_args) {
436 pr_err("cmd args NULL\n");
437 return -EINVAL;
438 }
439
440 cam_a5_cpas_vote(core_info, cpas_vote);
441 break;
442 }
443
444 case CAM_ICP_A5_CMD_CPAS_START: {
445 struct cam_icp_cpas_vote *cpas_vote = cmd_args;
446
447 if (!cmd_args) {
448 pr_err("cmd args NULL\n");
449 return -EINVAL;
450 }
451
Lakshmi Narayana Kalavalac2fac452017-06-12 12:38:07 -0700452 if (!core_info->cpas_start) {
453 rc = cam_cpas_start(core_info->cpas_handle,
454 &cpas_vote->ahb_vote,
455 &cpas_vote->axi_vote);
456 core_info->cpas_start = true;
457 }
Lakshmi Narayana Kalavala85c40352017-05-15 16:19:13 -0700458 break;
459 }
460
461 case CAM_ICP_A5_CMD_CPAS_STOP:
Lakshmi Narayana Kalavalac2fac452017-06-12 12:38:07 -0700462 if (core_info->cpas_start) {
463 cam_cpas_stop(core_info->cpas_handle);
464 core_info->cpas_start = false;
465 }
Lakshmi Narayana Kalavala85c40352017-05-15 16:19:13 -0700466 break;
467 default:
468 break;
469 }
470
471 return rc;
472}