xref: /OK3568_Linux_fs/kernel/drivers/media/platform/mtk-vpu/mtk_vpu.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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