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