CHROMIUM: MALI: Add Mali platform specific source

Create platform specific folder and source files
Add clock, power and regulator control
Support for GPU devfreq

BUG=b:109911488
Test=emerge-kukui sys-kernel/chromeos-kernel-4_19

Change-Id: I4e55458efb008553050e78703e09ac69d07ea21d
Signed-off-by: Nick Fan <Nick.Fan@mediatek.com>
Reviewed-on: https://chromium-review.googlesource.com/1317240
Commit-Ready: Nicolas Boichat <drinkcat@chromium.org>
Tested-by: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-by: Tomasz Figa <tfiga@chromium.org>
Reviewed-by: Nick Fan <nick.fan@mediatek.corp-partner.google.com>
diff --git a/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c b/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c
index 683a24c..88358e0 100644
--- a/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c
+++ b/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c
@@ -79,6 +79,98 @@
 	return freq;
 }
 
+static void voltage_range_check(unsigned long *voltage)
+{
+	voltage[1] = ((voltage[1] - voltage[0]) < MIN_VOLT_BIAS) ?
+		(voltage[0] + MIN_VOLT_BIAS) : voltage[1];
+	voltage[1] = ((voltage[1] - voltage[0]) > MAX_VOLT_BIAS) ?
+		(voltage[0] + MIN_VOLT_BIAS) : voltage[1];
+	voltage[1] = min_t(unsigned long, max_t(unsigned long, voltage[1],
+		VSRAM_GPU_MIN_VOLT), VSRAM_GPU_MAX_VOLT);
+}
+
+static bool get_step_volt(unsigned long *step_volt,
+	unsigned long *target_volt, int count, bool inc)
+{
+	unsigned long regulator_min_volt;
+	unsigned long regulator_max_volt;
+	unsigned long current_bias;
+	long adjust_step;
+	int i;
+
+	if (inc) {
+		current_bias = target_volt[1] - step_volt[0];
+		adjust_step = MIN_VOLT_BIAS;
+	} else {
+		current_bias = step_volt[1] - target_volt[0];
+		adjust_step = -MIN_VOLT_BIAS;
+	}
+
+	for (i = 0; i < count; ++i)
+		if (step_volt[i] != target_volt[i])
+			break;
+
+	if (i == count)
+		return 0;
+
+	for (i = 0; i < count; i++) {
+		if (i) {
+			regulator_min_volt = VSRAM_GPU_MIN_VOLT;
+			regulator_max_volt = VSRAM_GPU_MAX_VOLT;
+		} else {
+			regulator_min_volt = VGPU_MIN_VOLT;
+			regulator_max_volt = VGPU_MAX_VOLT;
+		}
+
+		if (current_bias > MAX_VOLT_BIAS) {
+			step_volt[i] = min(max(step_volt[0] + adjust_step,
+				regulator_min_volt), regulator_max_volt);
+		} else {
+			step_volt[i] = target_volt[i];
+		}
+	}
+	return 1;
+}
+
+static int
+set_voltages(struct kbase_device *kbdev, unsigned long *target_volt, int inc)
+{
+	unsigned long step_volt[REGULATOR_NUM];
+	ssize_t first, step;
+	ssize_t i;
+	int err;
+
+	for (i = 0; i < kbdev->regulator_num; ++i)
+		step_volt[i] = kbdev->current_voltage[i];
+
+	if (inc) {
+		first = kbdev->regulator_num - 1;
+		step = -1;
+	} else {
+		first = 0;
+		step = 1;
+	}
+
+	while (get_step_volt(step_volt, target_volt,
+			kbdev->regulator_num, inc)) {
+		for (i = first; i >= 0 && i < kbdev->regulator_num; i += step) {
+			if (kbdev->current_voltage[i] == step_volt[i])
+				continue;
+
+			err = regulator_set_voltage(kbdev->regulator[i],
+					step_volt[i], step_volt[i] + VOLT_TOL);
+
+			if (err) {
+				dev_err(kbdev->dev, "Failed to set reg %d voltage err:(%d)\n",
+					i, err);
+				return err;
+			}
+		}
+	}
+
+	return 0;
+}
+
 static int
 kbase_devfreq_target(struct device *dev, unsigned long *target_freq, u32 flags)
 {
@@ -86,9 +178,10 @@
 	struct dev_pm_opp *opp;
 	unsigned long nominal_freq;
 	unsigned long freq = 0;
-	unsigned long voltage;
-	int err;
+	unsigned long target_volt[REGULATOR_NUM];
+	int err, i;
 	u64 core_mask;
+	struct mfg_base *mfg = kbdev->platform_context;
 
 	freq = *target_freq;
 
@@ -96,7 +189,6 @@
 	rcu_read_lock();
 #endif
 	opp = devfreq_recommended_opp(dev, &freq, flags);
-	voltage = dev_pm_opp_get_voltage(opp);
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
 	rcu_read_unlock();
 #endif
@@ -104,6 +196,9 @@
 		dev_err(dev, "Failed to get opp (%ld)\n", PTR_ERR(opp));
 		return PTR_ERR(opp);
 	}
