438 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			438 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * Copyright (c) 2021 MediaTek Inc.
 | |
|  * Author: Anthony Huang <anthony.huang@mediatek.com>
 | |
|  */
 | |
| 
 | |
| #include <linux/dma-mapping.h>
 | |
| #include <linux/io.h>
 | |
| #include <linux/module.h>
 | |
| #include <linux/of_platform.h>
 | |
| #include <linux/slab.h>
 | |
| #if IS_ENABLED(CONFIG_MTK_SLBC)
 | |
| #include <slbc_ops.h>
 | |
| #endif
 | |
| 
 | |
| #include "cmdq-util.h"
 | |
| 
 | |
| #define mminfra_read(addr)			readl_relaxed(addr)
 | |
| #define mminfra_write(addr, val)		writel(val, addr)
 | |
| #define mminfra_crit(fmt, ...)        pr_info("[MMInfra] " fmt, ##__VA_ARGS__)
 | |
| 
 | |
| enum {
 | |
| 	DISPSYS_BASE,
 | |
| 	DISPSYS1_BASE,
 | |
| 	MDPSYS_BASE,
 | |
| 	MDPSYS1_BASE,
 | |
| 	DISP_LARB_0,
 | |
| 	DISP_LARB_1,
 | |
| 	DISP_LARB_2,
 | |
| 	DISP_LARB_3,
 | |
| 	MDP_LARB_0,
 | |
| 	MDP_LARB_1,
 | |
| };
 | |
| 
 | |
| #if IS_ENABLED(CONFIG_MTK_MMINFRA_DEBUG)
 | |
| static void __iomem *dispsys_base, *dispsys1_base;
 | |
| static void __iomem *mdpsys_base, *mdpsys1_base;
 | |
| static void __iomem *disp_larb_0_base, *disp_larb_1_base, *disp_larb_2_base, *disp_larb_3_base;
 | |
| static void __iomem *mdp_larb_0_base, *mdp_larb_1_base, *mdp_larb_2_base;
 | |
| static unsigned int mm_sram_base;
 | |
| #endif
 | |
| static unsigned int disp_larb_0_fake, disp_larb_1_fake, disp_larb_2_fake, disp_larb_3_fake;
 | |
| static unsigned int mdp_larb_0_fake, mdp_larb_1_fake, mdp_larb_2_fake;
 | |
| 
 | |
| static struct device *dev;
 | |
| static struct platform_device *g_pdev;
 | |
| #if IS_ENABLED(CONFIG_MTK_MMINFRA_DEBUG)
 | |
| static bool is_init;
 | |
| 
 | |
| static void init_mmsys(void)
 | |
