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