+
+	for (i = 0; i < kbdev->regulator_num; i++)
+		target_volt[i] = dev_pm_opp_get_voltage_supply(opp, i);
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
 	dev_pm_opp_put(opp);
 #endif
@@ -113,36 +208,51 @@
 	/*
 	 * Only update if there is a change of frequency
 	 */
-	if (kbdev->current_nominal_freq == nominal_freq) {
+	if (kbdev->current_nominal_freq == nominal_freq &&
+		kbdev->current_voltage[0] == target_volt[0]) {
 		*target_freq = nominal_freq;
 		return 0;
 	}
 
 	freq = opp_translate(kbdev, nominal_freq, &core_mask);
+	voltage_range_check(target_volt);
+
 #ifdef CONFIG_REGULATOR
-	if (kbdev->regulator && kbdev->current_voltage != voltage
-			&& kbdev->current_freq < freq) {
-		err = regulator_set_voltage(kbdev->regulator, voltage, voltage);
+	if (kbdev->current_voltage[0] < target_volt[0]) {
+		err = set_voltages(kbdev, target_volt, 1);
 		if (err) {
-			dev_err(dev, "Failed to increase voltage (%d)\n", err);
+			dev_err(kbdev->dev, "Failed to increase voltage\n");
 			return err;
 		}
 	}
 #endif
 
-	err = clk_set_rate(kbdev->clock, freq);
-	if (err) {
-		dev_err(dev, "Failed to set clock %lu (target %lu)\n",
-				freq, *target_freq);
-		return err;
+	if (kbdev->current_freq != freq) {
+		err = clk_set_parent(mfg->clk_mux, mfg->clk_sub_parent);
+		if (err) {
+			dev_err(kbdev->dev, "Failed to select sub clock src\n");
+			return err;
+		}
+
+		err = clk_set_rate(kbdev->clock, freq);
+		if (err) {
+			dev_err(dev, "Failed to set clock %lu (target %lu)\n",
+					freq, *target_freq);
+			return err;
+		}
+
+		err = clk_set_parent(mfg->clk_mux, mfg->clk_main_parent);
+		if (err) {
+			dev_err(kbdev->dev, "Failed to select main clock src\n");
+			return err;
+		}
 	}
 
 #ifdef CONFIG_REGULATOR
-	if (kbdev->regulator && kbdev->current_voltage != voltage
-			&& kbdev->current_freq > freq) {
-		err = regulator_set_voltage(kbdev->regulator, voltage, voltage);
+	if (kbdev->current_voltage[0] > target_volt[0]) {
+		err = set_voltages(kbdev, target_volt, 0);
 		if (err) {
-			dev_err(dev, "Failed to decrease voltage (%d)\n", err);
+			dev_err(kbdev->dev, "Failed to decrease voltage\n");
 			return err;
 		}
 	}
@@ -151,7 +261,8 @@
 	kbase_devfreq_set_core_mask(kbdev, core_mask);
 
 	*target_freq = nominal_freq;
-	kbdev->current_voltage = voltage;
+	for (i = 0; i < kbdev->regulator_num; i++)
+		kbdev->current_voltage[i] = target_volt[i];
 	kbdev->current_nominal_freq = nominal_freq;
 	kbdev->current_freq = freq;
 	kbdev->current_core_mask = core_mask;
@@ -321,7 +432,7 @@
 		kbdev->opp_table[i].core_mask = core_mask;
 
 		dev_info(kbdev->dev, "OPP %d : opp_freq=%llu real_freq=%llu core_mask=%llx\n",
-				i, opp_freq, real_freq, core_mask);
+			i, opp_freq, real_freq, core_mask);
 
 		i++;
 	}
diff --git a/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_debugfs.c b/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_debugfs.c
index 071a530..814bb4c 100644
--- a/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_debugfs.c
+++ b/drivers/gpu/arm/midgard/ipa/mali_kbase_ipa_debugfs.c
@@ -196,7 +196,7 @@
 
 	kbase_pm_context_active(kbdev);
 	kbase_get_real_power(df, &power,
-		kbdev->current_nominal_freq, (kbdev->current_voltage / 1000));
+		kbdev->current_nominal_freq, (kbdev->current_voltage[0] / 1000));
 	kbase_pm_context_idle(kbdev);
 
 	*val = power;
diff --git a/drivers/gpu/arm/midgard/mali_kbase_core_linux.c b/drivers/gpu/arm/midgard/mali_kbase_core_linux.c
index d44ebd9..cc7b94f 100644
--- a/drivers/gpu/arm/midgard/mali_kbase_core_linux.c
+++ b/drivers/gpu/arm/midgard/mali_kbase_core_linux.c
@@ -3134,23 +3134,41 @@
 {
 	struct kbase_device *kbdev = to_kbase_device(&pdev->dev);
 	int err = 0;
+	const char **reg_names;
+#if defined(CONFIG_REGULATOR)
+	int i;
+#endif
 
 	if (!kbdev)
 		return -ENODEV;
 
+	kbdev->regulator_num = of_property_count_strings(kbdev->dev->of_node,
+		"supply-names");
+
+	reg_names = kcalloc(kbdev->regulator_num, sizeof(char *), GFP_KERNEL);
+
+	if (of_property_read_string_array(kbdev->dev->of_node, "supply-names",
+		reg_names, kbdev->regulator_num) < 0) {
+		dev_err(&pdev->dev, "Failed to get supply names\n");
+		err = -EINVAL;
+		goto fail;
+	}
+
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) && defined(CONFIG_OF) \
 			&& defined(CONFIG_REGULATOR)
-	kbdev->regulator = regulator_get_optional(kbdev->dev, "mali");
-	if (IS_ERR_OR_NULL(kbdev->regulator)) {
-		err = PTR_ERR(kbdev->regulator);
-		kbdev->regulator = NULL;
-		if (err == -EPROBE_DEFER) {
-			dev_err(&pdev->dev, "Failed to get regulator\n");
-			return err;
+	for (i = 0; i < kbdev->regulator_num; i++) {
+		kbdev->regulator[i] = regulator_get_optional(kbdev->dev, reg_names[i]);
+		if (IS_ERR_OR_NULL(kbdev->regulator[i])) {
+			err = PTR_ERR(kbdev->regulator[i]);
+			kbdev->regulator[i] = NULL;
+			if (err == -EPROBE_DEFER) {
+				dev_err(&pdev->dev, "Failed to get regulator\n");
+				goto fail;
+			}
+			dev_info(kbdev->dev,
+				"Continuing without %s regulator control\n", reg_names[i]);
+			/* Allow probe to continue without regulator */
 		}
-		dev_info(kbdev->dev,
-			"Continuing without Mali regulator control\n");
-		/* Allow probe to continue without regulator */
 	}
 #endif /* LINUX_VERSION_CODE >= 3, 12, 0 */
 
@@ -3178,6 +3196,15 @@
 	/* Register the OPPs if they are available in device tree */
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) \
 	|| defined(LSK_OPPV2_BACKPORT)
+#if defined(CONFIG_REGULATOR)
+	kbdev->dev_opp_table = dev_pm_opp_set_regulators(kbdev->dev, reg_names, 2);
+	if (IS_ERR(kbdev->dev_opp_table)) {
+		err = PTR_ERR(kbdev->dev_opp_table);
+		kbdev->dev_opp_table = NULL;
+		dev_err(kbdev->dev, "Failed to set regulators for GPU err: %d\n",
+			err);
+	}
+#endif /* CONFIG_REGULATOR */
 	err = dev_pm_opp_of_add_table(kbdev->dev);
 #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0))
 	err = of_init_opp_table(kbdev->dev);
@@ -3188,6 +3215,8 @@
 		dev_dbg(kbdev->dev, "OPP table not found\n");
 #endif /* CONFIG_OF && CONFIG_PM_OPP */
 
+	kfree(reg_names);
+
 	return 0;
 
 fail:
@@ -3198,17 +3227,22 @@
 }
 
 #ifdef CONFIG_REGULATOR
-	if (NULL != kbdev->regulator) {
-		regulator_put(kbdev->regulator);
-		kbdev->regulator = NULL;
+	for (i = 0; i < kbdev->regulator_num; i++) {
+		if (NULL != kbdev->regulator[i]) {
+			regulator_put(kbdev->regulator[i]);
+			kbdev->regulator[i] = NULL;
+		}
 	}
 #endif
 
+	kfree(reg_names);
+
 	return err;
 }
 
 static void power_control_term(struct kbase_device *kbdev)
 {
+	int i;
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)) || \
 		defined(LSK_OPPV2_BACKPORT)
 	dev_pm_opp_of_remove_table(kbdev->dev);
