Commit | Line | Data |
---|---|---|
3003a180 ACC |
1 | /* |
2 | * Copyright (c) 2016 MediaTek Inc. | |
3 | * Author: Andrew-CT Chen <andrew-ct.chen@mediatek.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | */ | |
14 | #include <linux/clk.h> | |
15 | #include <linux/debugfs.h> | |
16 | #include <linux/firmware.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/iommu.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/of_address.h> | |
21 | #include <linux/of_irq.h> | |
22 | #include <linux/of_platform.h> | |
23 | #include <linux/of_reserved_mem.h> | |
24 | #include <linux/sched.h> | |
25 | #include <linux/sizes.h> | |
26 | ||
27 | #include "mtk_vpu.h" | |
28 | ||
29 | /** | |
30 | * VPU (video processor unit) is a tiny processor controlling video hardware | |
31 | * related to video codec, scaling and color format converting. | |
32 | * VPU interfaces with other blocks by share memory and interrupt. | |
33 | **/ | |
34 | ||
35 | #define INIT_TIMEOUT_MS 2000U | |
36 | #define IPI_TIMEOUT_MS 2000U | |
37 | #define VPU_FW_VER_LEN 16 | |
38 | ||
39 | /* maximum program/data TCM (Tightly-Coupled Memory) size */ | |
40 | #define VPU_PTCM_SIZE (96 * SZ_1K) | |
41 | #define VPU_DTCM_SIZE (32 * SZ_1K) | |
42 | /* the offset to get data tcm address */ | |
43 | #define VPU_DTCM_OFFSET 0x18000UL | |
44 | /* daynamic allocated maximum extended memory size */ | |
45 | #define VPU_EXT_P_SIZE SZ_1M | |
46 | #define VPU_EXT_D_SIZE SZ_4M | |
47 | /* maximum binary firmware size */ | |
48 | #define VPU_P_FW_SIZE (VPU_PTCM_SIZE + VPU_EXT_P_SIZE) | |
49 | #define VPU_D_FW_SIZE (VPU_DTCM_SIZE + VPU_EXT_D_SIZE) | |
50 | /* the size of share buffer between Host and VPU */ | |
51 | #define SHARE_BUF_SIZE 48 | |
52 | ||
53 | /* binary firmware name */ | |
54 | #define VPU_P_FW "vpu_p.bin" | |
55 | #define VPU_D_FW "vpu_d.bin" | |
56 | ||
57 | #define VPU_RESET 0x0 | |
58 | #define VPU_TCM_CFG 0x0008 | |
59 | #define VPU_PMEM_EXT0_ADDR 0x000C | |
60 | #define VPU_PMEM_EXT1_ADDR 0x0010 | |
61 | #define VPU_TO_HOST 0x001C | |
62 | #define VPU_DMEM_EXT0_ADDR 0x0014 | |
63 | #define VPU_DMEM_EXT1_ADDR 0x0018 | |
64 | #define HOST_TO_VPU 0x0024 | |
65 | #define VPU_PC_REG 0x0060 | |
66 | #define VPU_WDT_REG 0x0084 | |
67 | ||
68 | /* vpu inter-processor communication interrupt */ | |
69 | #define VPU_IPC_INT BIT(8) | |
70 | ||
71 | /** | |
72 | * enum vpu_fw_type - VPU firmware type | |
73 | * | |
74 | * @P_FW: program firmware | |
75 | * @D_FW: data firmware | |
76 | * | |
77 | */ | |
78 | enum vpu_fw_type { | |
79 | P_FW, | |
80 | D_FW, | |
81 | }; | |
82 | ||
83 | /** | |
84 | * struct vpu_mem - VPU extended program/data memory information | |
85 | * | |
86 | * @va: the kernel virtual memory address of VPU extended memory | |
87 | * @pa: the physical memory address of VPU extended memory | |
88 | * | |
89 | */ | |
90 | struct vpu_mem { | |
91 | void *va; | |
92 | dma_addr_t pa; | |
93 | }; | |
94 | ||
95 | /** | |
96 | * struct vpu_regs - VPU TCM and configuration registers | |
97 | * | |
98 | * @tcm: the register for VPU Tightly-Coupled Memory | |
99 | * @cfg: the register for VPU configuration | |
100 | * @irq: the irq number for VPU interrupt | |
101 | */ | |
102 | struct vpu_regs { | |
103 | void __iomem *tcm; | |
104 | void __iomem *cfg; | |
105 | int irq; | |
106 | }; | |
107 | ||
108 | /** | |
109 | * struct vpu_wdt_handler - VPU watchdog reset handler | |
110 | * | |
111 | * @reset_func: reset handler | |
112 | * @priv: private data | |
113 | */ | |
114 | struct vpu_wdt_handler { | |
115 | void (*reset_func)(void *); | |
116 | void *priv; | |
117 | }; | |
118 | ||
119 | /** | |
120 | * struct vpu_wdt - VPU watchdog workqueue | |
121 | * | |
122 | * @handler: VPU watchdog reset handler | |
123 | * @ws: workstruct for VPU watchdog | |
124 | * @wq: workqueue for VPU watchdog | |
125 | */ | |
126 | struct vpu_wdt { | |
127 | struct vpu_wdt_handler handler[VPU_RST_MAX]; | |
128 | struct work_struct ws; | |
129 | struct workqueue_struct *wq; | |
130 | }; | |
131 | ||
132 | /** | |
133 | * struct vpu_run - VPU initialization status | |
134 | * | |
135 | * @signaled: the signal of vpu initialization completed | |
136 | * @fw_ver: VPU firmware version | |
137 | * @enc_capability: encoder capability which is not used for now and | |
138 | * the value is reserved for future use | |
139 | * @wq: wait queue for VPU initialization status | |
140 | */ | |
141 | struct vpu_run { | |
142 | u32 signaled; | |
143 | char fw_ver[VPU_FW_VER_LEN]; | |
144 | unsigned int enc_capability; | |
145 | wait_queue_head_t wq; | |
146 | }; | |
147 | ||
148 | /** | |
149 | * struct vpu_ipi_desc - VPU IPI descriptor | |
150 | * | |
151 | * @handler: IPI handler | |
152 | * @name: the name of IPI handler | |
153 | * @priv: the private data of IPI handler | |
154 | */ | |
155 | struct vpu_ipi_desc { | |
156 | ipi_handler_t handler; | |
157 | const char *name; | |
158 | void *priv; | |
159 | }; | |
160 | ||
161 | /** | |
162 | * struct share_obj - DTCM (Data Tightly-Coupled Memory) buffer shared with | |
163 | * AP and VPU | |
164 | * | |
165 | * @id: IPI id | |
166 | * @len: share buffer length | |
167 | * @share_buf: share buffer data | |
168 | */ | |
169 | struct share_obj { | |
170 | s32 id; | |
171 | u32 len; | |
172 | unsigned char share_buf[SHARE_BUF_SIZE]; | |
173 | }; | |
174 | ||
175 | /** | |
176 | * struct mtk_vpu - vpu driver data | |
177 | * @extmem: VPU extended memory information | |
178 | * @reg: VPU TCM and configuration registers | |
179 | * @run: VPU initialization status | |
180 | * @ipi_desc: VPU IPI descriptor | |
181 | * @recv_buf: VPU DTCM share buffer for receiving. The | |
182 | * receive buffer is only accessed in interrupt context. | |
183 | * @send_buf: VPU DTCM share buffer for sending | |
184 | * @dev: VPU struct device | |
185 | * @clk: VPU clock on/off | |
186 | * @fw_loaded: indicate VPU firmware loaded | |
187 | * @enable_4GB: VPU 4GB mode on/off | |
188 | * @vpu_mutex: protect mtk_vpu (except recv_buf) and ensure only | |
189 | * one client to use VPU service at a time. For example, | |
190 | * suppose a client is using VPU to decode VP8. | |
191 | * If the other client wants to encode VP8, | |
192 | * it has to wait until VP8 decode completes. | |
193 | * @wdt_refcnt WDT reference count to make sure the watchdog can be | |
194 | * disabled if no other client is using VPU service | |
195 | * @ack_wq: The wait queue for each codec and mdp. When sleeping | |
196 | * processes wake up, they will check the condition | |
197 | * "ipi_id_ack" to run the corresponding action or | |
198 | * go back to sleep. | |
199 | * @ipi_id_ack: The ACKs for registered IPI function sending | |
200 | * interrupt to VPU | |
201 | * | |
202 | */ | |
203 | struct mtk_vpu { | |
204 | struct vpu_mem extmem[2]; | |
205 | struct vpu_regs reg; | |
206 | struct vpu_run run; | |
207 | struct vpu_wdt wdt; | |
208 | struct vpu_ipi_desc ipi_desc[IPI_MAX]; | |
209 | struct share_obj *recv_buf; | |
210 | struct share_obj *send_buf; | |
211 | struct device *dev; | |
212 | struct clk *clk; | |
213 | bool fw_loaded; | |
214 | bool enable_4GB; | |
215 | struct mutex vpu_mutex; /* for protecting vpu data data structure */ | |
216 | u32 wdt_refcnt; | |
217 | wait_queue_head_t ack_wq; | |
218 | bool ipi_id_ack[IPI_MAX]; | |
219 | }; | |
220 | ||
221 | static inline void vpu_cfg_writel(struct mtk_vpu *vpu, u32 val, u32 offset) | |
222 | { | |
223 | writel(val, vpu->reg.cfg + offset); | |
224 | } | |
225 | ||
226 | static inline u32 vpu_cfg_readl(struct mtk_vpu *vpu, u32 offset) | |
227 | { | |
228 | return readl(vpu->reg.cfg + offset); | |
229 | } | |
230 | ||
231 | static inline bool vpu_running(struct mtk_vpu *vpu) | |
232 | { | |
233 | return vpu_cfg_readl(vpu, VPU_RESET) & BIT(0); | |
234 | } | |
235 | ||
236 | static void vpu_clock_disable(struct mtk_vpu *vpu) | |
237 | { | |
238 | /* Disable VPU watchdog */ | |
239 | mutex_lock(&vpu->vpu_mutex); | |
240 | if (!--vpu->wdt_refcnt) | |
241 | vpu_cfg_writel(vpu, | |
242 | vpu_cfg_readl(vpu, VPU_WDT_REG) & ~(1L << 31), | |
243 | VPU_WDT_REG); | |
244 | mutex_unlock(&vpu->vpu_mutex); | |
245 | ||
246 | clk_disable(vpu->clk); | |
247 | } | |
248 | ||
249 | static int vpu_clock_enable(struct mtk_vpu *vpu) | |
250 | { | |
251 | int ret; | |
252 | ||
253 | ret = clk_enable(vpu->clk); | |
254 | if (ret) | |
255 | return ret; | |
256 | /* Enable VPU watchdog */ | |
257 | mutex_lock(&vpu->vpu_mutex); | |
258 | if (!vpu->wdt_refcnt++) | |
259 | vpu_cfg_writel(vpu, | |
260 | vpu_cfg_readl(vpu, VPU_WDT_REG) | (1L << 31), | |
261 | VPU_WDT_REG); | |
262 | mutex_unlock(&vpu->vpu_mutex); | |
263 | ||
264 | return ret; | |
265 | } | |
266 | ||
267 | int vpu_ipi_register(struct platform_device *pdev, | |
268 | enum ipi_id id, ipi_handler_t handler, | |
269 | const char *name, void *priv) | |
270 | { | |
271 | struct mtk_vpu *vpu = platform_get_drvdata(pdev); | |
272 | struct vpu_ipi_desc *ipi_desc; | |
273 | ||
274 | if (!vpu) { | |
275 | dev_err(&pdev->dev, "vpu device in not ready\n"); | |
276 | return -EPROBE_DEFER; | |
277 | } | |
278 | ||
279 | if (id >= 0 && id < IPI_MAX && handler) { | |
280 | ipi_desc = vpu->ipi_desc; | |
281 | ipi_desc[id].name = name; | |
282 | ipi_desc[id].handler = handler; | |
283 | ipi_desc[id].priv = priv; | |
284 | return 0; | |
285 | } | |
286 | ||
287 | dev_err(&pdev->dev, "register vpu ipi id %d with invalid arguments\n", | |
288 | id); | |
289 | return -EINVAL; | |
290 | } | |
291 | EXPORT_SYMBOL_GPL(vpu_ipi_register); | |
292 | ||
293 | int vpu_ipi_send(struct platform_device *pdev, | |
294 | enum ipi_id id, void *buf, | |
295 | unsigned int len) | |
296 | { | |
297 | struct mtk_vpu *vpu = platform_get_drvdata(pdev); | |
298 | struct share_obj *send_obj = vpu->send_buf; | |
299 | unsigned long timeout; | |
300 | int ret = 0; | |
301 | ||
302 | if (id <= IPI_VPU_INIT || id >= IPI_MAX || | |
303 | len > sizeof(send_obj->share_buf) || !buf) { | |
304 | dev_err(vpu->dev, "failed to send ipi message\n"); | |
305 | return -EINVAL; | |
306 | } | |
307 | ||
308 | ret = vpu_clock_enable(vpu); | |
309 | if (ret) { | |
310 | dev_err(vpu->dev, "failed to enable vpu clock\n"); | |
311 | return ret; | |
312 | } | |
313 | if (!vpu_running(vpu)) { | |
314 | dev_err(vpu->dev, "vpu_ipi_send: VPU is not running\n"); | |
315 | ret = -EINVAL; | |
316 | goto clock_disable; | |
317 | } | |
318 | ||
319 | mutex_lock(&vpu->vpu_mutex); | |
320 | ||
321 | /* Wait until VPU receives the last command */ | |
322 | timeout = jiffies + msecs_to_jiffies(IPI_TIMEOUT_MS); | |
323 | do { | |
324 | if (time_after(jiffies, timeout)) { | |
325 | dev_err(vpu->dev, "vpu_ipi_send: IPI timeout!\n"); | |
326 | ret = -EIO; | |
327 | goto mut_unlock; | |
328 | } | |
329 | } while (vpu_cfg_readl(vpu, HOST_TO_VPU)); | |
330 | ||
331 | memcpy((void *)send_obj->share_buf, buf, len); | |
332 | send_obj->len = len; | |
333 | send_obj->id = id; | |
334 | ||
335 | vpu->ipi_id_ack[id] = false; | |
336 | /* send the command to VPU */ | |
337 | vpu_cfg_writel(vpu, 0x1, HOST_TO_VPU); | |
338 | ||
339 | mutex_unlock(&vpu->vpu_mutex); | |
340 | ||
341 | /* wait for VPU's ACK */ | |
342 | timeout = msecs_to_jiffies(IPI_TIMEOUT_MS); | |
343 | ret = wait_event_timeout(vpu->ack_wq, vpu->ipi_id_ack[id], timeout); | |
344 | vpu->ipi_id_ack[id] = false; | |
345 | if (ret == 0) { | |
346 | dev_err(vpu->dev, "vpu ipi %d ack time out !", id); | |
347 | ret = -EIO; | |
348 | goto clock_disable; | |
349 | } | |
350 | vpu_clock_disable(vpu); | |
351 | ||
352 | return 0; | |
353 | ||
354 | mut_unlock: | |
355 | mutex_unlock(&vpu->vpu_mutex); | |
356 | clock_disable: | |
357 | vpu_clock_disable(vpu); | |
358 | ||
359 | return ret; | |
360 | } | |
361 | EXPORT_SYMBOL_GPL(vpu_ipi_send); | |
362 | ||
363 | static void vpu_wdt_reset_func(struct work_struct *ws) | |
364 | { | |
365 | struct vpu_wdt *wdt = container_of(ws, struct vpu_wdt, ws); | |
366 | struct mtk_vpu *vpu = container_of(wdt, struct mtk_vpu, wdt); | |
367 | struct vpu_wdt_handler *handler = wdt->handler; | |
368 | int index, ret; | |
369 | ||
370 | dev_info(vpu->dev, "vpu reset\n"); | |
371 | ret = vpu_clock_enable(vpu); | |
372 | if (ret) { | |
373 | dev_err(vpu->dev, "[VPU] wdt enables clock failed %d\n", ret); | |
374 | return; | |
375 | } | |
376 | mutex_lock(&vpu->vpu_mutex); | |
377 | vpu_cfg_writel(vpu, 0x0, VPU_RESET); | |
378 | vpu->fw_loaded = false; | |
379 | mutex_unlock(&vpu->vpu_mutex); | |
380 | vpu_clock_disable(vpu); | |
381 | ||
382 | for (index = 0; index < VPU_RST_MAX; index++) { | |
383 | if (handler[index].reset_func) { | |
384 | handler[index].reset_func(handler[index].priv); | |
385 | dev_dbg(vpu->dev, "wdt handler func %d\n", index); | |
386 | } | |
387 | } | |
388 | } | |
389 | ||
390 | int vpu_wdt_reg_handler(struct platform_device *pdev, | |
391 | void wdt_reset(void *), | |
392 | void *priv, enum rst_id id) | |
393 | { | |
394 | struct mtk_vpu *vpu = platform_get_drvdata(pdev); | |
395 | struct vpu_wdt_handler *handler; | |
396 | ||
397 | if (!vpu) { | |
398 | dev_err(&pdev->dev, "vpu device in not ready\n"); | |
399 | return -EPROBE_DEFER; | |
400 | } | |
401 | ||
402 | handler = vpu->wdt.handler; | |
403 | ||
404 | if (id >= 0 && id < VPU_RST_MAX && wdt_reset) { | |
405 | dev_dbg(vpu->dev, "wdt register id %d\n", id); | |
406 | mutex_lock(&vpu->vpu_mutex); | |
407 | handler[id].reset_func = wdt_reset; | |
408 | handler[id].priv = priv; | |
409 | mutex_unlock(&vpu->vpu_mutex); | |
410 | return 0; | |
411 | } | |
412 | ||
413 | dev_err(vpu->dev, "register vpu wdt handler failed\n"); | |
414 | return -EINVAL; | |
415 | } | |
416 | EXPORT_SYMBOL_GPL(vpu_wdt_reg_handler); | |
417 | ||
418 | unsigned int vpu_get_venc_hw_capa(struct platform_device *pdev) | |
419 | { | |
420 | struct mtk_vpu *vpu = platform_get_drvdata(pdev); | |
421 | ||
422 | return vpu->run.enc_capability; | |
423 | } | |
424 | EXPORT_SYMBOL_GPL(vpu_get_venc_hw_capa); | |
425 | ||
426 | void *vpu_mapping_dm_addr(struct platform_device *pdev, | |
427 | u32 dtcm_dmem_addr) | |
428 | { | |
429 | struct mtk_vpu *vpu = platform_get_drvdata(pdev); | |
430 | ||
431 | if (!dtcm_dmem_addr || | |
432 | (dtcm_dmem_addr > (VPU_DTCM_SIZE + VPU_EXT_D_SIZE))) { | |
433 | dev_err(vpu->dev, "invalid virtual data memory address\n"); | |
434 | return ERR_PTR(-EINVAL); | |
435 | } | |
436 | ||
437 | if (dtcm_dmem_addr < VPU_DTCM_SIZE) | |
438 | return (__force void *)(dtcm_dmem_addr + vpu->reg.tcm + | |
439 | VPU_DTCM_OFFSET); | |
440 | ||
441 | return vpu->extmem[D_FW].va + (dtcm_dmem_addr - VPU_DTCM_SIZE); | |
442 | } | |
443 | EXPORT_SYMBOL_GPL(vpu_mapping_dm_addr); | |
444 | ||
445 | struct platform_device *vpu_get_plat_device(struct platform_device *pdev) | |
446 | { | |
447 | struct device *dev = &pdev->dev; | |
448 | struct device_node *vpu_node; | |
449 | struct platform_device *vpu_pdev; | |
450 | ||
451 | vpu_node = of_parse_phandle(dev->of_node, "mediatek,vpu", 0); | |
452 | if (!vpu_node) { | |
453 | dev_err(dev, "can't get vpu node\n"); | |
454 | return NULL; | |
455 | } | |
456 | ||
457 | vpu_pdev = of_find_device_by_node(vpu_node); | |
458 | if (WARN_ON(!vpu_pdev)) { | |
459 | dev_err(dev, "vpu pdev failed\n"); | |
460 | of_node_put(vpu_node); | |
461 | return NULL; | |
462 | } | |
463 | ||
464 | return vpu_pdev; | |
465 | } | |
466 | EXPORT_SYMBOL_GPL(vpu_get_plat_device); | |
467 | ||
468 | /* load vpu program/data memory */ | |
469 | static int load_requested_vpu(struct mtk_vpu *vpu, | |
470 | const struct firmware *vpu_fw, | |
471 | u8 fw_type) | |
472 | { | |
473 | size_t tcm_size = fw_type ? VPU_DTCM_SIZE : VPU_PTCM_SIZE; | |
474 | size_t fw_size = fw_type ? VPU_D_FW_SIZE : VPU_P_FW_SIZE; | |
475 | char *fw_name = fw_type ? VPU_D_FW : VPU_P_FW; | |
476 | size_t dl_size = 0; | |
477 | size_t extra_fw_size = 0; | |
478 | void *dest; | |
479 | int ret; | |
480 | ||
481 | ret = request_firmware(&vpu_fw, fw_name, vpu->dev); | |
482 | if (ret < 0) { | |
483 | dev_err(vpu->dev, "Failed to load %s, %d\n", fw_name, ret); | |
484 | return ret; | |
485 | } | |
486 | dl_size = vpu_fw->size; | |
487 | if (dl_size > fw_size) { | |
488 | dev_err(vpu->dev, "fw %s size %zu is abnormal\n", fw_name, | |
489 | dl_size); | |
490 | release_firmware(vpu_fw); | |
491 | return -EFBIG; | |
492 | } | |
493 | dev_dbg(vpu->dev, "Downloaded fw %s size: %zu.\n", | |
494 | fw_name, | |
495 | dl_size); | |
496 | /* reset VPU */ | |
497 | vpu_cfg_writel(vpu, 0x0, VPU_RESET); | |
498 | ||
499 | /* handle extended firmware size */ | |
500 | if (dl_size > tcm_size) { | |
501 | dev_dbg(vpu->dev, "fw size %zu > limited fw size %zu\n", | |
502 | dl_size, tcm_size); | |
503 | extra_fw_size = dl_size - tcm_size; | |
504 | dev_dbg(vpu->dev, "extra_fw_size %zu\n", extra_fw_size); | |
505 | dl_size = tcm_size; | |
506 | } | |
507 | dest = (__force void *)vpu->reg.tcm; | |
508 | if (fw_type == D_FW) | |
509 | dest += VPU_DTCM_OFFSET; | |
510 | memcpy(dest, vpu_fw->data, dl_size); | |
511 | /* download to extended memory if need */ | |
512 | if (extra_fw_size > 0) { | |
513 | dest = vpu->extmem[fw_type].va; | |
514 | dev_dbg(vpu->dev, "download extended memory type %x\n", | |
515 | fw_type); | |
516 | memcpy(dest, vpu_fw->data + tcm_size, extra_fw_size); | |
517 | } | |
518 | ||
519 | release_firmware(vpu_fw); | |
520 | ||
521 | return 0; | |
522 | } | |
523 | ||
524 | int vpu_load_firmware(struct platform_device *pdev) | |
525 | { | |
526 | struct mtk_vpu *vpu = platform_get_drvdata(pdev); | |
527 | struct device *dev = &pdev->dev; | |
528 | struct vpu_run *run = &vpu->run; | |
529 | const struct firmware *vpu_fw = NULL; | |
530 | int ret; | |
531 | ||
532 | if (!pdev) { | |
533 | dev_err(dev, "VPU platform device is invalid\n"); | |
534 | return -EINVAL; | |
535 | } | |
536 | ||
537 | mutex_lock(&vpu->vpu_mutex); | |
538 | if (vpu->fw_loaded) { | |
539 | mutex_unlock(&vpu->vpu_mutex); | |
540 | return 0; | |
541 | } | |
542 | mutex_unlock(&vpu->vpu_mutex); | |
543 | ||
544 | ret = vpu_clock_enable(vpu); | |
545 | if (ret) { | |
546 | dev_err(dev, "enable clock failed %d\n", ret); | |
547 | return ret; | |
548 | } | |
549 | ||
550 | mutex_lock(&vpu->vpu_mutex); | |
551 | ||
552 | run->signaled = false; | |
553 | dev_dbg(vpu->dev, "firmware request\n"); | |
554 | /* Downloading program firmware to device*/ | |
555 | ret = load_requested_vpu(vpu, vpu_fw, P_FW); | |
556 | if (ret < 0) { | |
557 | dev_err(dev, "Failed to request %s, %d\n", VPU_P_FW, ret); | |
558 | goto OUT_LOAD_FW; | |
559 | } | |
560 | ||
561 | /* Downloading data firmware to device */ | |
562 | ret = load_requested_vpu(vpu, vpu_fw, D_FW); | |
563 | if (ret < 0) { | |
564 | dev_err(dev, "Failed to request %s, %d\n", VPU_D_FW, ret); | |
565 | goto OUT_LOAD_FW; | |
566 | } | |
567 | ||
568 | vpu->fw_loaded = true; | |
569 | /* boot up vpu */ | |
570 | vpu_cfg_writel(vpu, 0x1, VPU_RESET); | |
571 | ||
572 | ret = wait_event_interruptible_timeout(run->wq, | |
573 | run->signaled, | |
574 | msecs_to_jiffies(INIT_TIMEOUT_MS) | |
575 | ); | |
576 | if (ret == 0) { | |
577 | ret = -ETIME; | |
578 | dev_err(dev, "wait vpu initialization timout!\n"); | |
579 | goto OUT_LOAD_FW; | |
580 | } else if (-ERESTARTSYS == ret) { | |
581 | dev_err(dev, "wait vpu interrupted by a signal!\n"); | |
582 | goto OUT_LOAD_FW; | |
583 | } | |
584 | ||
585 | ret = 0; | |
586 | dev_info(dev, "vpu is ready. Fw version %s\n", run->fw_ver); | |
587 | ||
588 | OUT_LOAD_FW: | |
589 | mutex_unlock(&vpu->vpu_mutex); | |
590 | vpu_clock_disable(vpu); | |
591 | ||
592 | return ret; | |
593 | } | |
594 | EXPORT_SYMBOL_GPL(vpu_load_firmware); | |
595 | ||
596 | static void vpu_init_ipi_handler(void *data, unsigned int len, void *priv) | |
597 | { | |
598 | struct mtk_vpu *vpu = (struct mtk_vpu *)priv; | |
599 | struct vpu_run *run = (struct vpu_run *)data; | |
600 | ||
601 | vpu->run.signaled = run->signaled; | |
602 | strncpy(vpu->run.fw_ver, run->fw_ver, VPU_FW_VER_LEN); | |
603 | vpu->run.enc_capability = run->enc_capability; | |
604 | wake_up_interruptible(&vpu->run.wq); | |
605 | } | |
606 | ||
607 | #ifdef CONFIG_DEBUG_FS | |
608 | static ssize_t vpu_debug_read(struct file *file, char __user *user_buf, | |
609 | size_t count, loff_t *ppos) | |
610 | { | |
611 | char buf[256]; | |
612 | unsigned int len; | |
613 | unsigned int running, pc, vpu_to_host, host_to_vpu, wdt; | |
614 | int ret; | |
615 | struct device *dev = file->private_data; | |
616 | struct mtk_vpu *vpu = dev_get_drvdata(dev); | |
617 | ||
618 | ret = vpu_clock_enable(vpu); | |
619 | if (ret) { | |
620 | dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret); | |
621 | return 0; | |
622 | } | |
623 | ||
624 | /* vpu register status */ | |
625 | running = vpu_running(vpu); | |
626 | pc = vpu_cfg_readl(vpu, VPU_PC_REG); | |
627 | wdt = vpu_cfg_readl(vpu, VPU_WDT_REG); | |
628 | host_to_vpu = vpu_cfg_readl(vpu, HOST_TO_VPU); | |
629 | vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST); | |
630 | vpu_clock_disable(vpu); | |
631 | ||
632 | if (running) { | |
633 | len = snprintf(buf, sizeof(buf), "VPU is running\n\n" | |
634 | "FW Version: %s\n" | |
635 | "PC: 0x%x\n" | |
636 | "WDT: 0x%x\n" | |
637 | "Host to VPU: 0x%x\n" | |
638 | "VPU to Host: 0x%x\n", | |
639 | vpu->run.fw_ver, pc, wdt, | |
640 | host_to_vpu, vpu_to_host); | |
641 | } else { | |
642 | len = snprintf(buf, sizeof(buf), "VPU not running\n"); | |
643 | } | |
644 | ||
645 | return simple_read_from_buffer(user_buf, count, ppos, buf, len); | |
646 | } | |
647 | ||
648 | static const struct file_operations vpu_debug_fops = { | |
649 | .open = simple_open, | |
650 | .read = vpu_debug_read, | |
651 | }; | |
652 | #endif /* CONFIG_DEBUG_FS */ | |
653 | ||
654 | static void vpu_free_ext_mem(struct mtk_vpu *vpu, u8 fw_type) | |
655 | { | |
656 | struct device *dev = vpu->dev; | |
657 | size_t fw_ext_size = fw_type ? VPU_EXT_D_SIZE : VPU_EXT_P_SIZE; | |
658 | ||
659 | dma_free_coherent(dev, fw_ext_size, vpu->extmem[fw_type].va, | |
660 | vpu->extmem[fw_type].pa); | |
661 | } | |
662 | ||
663 | static int vpu_alloc_ext_mem(struct mtk_vpu *vpu, u32 fw_type) | |
664 | { | |
665 | struct device *dev = vpu->dev; | |
666 | size_t fw_ext_size = fw_type ? VPU_EXT_D_SIZE : VPU_EXT_P_SIZE; | |
667 | u32 vpu_ext_mem0 = fw_type ? VPU_DMEM_EXT0_ADDR : VPU_PMEM_EXT0_ADDR; | |
668 | u32 vpu_ext_mem1 = fw_type ? VPU_DMEM_EXT1_ADDR : VPU_PMEM_EXT1_ADDR; | |
669 | u32 offset_4gb = vpu->enable_4GB ? 0x40000000 : 0; | |
670 | ||
671 | vpu->extmem[fw_type].va = dma_alloc_coherent(dev, | |
672 | fw_ext_size, | |
673 | &vpu->extmem[fw_type].pa, | |
674 | GFP_KERNEL); | |
675 | if (!vpu->extmem[fw_type].va) { | |
676 | dev_err(dev, "Failed to allocate the extended program memory\n"); | |
677 | return PTR_ERR(vpu->extmem[fw_type].va); | |
678 | } | |
679 | ||
680 | /* Disable extend0. Enable extend1 */ | |
681 | vpu_cfg_writel(vpu, 0x1, vpu_ext_mem0); | |
682 | vpu_cfg_writel(vpu, (vpu->extmem[fw_type].pa & 0xFFFFF000) + offset_4gb, | |
683 | vpu_ext_mem1); | |
684 | ||
685 | dev_info(dev, "%s extend memory phy=0x%llx virt=0x%p\n", | |
686 | fw_type ? "Data" : "Program", | |
687 | (unsigned long long)vpu->extmem[fw_type].pa, | |
688 | vpu->extmem[fw_type].va); | |
689 | ||
690 | return 0; | |
691 | } | |
692 | ||
693 | static void vpu_ipi_handler(struct mtk_vpu *vpu) | |
694 | { | |
695 | struct share_obj *rcv_obj = vpu->recv_buf; | |
696 | struct vpu_ipi_desc *ipi_desc = vpu->ipi_desc; | |
697 | ||
698 | if (rcv_obj->id < IPI_MAX && ipi_desc[rcv_obj->id].handler) { | |
699 | ipi_desc[rcv_obj->id].handler(rcv_obj->share_buf, | |
700 | rcv_obj->len, | |
701 | ipi_desc[rcv_obj->id].priv); | |
702 | if (rcv_obj->id > IPI_VPU_INIT) { | |
703 | vpu->ipi_id_ack[rcv_obj->id] = true; | |
704 | wake_up(&vpu->ack_wq); | |
705 | } | |
706 | } else { | |
707 | dev_err(vpu->dev, "No such ipi id = %d\n", rcv_obj->id); | |
708 | } | |
709 | } | |
710 | ||
711 | static int vpu_ipi_init(struct mtk_vpu *vpu) | |
712 | { | |
713 | /* Disable VPU to host interrupt */ | |
714 | vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST); | |
715 | ||
716 | /* shared buffer initialization */ | |
717 | vpu->recv_buf = (__force struct share_obj *)(vpu->reg.tcm + | |
718 | VPU_DTCM_OFFSET); | |
719 | vpu->send_buf = vpu->recv_buf + 1; | |
720 | memset(vpu->recv_buf, 0, sizeof(struct share_obj)); | |
721 | memset(vpu->send_buf, 0, sizeof(struct share_obj)); | |
722 | ||
723 | return 0; | |
724 | } | |
725 | ||
726 | static irqreturn_t vpu_irq_handler(int irq, void *priv) | |
727 | { | |
728 | struct mtk_vpu *vpu = priv; | |
729 | u32 vpu_to_host; | |
730 | int ret; | |
731 | ||
732 | /* | |
733 | * Clock should have been enabled already. | |
734 | * Enable again in case vpu_ipi_send times out | |
735 | * and has disabled the clock. | |
736 | */ | |
737 | ret = clk_enable(vpu->clk); | |
738 | if (ret) { | |
739 | dev_err(vpu->dev, "[VPU] enable clock failed %d\n", ret); | |
740 | return IRQ_NONE; | |
741 | } | |
742 | vpu_to_host = vpu_cfg_readl(vpu, VPU_TO_HOST); | |
743 | if (vpu_to_host & VPU_IPC_INT) { | |
744 | vpu_ipi_handler(vpu); | |
745 | } else { | |
746 | dev_err(vpu->dev, "vpu watchdog timeout! 0x%x", vpu_to_host); | |
747 | queue_work(vpu->wdt.wq, &vpu->wdt.ws); | |
748 | } | |
749 | ||
750 | /* VPU won't send another interrupt until we set VPU_TO_HOST to 0. */ | |
751 | vpu_cfg_writel(vpu, 0x0, VPU_TO_HOST); | |
752 | clk_disable(vpu->clk); | |
753 | ||
754 | return IRQ_HANDLED; | |
755 | } | |
756 | ||
757 | #ifdef CONFIG_DEBUG_FS | |
758 | static struct dentry *vpu_debugfs; | |
759 | #endif | |
760 | static int mtk_vpu_probe(struct platform_device *pdev) | |
761 | { | |
762 | struct mtk_vpu *vpu; | |
763 | struct device *dev; | |
764 | struct resource *res; | |
765 | int ret = 0; | |
766 | ||
767 | dev_dbg(&pdev->dev, "initialization\n"); | |
768 | ||
769 | dev = &pdev->dev; | |
770 | vpu = devm_kzalloc(dev, sizeof(*vpu), GFP_KERNEL); | |
771 | if (!vpu) | |
772 | return -ENOMEM; | |
773 | ||
774 | vpu->dev = &pdev->dev; | |
775 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tcm"); | |
776 | vpu->reg.tcm = devm_ioremap_resource(dev, res); | |
14ee452f | 777 | if (IS_ERR((__force void *)vpu->reg.tcm)) |
3003a180 | 778 | return PTR_ERR((__force void *)vpu->reg.tcm); |
3003a180 ACC |
779 | |
780 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cfg_reg"); | |
781 | vpu->reg.cfg = devm_ioremap_resource(dev, res); | |
14ee452f | 782 | if (IS_ERR((__force void *)vpu->reg.cfg)) |
3003a180 | 783 | return PTR_ERR((__force void *)vpu->reg.cfg); |
3003a180 ACC |
784 | |
785 | /* Get VPU clock */ | |
786 | vpu->clk = devm_clk_get(dev, "main"); | |
ffe57033 | 787 | if (IS_ERR(vpu->clk)) { |
3003a180 | 788 | dev_err(dev, "get vpu clock failed\n"); |
ffe57033 | 789 | return PTR_ERR(vpu->clk); |
3003a180 ACC |
790 | } |
791 | ||
792 | platform_set_drvdata(pdev, vpu); | |
793 | ||
794 | ret = clk_prepare(vpu->clk); | |
795 | if (ret) { | |
796 | dev_err(dev, "prepare vpu clock failed\n"); | |
797 | return ret; | |
798 | } | |
799 | ||
800 | /* VPU watchdog */ | |
801 | vpu->wdt.wq = create_singlethread_workqueue("vpu_wdt"); | |
802 | if (!vpu->wdt.wq) { | |
803 | dev_err(dev, "initialize wdt workqueue failed\n"); | |
804 | return -ENOMEM; | |
805 | } | |
806 | INIT_WORK(&vpu->wdt.ws, vpu_wdt_reset_func); | |
807 | mutex_init(&vpu->vpu_mutex); | |
808 | ||
809 | ret = vpu_clock_enable(vpu); | |
810 | if (ret) { | |
811 | dev_err(dev, "enable vpu clock failed\n"); | |
812 | goto workqueue_destroy; | |
813 | } | |
814 | ||
815 | dev_dbg(dev, "vpu ipi init\n"); | |
816 | ret = vpu_ipi_init(vpu); | |
817 | if (ret) { | |
818 | dev_err(dev, "Failed to init ipi\n"); | |
819 | goto disable_vpu_clk; | |
820 | } | |
821 | ||
822 | /* register vpu initialization IPI */ | |
823 | ret = vpu_ipi_register(pdev, IPI_VPU_INIT, vpu_init_ipi_handler, | |
824 | "vpu_init", vpu); | |
825 | if (ret) { | |
826 | dev_err(dev, "Failed to register IPI_VPU_INIT\n"); | |
827 | goto vpu_mutex_destroy; | |
828 | } | |
829 | ||
830 | #ifdef CONFIG_DEBUG_FS | |
831 | vpu_debugfs = debugfs_create_file("mtk_vpu", S_IRUGO, NULL, (void *)dev, | |
832 | &vpu_debug_fops); | |
833 | if (!vpu_debugfs) { | |
834 | ret = -ENOMEM; | |
835 | goto cleanup_ipi; | |
836 | } | |
837 | #endif | |
838 | ||
839 | /* Set PTCM to 96K and DTCM to 32K */ | |
840 | vpu_cfg_writel(vpu, 0x2, VPU_TCM_CFG); | |
841 | ||
842 | vpu->enable_4GB = !!(totalram_pages > (SZ_2G >> PAGE_SHIFT)); | |
843 | dev_info(dev, "4GB mode %u\n", vpu->enable_4GB); | |
844 | ||
845 | if (vpu->enable_4GB) { | |
846 | ret = of_reserved_mem_device_init(dev); | |
847 | if (ret) | |
848 | dev_info(dev, "init reserved memory failed\n"); | |
849 | /* continue to use dynamic allocation if failed */ | |
850 | } | |
851 | ||
852 | ret = vpu_alloc_ext_mem(vpu, D_FW); | |
853 | if (ret) { | |
854 | dev_err(dev, "Allocate DM failed\n"); | |
855 | goto remove_debugfs; | |
856 | } | |
857 | ||
858 | ret = vpu_alloc_ext_mem(vpu, P_FW); | |
859 | if (ret) { | |
860 | dev_err(dev, "Allocate PM failed\n"); | |
861 | goto free_d_mem; | |
862 | } | |
863 | ||
864 | init_waitqueue_head(&vpu->run.wq); | |
865 | init_waitqueue_head(&vpu->ack_wq); | |
866 | ||
867 | res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | |
868 | if (!res) { | |
869 | dev_err(dev, "get IRQ resource failed.\n"); | |
870 | ret = -ENXIO; | |
871 | goto free_p_mem; | |
872 | } | |
873 | vpu->reg.irq = platform_get_irq(pdev, 0); | |
874 | ret = devm_request_irq(dev, vpu->reg.irq, vpu_irq_handler, 0, | |
875 | pdev->name, vpu); | |
876 | if (ret) { | |
877 | dev_err(dev, "failed to request irq\n"); | |
878 | goto free_p_mem; | |
879 | } | |
880 | ||
881 | vpu_clock_disable(vpu); | |
882 | dev_dbg(dev, "initialization completed\n"); | |
883 | ||
884 | return 0; | |
885 | ||
886 | free_p_mem: | |
887 | vpu_free_ext_mem(vpu, P_FW); | |
888 | free_d_mem: | |
889 | vpu_free_ext_mem(vpu, D_FW); | |
890 | remove_debugfs: | |
891 | of_reserved_mem_device_release(dev); | |
892 | #ifdef CONFIG_DEBUG_FS | |
893 | debugfs_remove(vpu_debugfs); | |
894 | cleanup_ipi: | |
895 | #endif | |
896 | memset(vpu->ipi_desc, 0, sizeof(struct vpu_ipi_desc) * IPI_MAX); | |
897 | vpu_mutex_destroy: | |
898 | mutex_destroy(&vpu->vpu_mutex); | |
899 | disable_vpu_clk: | |
900 | vpu_clock_disable(vpu); | |
901 | workqueue_destroy: | |
902 | destroy_workqueue(vpu->wdt.wq); | |
903 | ||
904 | return ret; | |
905 | } | |
906 | ||
907 | static const struct of_device_id mtk_vpu_match[] = { | |
908 | { | |
909 | .compatible = "mediatek,mt8173-vpu", | |
910 | }, | |
911 | {}, | |
912 | }; | |
913 | MODULE_DEVICE_TABLE(of, mtk_vpu_match); | |
914 | ||
915 | static int mtk_vpu_remove(struct platform_device *pdev) | |
916 | { | |
917 | struct mtk_vpu *vpu = platform_get_drvdata(pdev); | |
918 | ||
919 | #ifdef CONFIG_DEBUG_FS | |
920 | debugfs_remove(vpu_debugfs); | |
921 | #endif | |
922 | if (vpu->wdt.wq) { | |
923 | flush_workqueue(vpu->wdt.wq); | |
924 | destroy_workqueue(vpu->wdt.wq); | |
925 | } | |
926 | vpu_free_ext_mem(vpu, P_FW); | |
927 | vpu_free_ext_mem(vpu, D_FW); | |
928 | mutex_destroy(&vpu->vpu_mutex); | |
929 | clk_unprepare(vpu->clk); | |
930 | ||
931 | return 0; | |
932 | } | |
933 | ||
934 | static struct platform_driver mtk_vpu_driver = { | |
935 | .probe = mtk_vpu_probe, | |
936 | .remove = mtk_vpu_remove, | |
937 | .driver = { | |
938 | .name = "mtk_vpu", | |
939 | .of_match_table = mtk_vpu_match, | |
940 | }, | |
941 | }; | |
942 | ||
943 | module_platform_driver(mtk_vpu_driver); | |
944 | ||
945 | MODULE_LICENSE("GPL v2"); | |
946 | MODULE_DESCRIPTION("Mediatek Video Prosessor Unit driver"); |