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