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_ */
+