@@ -3224,11 +3258,20 @@
 
 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)) && defined(CONFIG_OF) \
 			&& defined(CONFIG_REGULATOR)
-	if (kbdev->regulator) {
-		regulator_put(kbdev->regulator);
-		kbdev->regulator = NULL;
+	for (i = 0; i < kbdev->regulator_num; i++) {
+		if (kbdev->regulator[i]) {
+			regulator_put(kbdev->regulator[i]);
+			kbdev->regulator[i] = NULL;
+		}
 	}
 #endif /* LINUX_VERSION_CODE >= 3, 12, 0 */
+
+#ifdef CONFIG_REGULATOR
+	if (kbdev->dev_opp_table != NULL) {
+		dev_pm_opp_put_regulators(kbdev->dev_opp_table);
+		kbdev->dev_opp_table = NULL;
+	}
+#endif
 }
 
 #ifdef MALI_KBASE_BUILD
diff --git a/drivers/gpu/arm/midgard/mali_kbase_defs.h b/drivers/gpu/arm/midgard/mali_kbase_defs.h
index 4adfe35..6c978cc 100644
--- a/drivers/gpu/arm/midgard/mali_kbase_defs.h
+++ b/drivers/gpu/arm/midgard/mali_kbase_defs.h
@@ -1165,6 +1165,9 @@
 
 
 #define DEVNAME_SIZE	16