| {
 | |
| 	if (dispsys_base) {
 | |
| 		mminfra_write(dispsys_base + 0x108, 0xffffffff);
 | |
| 		mminfra_write(dispsys_base + 0x118, 0xffffffff);
 | |
| 		mminfra_write(dispsys_base + 0x1a8, 0xffffffff);
 | |
| 	}
 | |
| 
 | |
| 	if (dispsys1_base) {
 | |
| 		mminfra_write(dispsys1_base + 0x108, 0xffffffff);
 | |
| 		mminfra_write(dispsys1_base + 0x118, 0xffffffff);
 | |
| 		mminfra_write(dispsys1_base + 0x1a8, 0xffffffff);
 | |
| 	}
 | |
| 
 | |
| 	if (mdpsys_base) {
 | |
| 		mminfra_write(mdpsys_base + 0x108, 0xffffffff);
 | |
| 		mminfra_write(mdpsys_base + 0x118, 0xffffffff);
 | |
| 		mminfra_write(mdpsys_base + 0x128, 0xffffffff);
 | |
| 		mminfra_write(mdpsys_base + 0x138, 0xffffffff);
 | |
| 		mminfra_write(mdpsys_base + 0x148, 0xffffffff);
 | |
| 	}
 | |
| 
 | |
| 	if (mdpsys1_base) {
 | |
| 		mminfra_write(mdpsys1_base + 0x108, 0xffffffff);
 | |
| 		mminfra_write(mdpsys1_base + 0x118, 0xffffffff);
 | |
| 		mminfra_write(mdpsys1_base + 0x128, 0xffffffff);
 | |
| 		mminfra_write(mdpsys1_base + 0x138, 0xffffffff);
 | |
| 		mminfra_write(mdpsys1_base + 0x148, 0xffffffff);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void init_mdp_smi(int id, void __iomem *mdp_larb_base, unsigned int fake_port)
 | |
| {
 | |
| 	/* NON_SEC_CON */
 | |
| 	mminfra_write(mdp_larb_base + 0x380 + 0x4 * fake_port,
 | |
| 		mminfra_read(mdp_larb_base + 0x380 + 0x4 * fake_port) | 0x000f0000);
 | |
| 
 | |
| 	mminfra_crit("%s %d NON_SEC_CON=0x%x\n", __func__, id,
 | |
| 		mminfra_read(mdp_larb_base + 0x380 + 0x4 * fake_port));
 | |
| }
 | |
| 
 | |
| static void init_disp_smi(int id, void __iomem *disp_larb_base, unsigned int fake_port)
 | |
| {
 | |
| 	/* NON_SEC_CON */
 | |
| 	mminfra_write(disp_larb_base + 0x380 + 0x4 * fake_port,
 | |
| 		mminfra_read(disp_larb_base + 0x380 + 0x4 * fake_port) & 0xfff0fffe);
 | |
| 
 | |
| 	mminfra_crit("%s %d NON_SEC_CON=0x%x\n", __func__, id,
 | |
| 		mminfra_read(disp_larb_base + 0x380 + 0x4 * fake_port));
 | |
| }
 | |
| 
 | |
| static void init_smi(void)
 | |
| {
 | |
| 	if (mdp_larb_0_base)
 | |
| 		init_mdp_smi(0, mdp_larb_0_base, mdp_larb_0_fake);
 | |
| 	if (mdp_larb_1_base)
 | |
| 		init_mdp_smi(1, mdp_larb_1_base, mdp_larb_1_fake);
 | |
| 	if (mdp_larb_2_base)
 | |
| 		init_mdp_smi(1, mdp_larb_2_base, mdp_larb_2_fake);
 | |
| 
 | |
| 	if (disp_larb_0_base)
 | |
| 		init_disp_smi(0, disp_larb_0_base, disp_larb_0_fake);
 | |
| 	if (disp_larb_1_base)
 | |
| 		init_disp_smi(1, disp_larb_1_base, disp_larb_1_fake);
 | |
| 	if (disp_larb_2_base)
 | |
| 		init_disp_smi(2, disp_larb_2_base, disp_larb_2_fake);
 | |
| 	if (disp_larb_3_base)
 | |
| 		init_disp_smi(3, disp_larb_3_base, disp_larb_3_fake);
 | |
| }
 | |
| 
 | |
| static void fake_eng_set(unsigned int id, void __iomem *subsys_base, unsigned int eng_id,
 | |
| 	unsigned int rd_addr, unsigned int wr_addr, unsigned int wr_pat, unsigned int length,
 | |
| 			unsigned int burst, unsigned int dis_rd, unsigned int dis_wr,
 | |
| 			unsigned int latency, unsigned int loop)
 | |
| {
 | |
| 	unsigned int shift = 0;
 | |
| 
 | |
| 	if (subsys_base) {
 | |
| 		if (eng_id == 1)
 | |
| 			shift = 0x20;
 | |
| 
 | |
| 		/* SUBSYS_FAKE_ENG_RD_ADDR */
 | |
| 		mminfra_write(subsys_base + 0x210 + shift, rd_addr);
 | |
| 		/* SUBSYS_FAKE_ENG_WR_ADDR */
 | |
| 		mminfra_write(subsys_base + 0x214 + shift, wr_addr);
 | |
| 		/* SUBSYS_FAKE_ENG_CON0 */
 | |
| 		mminfra_write(subsys_base + 0x208 + shift,
 | |
| 			(wr_pat << 24) | (loop << 22) | length);
 | |
| 		/* SUBSYS_FAKE_ENG_CON1 */
 | |
| 		mminfra_write(subsys_base + 0x20c + shift,
 | |
| 			(burst << 12) | (dis_wr << 11) | (dis_rd << 10) | latency);
 | |
| 		mminfra_write(subsys_base + 0x204 + shift, 0x1); /* SUBSYS_FAKE_ENG_RST */
 | |
| 		mminfra_write(subsys_base + 0x204 + shift, 0x0); /* SUBSYS_FAKE_ENG_RST */
 | |
| 		mminfra_write(subsys_base + 0x200 + shift, 0x3); /* SUBSYS_FAKE_ENG_EN */
 | |
| 
 | |
| 		mminfra_crit("%s id:%u eng:%u SUBSYS_FAKE_ENG_RD_ADDR=0x%x\n", __func__,
 | |
| 			id, eng_id, mminfra_read(subsys_base + 0x210 + shift));
 | |
| 		mminfra_crit("%s id:%u eng:%u SUBSYS_FAKE_ENG_WR_ADDR=0x%x\n", __func__,
 | |
| 			id, eng_id, mminfra_read(subsys_base + 0x214 + shift));
 | |
| 		mminfra_crit("%s id:%u eng:%u SUBSYS_FAKE_ENG_CON0=0x%x\n", __func__,
 | |
| 			id, eng_id, mminfra_read(subsys_base + 0x208 + shift));
 | |
| 		mminfra_crit("%s id:%u eng:%u SUBSYS_FAKE_ENG_CON1=0x%x\n", __func__,
 | |
| 			id, eng_id, mminfra_read(subsys_base + 0x20c + shift));
 | |
| 		mminfra_crit("%s id:%u eng:%u SUBSYS_FAKE_ENG_RST=0x%x\n", __func__,
 | |
| 			id, eng_id, mminfra_read(subsys_base + 0x204 + shift));
 | |
| 		mminfra_crit("%s id:%u eng:%u SUBSYS_FAKE_ENG_EN=0x%x\n", __func__,
 | |
| 			id, eng_id, mminfra_read(subsys_base + 0x200 + shift));
 | |
| 
 | |
| 		/* SUBSYS_FAKE_ENG_STATE */
 | |
| 		if ((mminfra_read(subsys_base + 0x218) & 0x1) != 0)
 | |
| 			mminfra_crit("%s id:%u eng:%u FAKE_ENG_STATE is busy\n",
 | |
| 				__func__, id, eng_id);
 | |
| 		else
 | |
| 			mminfra_crit("%s id:%u eng:%u FAKE_ENG_STATE is idle\n",
 | |
| 				__func__, id, eng_id);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static int init_ctrl_base(struct platform_device *pdev)
 | |
| {
 | |
| 	struct resource *res;
 | |
| 
 | |
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dispsys");
 | |
| 	if (!res) {
 | |
| 		dev_notice(dev, "could not get resource for dispsys\n");
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	dispsys_base = ioremap(res->start, resource_size(res));
 | |
| 	if (IS_ERR(dispsys_base)) {
 | |
| 		dev_notice(dev, "could not ioremap resource for dispsys:%d\n",
 | |
| 			PTR_ERR(dispsys_base));
 | |
| 		dispsys_base = NULL;
 | |
| 	}
 | |
| 
 | |
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dispsys1");
 | |
| 	if (!res)
 | |
| 		dev_notice(dev, "could not get resource for dispsys1\n");
 | |
| 
 | |
| 	if (res) {
 | |
| 		dispsys1_base = ioremap(res->start, resource_size(res));
 | |
| 		if (IS_ERR(dispsys1_base)) {
 | |
| 			dev_notice(dev, "could not ioremap resource for dispsys1\n");
 | |
| 			dispsys1_base = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mdpsys");
 | |
| 	if (!res)
 | |
| 		dev_notice(dev, "could not get resource for mdpsys\n");
 | |
| 
 | |
| 	if (res) {
 | |
| 		mdpsys_base = ioremap(res->start, resource_size(res));
 | |
| 		if (IS_ERR(mdpsys_base)) {
 | |
| 			dev_notice(dev, "could not ioremap resource for mdpsys\n");
 | |
| 			mdpsys_base = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mdpsys1");
 | |
| 	if (!res)
 | |
| 		dev_notice(dev, "could not get resource for mdpsys1\n");
 | |
| 
 | |
| 	if (res) {
 | |
| 		mdpsys1_base = ioremap(res->start, resource_size(res));
 | |
| 		if (IS_ERR(mdpsys1_base)) {
 | |
| 			dev_notice(dev, "could not ioremap resource for mdpsys1\n");
 | |
| 			mdpsys1_base = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "disp_larb_0");
 | |
| 	if (!res)
 | |
| 		dev_notice(dev, "could not get resource for disp_larb_0\n");
 | |
| 
 | |
| 	if (res) {
 | |
| 		disp_larb_0_base = ioremap(res->start, resource_size(res));
 | |
| 		if (IS_ERR(disp_larb_0_base)) {
 | |
| 			dev_notice(dev, "could not ioremap resource for disp_larb_0\n");
 | |
| 			disp_larb_0_base = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "disp_larb_1");
 | |
| 	if (!res)
 | |
| 		dev_notice(dev, "could not get resource for disp_larb_1\n");
 | |
| 
 | |
| 	if (res) {
 | |
| 		disp_larb_1_base = ioremap(res->start, resource_size(res));
 | |
| 		if (IS_ERR(disp_larb_1_base)) {
 | |
| 			dev_notice(dev, "could not ioremap resource for disp_larb_1\n");
 | |
| 			disp_larb_1_base = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "disp_larb_2");
 | |
| 	if (!res)
 | |
| 		dev_notice(dev, "could not get resource for disp_larb_2\n");
 | |
| 
 | |
| 	if (res) {
 | |
| 		disp_larb_2_base = ioremap(res->start, resource_size(res));
 | |
| 		if (IS_ERR(disp_larb_2_base)) {
 | |
| 			dev_notice(dev, "could not ioremap resource for disp_larb_2\n");
 | |
| 			disp_larb_2_base = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "disp_larb_3");
 | |
| 	if (!res)
 | |
| 		dev_notice(dev, "could not get resource for disp_larb_3\n");
 | |
| 
 | |
| 	if (res) {
 | |
| 		disp_larb_3_base = ioremap(res->start, resource_size(res));
 | |
| 		if (IS_ERR(disp_larb_3_base)) {
 | |
| 			dev_notice(dev, "could not ioremap resource for disp_larb_3\n");
 | |
| 			disp_larb_3_base = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mdp_larb_0");
 | |
| 	if (!res)
 | |
| 		dev_notice(dev, "could not get resource for mdp_larb_0\n");
 | |
| 
 | |
| 	if (res) {
 | |
| 		mdp_larb_0_base = ioremap(res->start, resource_size(res));
 | |
| 		if (IS_ERR(mdp_larb_0_base)) {
 | |
| 			dev_notice(dev, "could not ioremap resource for mdp_larb_0\n");
 | |
| 			mdp_larb_0_base = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mdp_larb_1");
 | |
| 	if (!res)
 | |
| 		dev_notice(dev, "could not get resource for mdp_larb_1\n");
 | |
| 
 | |
| 	if (res) {
 | |
| 		mdp_larb_1_base = ioremap(res->start, resource_size(res));
 | |
| 		if (IS_ERR(mdp_larb_1_base)) {
 | |
| 			dev_notice(dev, "could not ioremap resource for mdp_larb_1\n");
 | |
| 			mdp_larb_1_base = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mdp_larb_2");
 | |
| 	if (!res)
 | |
| 		dev_notice(dev, "could not get resource for mdp_larb_2\n");
 | |
| 
 | |
| 	if (res) {
 | |
| 		mdp_larb_2_base = ioremap(res->start, resource_size(res));
 | |
| 		if (IS_ERR(mdp_larb_2_base)) {
 | |
| 			dev_notice(dev, "could not ioremap resource for mdp_larb_2\n");
 | |
| 			mdp_larb_2_base = NULL;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	of_property_read_u32(dev->of_node, "disp-larb0-fake-port", &disp_larb_0_fake);
 | |
| 	of_property_read_u32(dev->of_node, "disp-larb1-fake-port", &disp_larb_1_fake);
 | |
| 	of_property_read_u32(dev->of_node, "disp-larb2-fake-port", &disp_larb_2_fake);
 | |
| 	of_property_read_u32(dev->of_node, "disp-larb3-fake-port", &disp_larb_3_fake);
 | |
| 
 | |
| 	of_property_read_u32(dev->of_node, "mdp-larb0-fake-port", &mdp_larb_0_fake);
 | |
| 	of_property_read_u32(dev->of_node, "mdp-larb1-fake-port", &mdp_larb_1_fake);
 | |
| 	of_property_read_u32(dev->of_node, "mdp-larb2-fake-port", &mdp_larb_2_fake);
 | |
| 
 | |
| 	of_property_read_u32(dev->of_node, "mm-sram-base", &mm_sram_base);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int do_mminfra_imax(const char *val, const struct kernel_param *kp)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 	unsigned int latency, is_sram;
 | |
| 	void *dram_base;
 | |
| 	dma_addr_t dram_phy_base;
 | |
| #if IS_ENABLED(CONFIG_MTK_SLBC)
 | |
| 	struct slbc_data sram_data;
 | |
| #endif
 | |
| 
 | |
| 	ret = sscanf(val, "%u %u", &latency, &is_sram);
 | |
| 	if (ret != 2) {
 | |
| 		pr_notice("%s: invalid input: %s, result(%d)\n", __func__, val, ret);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	if (!is_init) {
 | |
| 		init_ctrl_base(g_pdev);
 | |
| 		is_init = true;
 | |
| 	}
 | |
| 	init_mmsys();
 | |
| 	init_smi();
 | |
| 	cmdq_util_mminfra_cmd(2);
 | |
| 
 | |
| #if IS_ENABLED(CONFIG_MTK_SLBC)
 | |
| 	sram_data.uid = UID_MML;
 | |
| 	sram_data.type = TP_BUFFER;
 | |
| 	//sram_data.flag = FG_POWER;
 | |
| 	ret = slbc_request(&sram_data);
 | |
| 
 | |
| 	if (ret >= 0)
 | |
| 		mm_sram_base = (unsigned long)sram_data.paddr;
 | |
| #endif
 | |
| 
 | |
| 	if (mm_sram_base) {
 | |
| 		fake_eng_set(MDPSYS_BASE, mdpsys_base, 0, mm_sram_base, mm_sram_base,
 | |
| 			4, 255, 7, 0, 0, latency, 1);
 | |
| 		fake_eng_set(MDPSYS1_BASE, mdpsys1_base, 0, mm_sram_base, mm_sram_base,
 | |
| 			4, 255, 7, 0, 0, latency, 1);
 | |
| 	}
 | |
| 
 | |
| 	dram_base = dma_alloc_attrs(dev, 1024*1024, &dram_phy_base,
 | |
| 				GFP_KERNEL, DMA_ATTR_FORCE_CONTIGUOUS);
 | |
| 	if (!dram_base) {
 | |
| 		mminfra_crit("%s: allocate dram memory failed\n", __func__);
 | |
| 		return -ENOMEM;
 | |
| 	}
 | |
| 
 | |
| 	fake_eng_set(DISPSYS_BASE, dispsys_base, 0, dram_phy_base, dram_phy_base,
 | |
| 		4, 255, 7, 0, 0, latency, 1);
 | |
| 	fake_eng_set(DISPSYS_BASE, dispsys_base, 1, dram_phy_base, dram_phy_base,
 | |
| 		4, 255, 7, 0, 0, latency, 1);
 | |
| 	fake_eng_set(DISPSYS1_BASE, dispsys1_base, 0, dram_phy_base, dram_phy_base,
 | |
| 		4, 255, 7, 0, 0, latency, 1);
 | |
| 	fake_eng_set(DISPSYS1_BASE, dispsys1_base, 1, dram_phy_base, dram_phy_base,
 | |
| 		4, 255, 7, 0, 0, latency, 1);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static struct kernel_param_ops mminfra_imax_ops = {
 | |
| 	.set = do_mminfra_imax,
 | |
| };
 | |
| module_param_cb(mminfra_imax, &mminfra_imax_ops, NULL, 0644);
 | |
| MODULE_PARM_DESC(mminfra_imax, "mminfra imax");
 | |
| #endif
 | |
| 
 | |
| static int mminfra_imax_probe(struct platform_device *pdev)
 | |
| {
 | |
| 	g_pdev = pdev;
 | |
| 	dev = &pdev->dev;
 | |
| 
 | |
| 	of_property_read_u32(dev->of_node, "disp_larb0_fake_port", &disp_larb_0_fake);
 | |
| 	of_property_read_u32(dev->of_node, "disp_larb1_fake_port", &disp_larb_1_fake);
 | |
| 	of_property_read_u32(dev->of_node, "disp_larb2_fake_port", &disp_larb_2_fake);
 | |
| 	of_property_read_u32(dev->of_node, "disp_larb3_fake_port", &disp_larb_3_fake);
 | |
| 
 | |
| 	of_property_read_u32(dev->of_node, "mdp_larb0_fake_port", &mdp_larb_0_fake);
 | |
| 	of_property_read_u32(dev->of_node, "mdp_larb1_fake_port", &mdp_larb_1_fake);
 | |
| 	of_property_read_u32(dev->of_node, "mdp_larb2_fake_port", &mdp_larb_2_fake);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct of_device_id of_mminfra_imax_match_tbl[] = {
 | |
| 	{
 | |
| 		.compatible = "mediatek,mminfra-imax",
 | |
| 	},
 | |
| 	{}
 | |
| };
 | |
| 
 | |
| static struct platform_driver mminfra_imax_drv = {
 | |
| 	.probe = mminfra_imax_probe,
 | |
| 	.driver = {
 | |
| 		.name = "mtk-mminfra-imax",
 | |
| 		.of_match_table = of_mminfra_imax_match_tbl,
 | |
| 	},
 | |
| };
 | |
| 
 | |
| static int __init mtk_mminfra_imax_init(void)
 | |
| {
 | |
| 	s32 status;
 | |
| 
 | |
| 	status = platform_driver_register(&mminfra_imax_drv);
 | |
| 	if (status) {
 | |
| 		pr_notice("Failed to register MMInfra imax driver(%d)\n", status);
 | |
| 		return -ENODEV;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void __exit mtk_mminfra_imax_exit(void)
 | |
| {
 | |
| 	platform_driver_unregister(&mminfra_imax_drv);
 | |
| }
 | |
| 
 | |
| module_init(mtk_mminfra_imax_init);
 | |
| module_exit(mtk_mminfra_imax_exit);
 | |
| MODULE_DESCRIPTION("MTK MMInfra IMAX driver");
 | |
| MODULE_AUTHOR("Anthony Huang<anthony.huang@mediatek.com>");
 | |
| MODULE_LICENSE("GPL v2");
 |