diff --git a/drivers/mxc/gpu-viv/hal/os/linux/kernel/platform/freescale/gc_hal_kernel_platform_imx.c b/drivers/mxc/gpu-viv/hal/os/linux/kernel/platform/freescale/gc_hal_kernel_platform_imx.c index e55b97b304dbed4d83bfa4440b620e7a24d1dcff..96de21d41b14a15e1317dad6435e59524c234720 100644 --- a/drivers/mxc/gpu-viv/hal/os/linux/kernel/platform/freescale/gc_hal_kernel_platform_imx.c +++ b/drivers/mxc/gpu-viv/hal/os/linux/kernel/platform/freescale/gc_hal_kernel_platform_imx.c @@ -59,6 +59,9 @@ #include "gc_hal_driver.h" #include <linux/slab.h> #include <linux/pm_qos.h> +#include <linux/module.h> +#include <linux/thermal.h> +#include <linux/err.h> #if defined(CONFIG_PM_OPP) #include <linux/pm_opp.h> @@ -135,6 +138,22 @@ module_param(initgpu3DMinClock, int, 0644); struct platform_device *pdevice; +#if gcdENABLE_FSCALE_VAL_ADJUST && \ + (defined(CONFIG_DEVICE_THERMAL) || defined(CONFIG_DEVICE_THERMAL_MODULE)) +#if defined(CONFIG_ANDROID) +int gcdENABLE_GPU_THERMAL = 0; +struct devfreq_cooling_device { + int id; + struct thermal_cooling_device *cool_dev; + unsigned int devfreq_state; + unsigned int max_state; +}; + +static DEFINE_IDR(devfreq_idr); +static DEFINE_MUTEX(devfreq_cooling_lock); +# endif +#endif + #ifdef CONFIG_GPU_LOW_MEMORY_KILLER # include <linux/kernel.h> # include <linux/mm.h> @@ -283,11 +302,11 @@ _ShrinkMemory( #endif #if gcdENABLE_FSCALE_VAL_ADJUST && (defined(CONFIG_DEVICE_THERMAL) || defined(CONFIG_DEVICE_THERMAL_MODULE)) -static int thermal_hot_pm_notify(struct notifier_block *nb, unsigned long event, - void *dummy) +static int devfreq_cooling_handle_event_change(unsigned long event) { - static gctUINT orgFscale, minFscale, maxFscale; - static gctBOOL bAlreadyTooHot = gcvFALSE; + static gctUINT orgFscale; + static unsigned long prev_event = 0xffffffff; + gctUINT curFscale, minFscale, maxFscale; gckHARDWARE hardware; gckGALDEVICE galDevice; gctUINT FscaleVal = orgFscale; @@ -312,14 +331,35 @@ static int thermal_hot_pm_notify(struct notifier_block *nb, unsigned long event, return NOTIFY_OK; } - if (event && !bAlreadyTooHot) { - gckHARDWARE_GetFscaleValue(hardware,&orgFscale,&minFscale, &maxFscale); - FscaleVal = minFscale; - bAlreadyTooHot = gcvTRUE; - printk("System is too hot. GPU3D will work at %d/64 clock.\n", minFscale); - } else if (!event && bAlreadyTooHot) { - printk("Hot alarm is canceled. GPU3D clock will return to %d/64\n", orgFscale); - bAlreadyTooHot = gcvFALSE; + gckHARDWARE_GetFscaleValue(hardware, &curFscale, &minFscale, &maxFscale); + if (prev_event == 0xffffffff) /* get initial value of Fscale */ + orgFscale = curFscale; + else if (prev_event == event) + return NOTIFY_OK; + + prev_event = event; + + switch (event) { + case 0: + FscaleVal = orgFscale; + printk("Hot alarm is canceled. GPU3D clock will return to %d/64\n", orgFscale); + break; + case 1: +#if defined(CONFIG_ANDROID) + if (of_find_compatible_node(NULL, NULL, "fsl,imx8mq-gpu")) { + FscaleVal = maxFscale >> 1; /* switch to 1/2 of max frequency */ + printk("System is a little hot. GPU3D clock will work at %d/64\n", maxFscale >> 1); + break; + } +#endif + case 2: + FscaleVal = minFscale; + printk("System is too hot. GPU3D will work at %d/64 clock.\n", minFscale); + break; + default: + FscaleVal = orgFscale; + printk("System don't support such event: %ld.\n", event); + break; } while (galDevice->kernels[core] && core <= gcvCORE_3D_MAX) @@ -330,11 +370,125 @@ static int thermal_hot_pm_notify(struct notifier_block *nb, unsigned long event, return NOTIFY_OK; } +static int thermal_hot_pm_notify(struct notifier_block *nb, unsigned long event, void *dummy) { + int ret = devfreq_cooling_handle_event_change(event); + return ret; +} + static struct notifier_block thermal_hot_pm_notifier = { .notifier_call = thermal_hot_pm_notify, }; +#if defined(CONFIG_ANDROID) +static int devfreq_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + // Only when GPU is ready, will start to change GPU freq. + if (gcdENABLE_GPU_THERMAL == 1) { + struct devfreq_cooling_device *devfreq_device = cdev->devdata; + int ret; + ret = devfreq_cooling_handle_event_change(state); + if (ret) + return -EINVAL; + devfreq_device->devfreq_state = state; + } + return 0; +} + +static int devfreq_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct devfreq_cooling_device *devfreq_device = cdev->devdata; + *state = devfreq_device->max_state; + + return 0; +} + +static int devfreq_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct devfreq_cooling_device *devfreq_device = cdev->devdata; + *state = devfreq_device->devfreq_state; + + return 0; +} + +static struct thermal_cooling_device_ops const devfreq_cooling_ops = { + .get_max_state = devfreq_get_max_state, + .get_cur_state = devfreq_get_cur_state, + .set_cur_state = devfreq_set_cur_state, +}; + +static int get_idr(struct idr *idr, int *id) +{ + int ret; + + mutex_lock(&devfreq_cooling_lock); + ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL); + mutex_unlock(&devfreq_cooling_lock); + if (unlikely(ret < 0)) + return ret; + *id = ret; + + return 0; +} + +static void release_idr(struct idr *idr, int id) +{ + mutex_lock(&devfreq_cooling_lock); + idr_remove(idr, id); + mutex_unlock(&devfreq_cooling_lock); +} + +struct thermal_cooling_device *device_gpu_cooling_register(struct device_node *np, + unsigned long states) +{ + struct thermal_cooling_device *cool_dev; + struct devfreq_cooling_device *devfreq_dev = NULL; + char dev_name[THERMAL_NAME_LENGTH]; + int ret = 0; + + devfreq_dev = kzalloc(sizeof(struct devfreq_cooling_device), + GFP_KERNEL); + if (!devfreq_dev) + return ERR_PTR(-ENOMEM); + + ret = get_idr(&devfreq_idr, &devfreq_dev->id); + if (ret) { + kfree(devfreq_dev); + return ERR_PTR(-EINVAL); + } + + snprintf(dev_name, sizeof(dev_name), "thermal-gpufreq-%d", + devfreq_dev->id); + + devfreq_dev->max_state = states; + cool_dev = thermal_of_cooling_device_register(np, dev_name, devfreq_dev, + &devfreq_cooling_ops); + if (!cool_dev) { + release_idr(&devfreq_idr, devfreq_dev->id); + kfree(devfreq_dev); + return ERR_PTR(-EINVAL); + } + devfreq_dev->cool_dev = cool_dev; + devfreq_dev->devfreq_state = 0; + + return cool_dev; +} +EXPORT_SYMBOL_GPL(device_gpu_cooling_register); + +void device_gpu_cooling_unregister(struct thermal_cooling_device *cdev) +{ + struct devfreq_cooling_device *devfreq_dev = cdev->devdata; + + thermal_cooling_device_unregister(devfreq_dev->cool_dev); + release_idr(&devfreq_idr, devfreq_dev->id); + kfree(devfreq_dev); +} +EXPORT_SYMBOL_GPL(device_gpu_cooling_unregister); +#endif + static ssize_t gpu3DMinClock_show(struct device_driver *dev, char *buf) { gctUINT currentf = 0, minf = 0, maxf = 0; @@ -1396,6 +1550,11 @@ static inline int get_power(struct device *pdev) if (ret) dev_err(pdev, "create gpu3DClockScale attr failed (%d)\n", ret); + +#if defined(CONFIG_ANDROID) + gcdENABLE_GPU_THERMAL = 1; +#endif + #endif #if defined(CONFIG_PM_OPP) @@ -1459,6 +1618,10 @@ static inline void put_power(void) #endif #if gcdENABLE_FSCALE_VAL_ADJUST && (defined(CONFIG_DEVICE_THERMAL) || defined(CONFIG_DEVICE_THERMAL_MODULE)) +#if defined(CONFIG_ANDROID) + gcdENABLE_GPU_THERMAL = 0; +#endif + UNREG_THERMAL_NOTIFIER(&thermal_hot_pm_notifier); driver_remove_file(pdevice->dev.driver, &driver_attr_gpu3DMinClock);