+#if defined(CONFIG_REGULATOR)
+#define REGULATOR_NUM	2
+#endif
 
 
 /**
@@ -1429,7 +1432,9 @@
 
 	struct clk *clock;
 #ifdef CONFIG_REGULATOR
-	struct regulator *regulator;
+	int regulator_num;
+	struct regulator *regulator[REGULATOR_NUM];
+	struct opp_table *dev_opp_table;
 #endif
 	char devname[DEVNAME_SIZE];
 
@@ -1515,7 +1520,7 @@
 	struct devfreq *devfreq;
 	unsigned long current_freq;
 	unsigned long current_nominal_freq;
-	unsigned long current_voltage;
+	unsigned long current_voltage[REGULATOR_NUM];
 	u64 current_core_mask;
 	struct kbase_devfreq_opp *opp_table;
 	int num_opps;
diff --git a/drivers/gpu/arm/midgard/platform/mediatek/Kbuild b/drivers/gpu/arm/midgard/platform/mediatek/Kbuild
new file mode 100644
index 0000000..c1161b0
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/mediatek/Kbuild
@@ -0,0 +1,14 @@
+# Copyright (C) 2018 MediaTek Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+
+mali_kbase-y += \
+	$(MALI_PLATFORM_DIR)/mali_kbase_config_mediatek.o \
+	$(MALI_PLATFORM_DIR)/mali_kbase_runtime_pm.o
diff --git a/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_mediatek.c b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_mediatek.c
new file mode 100644
index 0000000..a273717
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_mediatek.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+
+#include <mali_kbase_config.h>
+
+int kbase_platform_early_init(void)
+{
+	/* Nothing needed at this stage */
+	return 0;
+}
+
+static struct kbase_platform_config dummy_platform_config;
+
+struct kbase_platform_config *kbase_get_platform_config(void)
+{
+	return &dummy_platform_config;
+}
+
+int kbase_platform_register(void)
+{
+	return 0;
+}
+
+void kbase_platform_unregister(void)
+{
+}
diff --git a/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_platform.h b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_platform.h
new file mode 100644
index 0000000..848b533
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_config_platform.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+struct mfg_base {
+	struct clk *clk_mux;
+	struct clk *clk_main_parent;
+	struct clk *clk_sub_parent;
+	struct clk *subsys_mfg_cg;
+	struct platform_device *gpu_core1_dev;
+	struct platform_device *gpu_core2_dev;
+	bool is_powered;
+};
+
+/* Definition for PMIC regulators */
+#define VSRAM_GPU_MAX_VOLT (925000)	/* uV */
+#define VSRAM_GPU_MIN_VOLT (850000) /* uV */
+#define VGPU_MAX_VOLT (825000)	/* uV */
+#define VGPU_MIN_VOLT (625000)	/* uV */
+
+#define MIN_VOLT_BIAS (100000) /* uV */
+#define MAX_VOLT_BIAS (250000) /* uV */
+#define VOLT_TOL (125) /* uV */
+
+/**
+ * Maximum frequency GPU will be clocked at. Given in kHz.
+ * This must be specified as there is no default value.
+ *
+ * Attached value: number in kHz
+ * Default value: NA
+ */
+#define GPU_FREQ_KHZ_MAX (800000)
+/**
+ * Minimum frequency GPU will be clocked at. Given in kHz.
+ * This must be specified as there is no default value.
+ *
+ * Attached value: number in kHz
+ * Default value: NA
+ */
+#define GPU_FREQ_KHZ_MIN (300000)
+
+/**
+ * CPU_SPEED_FUNC - A pointer to a function that calculates the CPU clock
+ *
+ * CPU clock speed of the platform is in MHz - see kbase_cpu_clk_speed_func
+ * for the function prototype.
+ *
+ * Attached value: A kbase_cpu_clk_speed_func.
+ * Default Value:  NA
+ */
+#define CPU_SPEED_FUNC (NULL)
+
+/**
+ * GPU_SPEED_FUNC - A pointer to a function that calculates the GPU clock
+ *
+ * GPU clock speed of the platform in MHz - see kbase_gpu_clk_speed_func
+ * for the function prototype.
+ *
+ * Attached value: A kbase_gpu_clk_speed_func.
+ * Default Value:  NA
+ */
+#define GPU_SPEED_FUNC (NULL)
+
+/**
+ * Power management configuration
+ *
+ * Attached value: pointer to @ref kbase_pm_callback_conf
+ * Default value: See @ref kbase_pm_callback_conf
+ */
+#define POWER_MANAGEMENT_CALLBACKS (&pm_callbacks)
+
+/**
+ * Platform specific configuration functions
+ *
+ * Attached value: pointer to @ref kbase_platform_funcs_conf
+ * Default value: See @ref kbase_platform_funcs_conf
+ */
+#define PLATFORM_FUNCS (&platform_funcs)
+
+extern struct kbase_pm_callback_conf pm_callbacks;
+extern struct kbase_platform_funcs_conf platform_funcs;
+
+/**
+ * Autosuspend delay
+ *
+ * The delay time (in milliseconds) to be used for autosuspend
+ */
+#define AUTO_SUSPEND_DELAY (100)
diff --git a/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_runtime_pm.c b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_runtime_pm.c
new file mode 100644
index 0000000..a18ae2c
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/mediatek/mali_kbase_runtime_pm.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2018 MediaTek Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See http://www.gnu.org/licenses/gpl-2.0.html for more details.
+ */
+
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <mali_kbase.h>
+#include "mali_kbase_config_platform.h"
+#include <mali_kbase_defs.h>
+
+static struct platform_device *probe_gpu_core1_dev;
+static struct platform_device *probe_gpu_core2_dev;
+
+static const struct of_device_id mtk_gpu_corex_of_ids[] = {
+	{ .compatible = "mediatek,gpu_core1", .data = "1" },
+	{ .compatible = "mediatek,gpu_core2", .data = "2" },
+	{}
+};
+
+static int mtk_gpu_corex_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	const char *tmp;
+
+	match = of_match_device(mtk_gpu_corex_of_ids, dev);
+	if (!match)
+		return -ENODEV;
+	tmp = match->data;
+	if (*tmp == '1')
+		probe_gpu_core1_dev = pdev;
+	else
+		probe_gpu_core2_dev = pdev;
+
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+}
+
+static int mtk_gpu_corex_remove(struct platform_device *pdev)
+{
+	pm_runtime_disable(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver mtk_gpu_corex_driver = {
+	.probe  = mtk_gpu_corex_probe,
+	.remove = mtk_gpu_corex_remove,
+	.driver = {
+		.name = "gpu_corex",
+		.of_match_table = mtk_gpu_corex_of_ids,
+	}
+};
+
+static int pm_callback_power_on(struct kbase_device *kbdev)
+{
+	int error, i;
+	struct mfg_base *mfg = kbdev->platform_context;
+
+	if (mfg->is_powered) {
+		dev_dbg(kbdev->dev, "mali_device is already powered\n");
+		return 0;
+	}
+
+	for (i = 0; i < kbdev->regulator_num; i++) {
+		error = regulator_enable(kbdev->regulator[i]);
+		if (error < 0) {
+			dev_err(kbdev->dev,
+				"Power on reg %d failed error = %d\n",
+				i, error);
+			return error;
+		}
+	}
+
+	error = pm_runtime_get_sync(kbdev->dev);
+	if (error < 0) {
+		dev_err(kbdev->dev,
+			"Power on core 0 failed (err: %d)\n", error);
+		return error;
+	}
+
+	error = pm_runtime_get_sync(&mfg->gpu_core1_dev->dev);
+	if (error < 0) {
+		dev_err(kbdev->dev,
+			"Power on core 1 failed (err: %d)\n", error);
+		return error;
+	}
+
+	error = pm_runtime_get_sync(&mfg->gpu_core2_dev->dev);
+	if (error < 0) {
+		dev_err(kbdev->dev,
+			"Power on core 2 failed (err: %d)\n", error);
+		return error;
+	}
+
+	error = clk_prepare_enable(mfg->clk_main_parent);
+	if (error < 0) {
+		dev_err(kbdev->dev,
+			"clk_main_parent clock enable failed (err: %d)\n",
+			error);
+		return error;
+	}
+
+	error = clk_prepare_enable(mfg->clk_mux);
+	if (error < 0) {
+		dev_err(kbdev->dev,
+			"clk_mux clock enable failed (err: %d)\n", error);
+		return error;
+	}
+
+	error = clk_prepare_enable(mfg->subsys_mfg_cg);
+	if (error < 0) {
+		dev_err(kbdev->dev,
+			"subsys_mfg_cg clock enable failed (err: %d)\n", error);
+		return error;
+	}
+
+	mfg->is_powered = true;
+
+	return 1;
+}
+
+static void pm_callback_power_off(struct kbase_device *kbdev)
+{
+	struct mfg_base *mfg = kbdev->platform_context;
+	int error, i;
+
+	if (!mfg->is_powered) {
+		dev_dbg(kbdev->dev, "mali_device is already powered off\n");
+		return;
+	}
+
+	mfg->is_powered = false;
+
+	clk_disable_unprepare(mfg->subsys_mfg_cg);
+
+	clk_disable_unprepare(mfg->clk_mux);
+
+	clk_disable_unprepare(mfg->clk_main_parent);
+
+	error = pm_runtime_put_sync(&mfg->gpu_core2_dev->dev);
+	if (error < 0)
+		dev_err(kbdev->dev,
+		"Power off core 2 failed (err: %d)\n", error);
+
+	error = pm_runtime_put_sync(&mfg->gpu_core1_dev->dev);
+	if (error < 0)
+		dev_err(kbdev->dev,
+		"Power off core 1 failed (err: %d)\n", error);
+
+	error = pm_runtime_put_sync(kbdev->dev);
+	if (error < 0)
+		dev_err(kbdev->dev,
+		"Power off core 0 failed (err: %d)\n", error);
+
+	for (i = 0; i < kbdev->regulator_num; i++) {
+		error = regulator_disable(kbdev->regulator[i]);
+		if (error < 0) {
+			dev_err(kbdev->dev,
+				"Power off reg %d failed error = %d\n",
+				i, error);
+		}
+	}
+}
+
+static int kbase_device_runtime_init(struct kbase_device *kbdev)
+{
+	dev_dbg(kbdev->dev, "%s\n", __func__);
+
+	return 0;
+}
+
+static void kbase_device_runtime_disable(struct kbase_device *kbdev)
+{
+	dev_dbg(kbdev->dev, "%s\n", __func__);
+}
+
+static int pm_callback_runtime_on(struct kbase_device *kbdev)
+{
+	dev_dbg(kbdev->dev, "%s\n", __func__);
+
+	return 0;
+}
+
+static void pm_callback_runtime_off(struct kbase_device *kbdev)
+{
+	dev_dbg(kbdev->dev, "%s\n", __func__);
+}
+
+static void pm_callback_resume(struct kbase_device *kbdev)
+{
+	pm_callback_power_on(kbdev);
+}
+
+static void pm_callback_suspend(struct kbase_device *kbdev)
+{
+	pm_callback_power_off(kbdev);
+}
+
+struct kbase_pm_callback_conf pm_callbacks = {
+	.power_on_callback = pm_callback_power_on,
+	.power_off_callback = pm_callback_power_off,
+	.power_suspend_callback = pm_callback_suspend,
+	.power_resume_callback = pm_callback_resume,
+#ifdef KBASE_PM_RUNTIME
+	.power_runtime_init_callback = kbase_device_runtime_init,
+	.power_runtime_term_callback = kbase_device_runtime_disable,
+	.power_runtime_on_callback = pm_callback_runtime_on,
+	.power_runtime_off_callback = pm_callback_runtime_off,
+#else				/* KBASE_PM_RUNTIME */
+	.power_runtime_init_callback = NULL,
+	.power_runtime_term_callback = NULL,
+	.power_runtime_on_callback = NULL,
+	.power_runtime_off_callback = NULL,
+#endif				/* KBASE_PM_RUNTIME */
+};
+
+
+int mali_mfgsys_init(struct kbase_device *kbdev, struct mfg_base *mfg)
+{
+	int err = 0, i;
+	unsigned long volt;
+
+	if (!probe_gpu_core1_dev || !probe_gpu_core2_dev) {
+		dev_err(kbdev->dev,
+		"Wait for other power domain ret: %d\n", -EPROBE_DEFER);
+		return -EPROBE_DEFER;
+	}
+
+	for (i = 0; i < kbdev->regulator_num; i++)
+		if (kbdev->regulator[i] == NULL)
+			return -EINVAL;
+
+	mfg->gpu_core1_dev = probe_gpu_core1_dev;
+	mfg->gpu_core2_dev = probe_gpu_core2_dev;
+
+	mfg->clk_main_parent = devm_clk_get(kbdev->dev, "clk_main_parent");
+	if (IS_ERR(mfg->clk_main_parent)) {
+		err = PTR_ERR(mfg->clk_main_parent);
+		dev_err(kbdev->dev, "devm_clk_get clk_main_parent failed\n");
+		return err;
+	}
+
+	mfg->clk_sub_parent = devm_clk_get(kbdev->dev, "clk_sub_parent");
+	if (IS_ERR(mfg->clk_sub_parent)) {
+		err = PTR_ERR(mfg->clk_sub_parent);
+		dev_err(kbdev->dev, "devm_clk_get clk_sub_parent failed\n");
+		return err;
+	}
+
+	mfg->clk_mux = devm_clk_get(kbdev->dev, "clk_mux");
+	if (IS_ERR(mfg->clk_mux)) {
+		err = PTR_ERR(mfg->clk_mux);
+		dev_err(kbdev->dev, "devm_clk_get clk_mux failed\n");
+		return err;
+	}
+
+	mfg->subsys_mfg_cg = devm_clk_get(kbdev->dev, "subsys_mfg_cg");
+	if (IS_ERR(mfg->subsys_mfg_cg)) {
+		err = PTR_ERR(mfg->subsys_mfg_cg);
+		dev_err(kbdev->dev, "devm_clk_get subsys_mfg_cg failed\n");
+		return err;
+	}
+
+	for (i = 0; i < kbdev->regulator_num; i++) {
+		volt = (0 == i) ? VGPU_MAX_VOLT : VSRAM_GPU_MAX_VOLT;
+		err = regulator_set_voltage(kbdev->regulator[i],
+			volt, volt + VOLT_TOL);
+		if (err < 0) {
+			dev_err(kbdev->dev,
+				"Regulator %d set voltage failed: %d\n",
+				i, err);
+			return err;
+		}
+		kbdev->current_voltage[i] = volt;
+
+		err = regulator_enable(kbdev->regulator[i]);
+		if (err < 0) {
+			dev_err(kbdev->dev,
+				"Regulator %d enable failed: %d\n", i, err);
+			return err;
+		}
+	}
+
+	mfg->is_powered = false;
+
+	return 0;
+}
+
+void mali_mfgsys_deinit(struct kbase_device *kbdev)
+{
+	int err, i;
+
+	for (i = 0; i < kbdev->regulator_num; i++) {
+		err = regulator_disable(kbdev->regulator[i]);
+		if (err < 0) {
+			dev_err(kbdev->dev,
+				"Regulator %d disable failed: %d\n", i, err);
+		}
+	}
+}
+
+
+static int platform_init(struct kbase_device *kbdev)
+{
+	int err;
+	struct mfg_base *mfg;
+
+	mfg = kzalloc(sizeof(*mfg), GFP_KERNEL);
+	if (!mfg)
+		return -ENOMEM;
+
+	err = mali_mfgsys_init(kbdev, mfg);
+	if (err)
+		return err;
+
+	kbdev->platform_context = mfg;
+	pm_runtime_enable(kbdev->dev);
+
+	err = clk_set_parent(mfg->clk_mux, mfg->clk_sub_parent);
+	if (err) {
+		dev_err(kbdev->dev, "Failed to select sub clock src\n");
+		return err;
+	}
+
+	err = clk_set_rate(mfg->clk_main_parent, GPU_FREQ_KHZ_MAX * 1000);
+	if (err) {
+		dev_err(kbdev->dev, "Failed to set clock %d kHz\n",
+				GPU_FREQ_KHZ_MAX);
+		return err;
+	}
+
+	err = clk_set_parent(mfg->clk_mux, mfg->clk_main_parent);
+	if (err) {
+		dev_err(kbdev->dev, "Failed to select main clock src\n");
+		return err;
+	}
+
+	return 0;
+}
+
+static void platform_term(struct kbase_device *kbdev)
+{
+	struct mfg_base *mfg = kbdev->platform_context;
+
+	kfree(mfg);
+	kbdev->platform_context = NULL;
+	mali_mfgsys_deinit(kbdev);
+	pm_runtime_disable(kbdev->dev);
+}
+
+struct kbase_platform_funcs_conf platform_funcs = {
+	.platform_init_func = platform_init,
+	.platform_term_func = platform_term
+};
+
+static int __init mtk_mfg_corex(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&mtk_gpu_corex_driver);
+	if (ret != 0)
+		pr_debug("%s: Failed to register GPU core driver", __func__);
+
+	return ret;
+}
+
+subsys_initcall(mtk_mfg_corex);
+