CHROMIUM: MALI: Add support for Rockchip

This is a copy of what we had in the Chrome OS 4.4 kernel as of commit
50549bb894e8 ("UPSTREAM: HID: intel-ish-hid: Use VID/PID from ISH")
but with the following changes:

We handle the fact that CL:1325749 changed the mali platform code to
expect an array of regulators instead of a single regulator.

We handle the fact that CL:1433153 caused the mali platform code to no
longer prepare the clock for us.  ...so we switch to use a
prepare_enable, which seems to work (though I haven't done any deep
digging to check clock enables/disables).

We set PLATFORM_POWER_DOWN_ONLY.  As of CL:1539807 I have extended the
mali platform code to allow defining this for a given platform.  NOTE:
obviously this wouldn't be an upstream solution since upstream would
require things like this to be decided at runtime so you could have a
single kernel for multiple devices, but with the current setup of the
mali platform code there isn't tons of choice here.

BUG=chromium:941638
TEST=With device tree + config the GPU works
TEST=git diff cros/chromeos-4.4 drivers/*/*/midgard/*/rk matches desc.

Change-Id: Ife3bb7175b78158e184629753acba2b52945d0db
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/1539808
Reviewed-by: Dominik Behr <dbehr@chromium.org>
Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
diff --git a/drivers/gpu/arm/midgard/platform/rk/Kbuild b/drivers/gpu/arm/midgard/platform/rk/Kbuild
new file mode 100644
index 0000000..661c155
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/rk/Kbuild
@@ -0,0 +1,16 @@
+#
+# (C) COPYRIGHT 2012-2016 ARM Limited. All rights reserved.
+#
+# This program is free software and is provided to you under the terms of the
+# GNU General Public License version 2 as published by the Free Software
+# Foundation, and any use by you of this program is subject to the terms
+# of such GNU licence.
+#
+
+ccflags-y += -I$(srctree)/drivers/staging/android
+
+ifeq ($(CONFIG_MALI_MIDGARD),y)
+	obj-y += platform/rk/mali_kbase_config_rk.o
+else ifeq ($(CONFIG_MALI_MIDGARD),m)
+	SRC += platform/rk/mali_kbase_config_rk.c
+endif
diff --git a/drivers/gpu/arm/midgard/platform/rk/mali_kbase_config_platform.h b/drivers/gpu/arm/midgard/platform/rk/mali_kbase_config_platform.h
new file mode 100644
index 0000000..551b598
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/rk/mali_kbase_config_platform.h
@@ -0,0 +1,93 @@
+/*
+ * (C) COPYRIGHT 2014-2016 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ */
+
+/**
+ * @file mali_kbase_config_platform.h
+ * That define the RK platform config.
+ */
+
+/**
+ * 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 (500000)
+
+/**
+ * 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 (500000)
+
+/**
+ * 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)
+extern struct kbase_pm_callback_conf 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_platform_funcs_conf platform_funcs;
+
+/**
+ * Secure mode switch
+ *
+ * Attached value: pointer to @ref kbase_secure_ops
+ */
+#define SECURE_CALLBACKS (NULL)
+
+/*
+ * Both rk3288 and rk3399 get really unhappy unless you do this.
+ * See https://crrev.com/c/1325749 for some context.
+ */
+#define PLATFORM_POWER_DOWN_ONLY (1)
+
diff --git a/drivers/gpu/arm/midgard/platform/rk/mali_kbase_config_rk.c b/drivers/gpu/arm/midgard/platform/rk/mali_kbase_config_rk.c
new file mode 100644
index 0000000..be78a02
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/rk/mali_kbase_config_rk.c
@@ -0,0 +1,276 @@
+/*
+ * (C) COPYRIGHT 2016 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ */
+
+#include <mali_kbase.h>
+#include <mali_kbase_defs.h>
+#include <mali_kbase_config.h>
+
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+
+#include "mali_kbase_rk.h"
+
+static int kbase_pm_notifier(struct notifier_block *nb, unsigned long action,
+		void *data)
+{
+	struct rk_context *platform = container_of(nb, struct rk_context, pm_nb);
+	struct device *dev = platform->kbdev->dev;
+
+	switch (action) {
+	case PM_SUSPEND_PREPARE:
+		return pm_runtime_get_sync(dev);
+	case PM_POST_SUSPEND:
+		return pm_runtime_put(dev);
+	}
+
+	return 0;
+}
+
+static int kbase_platform_rk_init(struct kbase_device *kbdev)
+{
+	struct rk_context *platform;
+
+	platform = kzalloc(sizeof(*platform), GFP_KERNEL);
+	if (!platform)
+		return -ENOMEM;
+
+	platform->is_powered = false;
+
+	kbdev->platform_context = platform;
+	platform->kbdev = kbdev;
+
+	return 0;
+}
+
+static void kbase_platform_rk_term(struct kbase_device *kbdev)
+{
+	struct rk_context *platform = kbdev->platform_context;
+
+	kfree(platform);
+}
+
+struct kbase_platform_funcs_conf platform_funcs = {
+	.platform_init_func = &kbase_platform_rk_init,
+	.platform_term_func = &kbase_platform_rk_term,
+};
+
+/* TODO */
+static int rk_pm_callback_runtime_on(struct kbase_device *kbdev)
+{
+	return 0;
+}
+
+static void rk_pm_callback_runtime_off(struct kbase_device *kbdev)
+{
+}
+
+static int rk_pm_enable_regulator(struct kbase_device *kbdev)
+{
+	int error;
+	int i;
+
+	dev_dbg(kbdev->dev, "Enabling regulator.");
+
+	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;
+		}
+	}
+
+	return 0;
+}
+
+static void rk_pm_disable_regulator(struct kbase_device *kbdev)
+{
+	int error;
+	int i;
+
+	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 rk_pm_enable_clk(struct kbase_device *kbdev)
+{
+	int ret = 0;
+
+	if (!(kbdev->clock)) {
+		dev_dbg(kbdev->dev, "Continuing without Mali clock control\n");
+		/* Allow probe to continue without clock. */
+	} else {
+		ret = clk_prepare_enable(kbdev->clock);
+		if (ret)
+			dev_err(kbdev->dev, "failed to enable clk: %d\n", ret);
+	}
+
+	return ret;
+}
+
+static void rk_pm_disable_clk(struct kbase_device *kbdev)
+{
+	if (!(kbdev->clock))
+		dev_dbg(kbdev->dev, "Continuing without Mali clock control\n");
+		/* Allow probe to continue without clock. */
+	else
+		clk_disable_unprepare(kbdev->clock);
+}
+
+static int rk_pm_callback_power_on(struct kbase_device *kbdev)
+{
+	int ret = 1; /* Assume GPU has been powered off */
+	int err = 0;
+	struct rk_context *platform;
+
+	platform = kbdev->platform_context;
+	if (platform->is_powered) {
+		dev_dbg(kbdev->dev, "mali_device is already powered\n");
+		return 0;
+	}
+
+	dev_dbg(kbdev->dev, "powering on.");
+
+	/* we must enable vdd_gpu before pd_gpu_in_chip. */
+	err = rk_pm_enable_regulator(kbdev);
+	if (err) {
+		dev_err(kbdev->dev, "fail to enable regulator, err=%d\n", err);
+		return err;
+	}
+
+	err = pm_runtime_get_sync(kbdev->dev);
+	if (err < 0 && err != -EACCES) {
+		dev_err(kbdev->dev,
+			"failed to runtime resume device: %d\n", err);
+		pm_runtime_put_sync(kbdev->dev);
+		rk_pm_disable_regulator(kbdev);
+		return err;
+	} else if (err == 1) {
+		/*
+		 * Let core know that the chip has not been
+		 * powered off, so we can save on re-initialization.
+		 */
+		ret = 0;
+	}
+
+	err = rk_pm_enable_clk(kbdev); /* clk is not relative to pd */
+	if (err) {
+		dev_err(kbdev->dev, "failed to enable clk: %d\n", err);
+		pm_runtime_put_sync(kbdev->dev);
+		rk_pm_disable_clk(kbdev);
+		return err;
+	}
+
+	platform->is_powered = true;
+
+	return ret;
+}
+
+static void rk_pm_callback_power_off(struct kbase_device *kbdev)
+{
+	struct rk_context *platform = kbdev->platform_context;
+
+	if (!platform->is_powered) {
+		dev_dbg(kbdev->dev, "mali_dev is already powered off\n");
+		return;
+	}
+
+	dev_dbg(kbdev->dev, "powering off\n");
+
+	platform->is_powered = false;
+
+	rk_pm_disable_clk(kbdev);
+
+	pm_runtime_mark_last_busy(kbdev->dev);
+	pm_runtime_put_autosuspend(kbdev->dev);
+
+	rk_pm_disable_regulator(kbdev);
+}
+
+int rk_kbase_device_runtime_init(struct kbase_device *kbdev)
+{
+	struct rk_context *platform = kbdev->platform_context;
+	int err;
+
+	pm_runtime_set_autosuspend_delay(kbdev->dev, 200);
+	pm_runtime_use_autosuspend(kbdev->dev);
+
+	platform->pm_nb.notifier_call = kbase_pm_notifier;
+	platform->pm_nb.priority = 0;
+	err = register_pm_notifier(&platform->pm_nb);
+	if (err) {
+		dev_err(kbdev->dev, "Couldn't register pm notifier\n");
+		return -ENODEV;
+	}
+
+	/* no need to call pm_runtime_set_active here. */
+	pm_runtime_enable(kbdev->dev);
+
+	return 0;
+}
+
+void rk_kbase_device_runtime_disable(struct kbase_device *kbdev)
+{
+	struct rk_context *platform = kbdev->platform_context;
+
+	pm_runtime_disable(kbdev->dev);
+	unregister_pm_notifier(&platform->pm_nb);
+}
+
+static void rk_pm_suspend_callback(struct kbase_device *kbdev)
+{
+	/*
+	 * Depending on power policy, the GPU might not be powered off at this
+	 * point. We have to call rk_pm_callback_power_off() here to make
+	 * sure it is before turning off the regulator. The function can be
+	 * called safely even if the GPU is already powered off.
+	 */
+	rk_pm_callback_power_off(kbdev);
+	rk_pm_disable_regulator(kbdev);
+}
+
+static void rk_pm_resume_callback(struct kbase_device *kbdev)
+{
+	rk_pm_enable_regulator(kbdev);
+	/*
+	 * Core will call rk_pm_enable_regulator() itself before attempting
+	 * to access the GPU, so no need to do it here.
+	 */
+}
+
+struct kbase_pm_callback_conf pm_callbacks = {
+	.power_on_callback = rk_pm_callback_power_on,
+	.power_off_callback = rk_pm_callback_power_off,
+	.power_suspend_callback = rk_pm_suspend_callback,
+	.power_resume_callback = rk_pm_resume_callback,
+#ifdef CONFIG_PM
+	.power_runtime_init_callback = rk_kbase_device_runtime_init,
+	.power_runtime_term_callback = rk_kbase_device_runtime_disable,
+	.power_runtime_on_callback = rk_pm_callback_runtime_on,
+	.power_runtime_off_callback = rk_pm_callback_runtime_off,
+#else				/* CONFIG_PM */
+	.power_runtime_init_callback = NULL,
+	.power_runtime_term_callback = NULL,
+	.power_runtime_on_callback = NULL,
+	.power_runtime_off_callback = NULL,
+#endif				/* CONFIG_PM */
+};
+
+int kbase_platform_early_init(void)
+{
+	/* Nothing needed at this stage */
+	return 0;
+}
diff --git a/drivers/gpu/arm/midgard/platform/rk/mali_kbase_rk.h b/drivers/gpu/arm/midgard/platform/rk/mali_kbase_rk.h
new file mode 100644
index 0000000..1e59118
--- /dev/null
+++ b/drivers/gpu/arm/midgard/platform/rk/mali_kbase_rk.h
@@ -0,0 +1,37 @@
+/* drivers/gpu/t6xx/kbase/src/platform/rk/mali_kbase_platform.h
+ * Rockchip SoC Mali-Midgard platform-dependent codes
+ *
+ * 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.
+ */
+
+/**
+ * @file mali_kbase_rk.h
+ *
+ * defines work_context type of platform_dependent_part.
+ */
+
+#ifndef _MALI_KBASE_RK_H_
+#define _MALI_KBASE_RK_H_
+
+#include <linux/notifier.h>
+
+#include <mali_kbase.h>
+
+/*---------------------------------------------------------------------------*/
+
+/**
+ * struct rk_context - work_context of platform_dependent_part_of_rk.
+ * @is_powered: record the status
+ *      of common_parts calling 'power_on_callback' and 'power_off_callback'.
+ */
+struct rk_context {
+	struct kbase_device *kbdev;
+	/* Notifier block to runtime resume the kbase_device on suspend. */
+	struct notifier_block pm_nb;
+	bool is_powered;
+};
+
+#endif				/* _MALI_KBASE_RK_H_ */
+