aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/samsung/mali/platform/orion-m400/mali_platform_dvfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/samsung/mali/platform/orion-m400/mali_platform_dvfs.c')
-rw-r--r--drivers/media/video/samsung/mali/platform/orion-m400/mali_platform_dvfs.c448
1 files changed, 448 insertions, 0 deletions
diff --git a/drivers/media/video/samsung/mali/platform/orion-m400/mali_platform_dvfs.c b/drivers/media/video/samsung/mali/platform/orion-m400/mali_platform_dvfs.c
new file mode 100644
index 0000000..4efa759
--- /dev/null
+++ b/drivers/media/video/samsung/mali/platform/orion-m400/mali_platform_dvfs.c
@@ -0,0 +1,448 @@
+/* * Copyright (C) 2010-2012 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.
+ *
+ * A copy of the licence is included with the program, and can also be obtained from Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * @file mali_platform_dvfs.c
+ * Platform specific Mali driver dvfs functions
+ */
+
+#include "mali_kernel_common.h"
+#include "mali_osk.h"
+#include "mali_platform.h"
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+
+#include <asm/io.h>
+
+#ifdef CONFIG_CPU_FREQ
+#include <mach/asv.h>
+#include <mach/regs-pmu.h>
+#define EXYNOS4_ASV_ENABLED
+#endif
+
+#include "mali_device_pause_resume.h"
+#include <linux/workqueue.h>
+
+#define MALI_DVFS_WATING 10 // msec
+
+static int bMaliDvfsRun=0;
+
+#if MALI_GPU_BOTTOM_LOCK
+static _mali_osk_atomic_t bottomlock_status;
+#endif
+
+typedef struct mali_dvfs_tableTag{
+ unsigned int clock;
+ unsigned int freq;
+ unsigned int vol;
+}mali_dvfs_table;
+
+typedef struct mali_dvfs_statusTag{
+ unsigned int currentStep;
+ mali_dvfs_table * pCurrentDvfs;
+
+}mali_dvfs_currentstatus;
+
+typedef struct mali_dvfs_thresholdTag{
+ unsigned int downthreshold;
+ unsigned int upthreshold;
+}mali_dvfs_threshold_table;
+
+typedef struct mali_dvfs_staycount{
+ unsigned int staycount;
+}mali_dvfs_staycount_table;
+
+mali_dvfs_staycount_table mali_dvfs_staycount[MALI_DVFS_STEPS]={
+ /*step 0*/{1},
+ /*step 1*/{1},
+ /*step 2*/{1} };
+
+/*dvfs threshold*/
+mali_dvfs_threshold_table mali_dvfs_threshold[MALI_DVFS_STEPS]={
+ /*step 0*/{((int)((255*0)/100)) ,((int)((255*85)/100))},
+ /*step 1*/{((int)((255*75)/100)) ,((int)((255*85)/100))},
+ /*step 2*/{((int)((255*75)/100)) ,((int)((255*100)/100))} };
+
+/*dvfs status*/
+mali_dvfs_currentstatus maliDvfsStatus;
+int mali_dvfs_control=0;
+
+/*dvfs table*/
+mali_dvfs_table mali_dvfs[MALI_DVFS_STEPS]={
+#ifdef CONFIG_EXYNOS4210_1400MHZ_SUPPORT
+ /*step 0*/{134 ,1000000 , 950000},
+#else
+ /*step 0*/{100 ,1000000 , 950000},
+#endif
+ /*step 1*/{160 ,1000000 , 950000},
+ /*step 2*/{267 ,1000000 ,1000000} };
+
+#ifdef EXYNOS4_ASV_ENABLED
+
+#define ASV_8_LEVEL 8
+#define ASV_5_LEVEL 5
+
+static unsigned int asv_3d_volt_5_table[ASV_5_LEVEL][MALI_DVFS_STEPS] = {
+ /* L3 (100/134MHz) L2(160MHz), L1(267MHz) */
+ {1000000, 1000000, 1100000}, /* S */
+ {1000000, 1000000, 1100000}, /* A */
+ { 950000, 950000, 1000000}, /* B */
+ { 950000, 950000, 1000000}, /* C */
+ { 950000, 950000, 950000}, /* D */
+};
+
+static unsigned int asv_3d_volt_8_table[ASV_8_LEVEL][MALI_DVFS_STEPS] = {
+ /* L3 (100/134MHz) L2(160MHz), L1(267MHz) */
+ {1000000, 1000000, 1100000}, /* SS */
+ {1000000, 1000000, 1100000}, /* A1 */
+ {1000000, 1000000, 1100000}, /* A2 */
+ { 950000, 950000, 1000000}, /* B1 */
+ { 950000, 950000, 1000000}, /* B2 */
+ { 950000, 950000, 1000000}, /* C1 */
+ { 950000, 950000, 1000000}, /* C2 */
+ { 950000, 950000, 950000}, /* D1 */
+};
+#endif
+
+static u32 mali_dvfs_utilization = 255;
+
+static void mali_dvfs_work_handler(struct work_struct *w);
+
+static struct workqueue_struct *mali_dvfs_wq = 0;
+extern mali_io_address clk_register_map;
+
+#if MALI_GPU_BOTTOM_LOCK
+extern _mali_osk_lock_t *mali_dvfs_lock;
+#endif
+
+static DECLARE_WORK(mali_dvfs_work, mali_dvfs_work_handler);
+
+static unsigned int get_mali_dvfs_status(void)
+{
+ return maliDvfsStatus.currentStep;
+}
+
+#if MALI_GPU_BOTTOM_LOCK
+#if MALI_PMM_RUNTIME_JOB_CONTROL_ON
+int get_mali_dvfs_control_status(void)
+{
+ return mali_dvfs_control;
+}
+
+mali_bool set_mali_dvfs_current_step(unsigned int step)
+{
+ _mali_osk_lock_wait(mali_dvfs_lock, _MALI_OSK_LOCKMODE_RW);
+ maliDvfsStatus.currentStep = step;
+ _mali_osk_lock_signal(mali_dvfs_lock, _MALI_OSK_LOCKMODE_RW);
+ return MALI_TRUE;
+}
+#endif
+#endif
+
+static mali_bool set_mali_dvfs_status(u32 step,mali_bool boostup)
+{
+ u32 validatedStep=step;
+
+#ifdef CONFIG_REGULATOR
+ if (mali_regulator_get_usecount()==0) {
+ MALI_DEBUG_PRINT(1, ("regulator use_count is 0 \n"));
+ return MALI_FALSE;
+ }
+#endif
+
+ if (boostup) {
+#ifdef CONFIG_REGULATOR
+ /*change the voltage*/
+ mali_regulator_set_voltage(mali_dvfs[step].vol, mali_dvfs[step].vol);
+#endif
+ /*change the clock*/
+ mali_clk_set_rate(mali_dvfs[step].clock, mali_dvfs[step].freq);
+ } else {
+ /*change the clock*/
+ mali_clk_set_rate(mali_dvfs[step].clock, mali_dvfs[step].freq);
+#ifdef CONFIG_REGULATOR
+ /*change the voltage*/
+ mali_regulator_set_voltage(mali_dvfs[step].vol, mali_dvfs[step].vol);
+#endif
+ }
+
+ maliDvfsStatus.currentStep = validatedStep;
+ /*for future use*/
+ maliDvfsStatus.pCurrentDvfs = &mali_dvfs[validatedStep];
+
+ return MALI_TRUE;
+}
+
+static void mali_platform_wating(u32 msec)
+{
+ /*sample wating
+ change this in the future with proper check routine.
+ */
+ unsigned int read_val;
+ while(1) {
+ read_val = _mali_osk_mem_ioread32(clk_register_map, 0x00);
+ if ((read_val & 0x8000)==0x0000) break;
+
+ _mali_osk_time_ubusydelay(100); // 1000 -> 100 : 20101218
+ }
+ /* _mali_osk_time_ubusydelay(msec*1000);*/
+}
+
+static mali_bool change_mali_dvfs_status(u32 step, mali_bool boostup )
+{
+
+ MALI_DEBUG_PRINT(1, ("> change_mali_dvfs_status: %d, %d \n",step, boostup));
+
+ if (!set_mali_dvfs_status(step, boostup)) {
+ MALI_DEBUG_PRINT(1, ("error on set_mali_dvfs_status: %d, %d \n",step, boostup));
+ return MALI_FALSE;
+ }
+
+ /*wait until clock and voltage is stablized*/
+ mali_platform_wating(MALI_DVFS_WATING); /*msec*/
+
+ return MALI_TRUE;
+}
+
+static unsigned int decideNextStatus(unsigned int utilization)
+{
+ unsigned int level=0; // 0:stay, 1:up
+
+ if (!mali_dvfs_control) {
+#if MALI_GPU_BOTTOM_LOCK
+ if (_mali_osk_atomic_read(&bottomlock_status) > 0)
+ level = 1; /* or bigger */
+ else
+#endif
+ switch(maliDvfsStatus.currentStep)
+ {
+ case 0:
+ if( utilization > mali_dvfs_threshold[maliDvfsStatus.currentStep].upthreshold)
+ level=1;
+ else
+ level = maliDvfsStatus.currentStep;
+ break;
+ case 1:
+ if( utilization > mali_dvfs_threshold[maliDvfsStatus.currentStep].upthreshold)
+ level=2;
+ else if( utilization <
+ (mali_dvfs_threshold[maliDvfsStatus.currentStep].downthreshold*mali_dvfs[maliDvfsStatus.currentStep-1].clock)/
+ mali_dvfs[maliDvfsStatus.currentStep].clock)
+ level=0;
+ else
+ level = maliDvfsStatus.currentStep;
+ break;
+ case 2:
+ if( utilization <
+ (mali_dvfs_threshold[maliDvfsStatus.currentStep].downthreshold*mali_dvfs[maliDvfsStatus.currentStep-1].clock)/
+ mali_dvfs[maliDvfsStatus.currentStep].clock)
+ level=1;
+ else
+ level = maliDvfsStatus.currentStep;
+ break;
+ }
+ }
+ else
+ {
+ if((mali_dvfs_control == 1)||(( mali_dvfs_control > 3) && (mali_dvfs_control < mali_dvfs[0].clock+1)))
+ {
+ level=0;
+ }
+ else if((mali_dvfs_control == 2)||(( mali_dvfs_control > mali_dvfs[0].clock) && (mali_dvfs_control < mali_dvfs[1].clock+1)))
+ {
+ level=1;
+ }
+ else
+ {
+ level=2;
+ }
+ }
+
+ return level;
+}
+
+#ifdef EXYNOS4_ASV_ENABLED
+static mali_bool mali_dvfs_table_update(void)
+{
+ unsigned int exynos_result_of_asv_group;
+ unsigned int target_asv;
+ unsigned int i;
+ exynos_result_of_asv_group = exynos_result_of_asv & 0xf;
+ target_asv = exynos_result_of_asv >> 28;
+ MALI_PRINT(("exynos_result_of_asv_group = 0x%x, target_asv = 0x%x\n", exynos_result_of_asv_group, target_asv));
+
+ if (target_asv == 0x8) { //SUPPORT_1400MHZ
+ for (i = 0; i < MALI_DVFS_STEPS; i++) {
+ mali_dvfs[i].vol = asv_3d_volt_5_table[exynos_result_of_asv_group][i];
+ MALI_PRINT(("mali_dvfs[%d].vol = %d\n", i, mali_dvfs[i].vol));
+ }
+ } else if (target_asv == 0x4){ //SUPPORT_1200MHZ
+ for (i = 0; i < MALI_DVFS_STEPS; i++) {
+ mali_dvfs[i].vol = asv_3d_volt_8_table[exynos_result_of_asv_group][i];
+ MALI_PRINT(("mali_dvfs[%d].vol = %d\n", i, mali_dvfs[i].vol));
+ }
+ }
+
+ return MALI_TRUE;
+
+}
+#endif
+
+static mali_bool mali_dvfs_status(u32 utilization)
+{
+ unsigned int nextStatus = 0;
+ unsigned int curStatus = 0;
+ mali_bool boostup = MALI_FALSE;
+#ifdef EXYNOS4_ASV_ENABLED
+ static mali_bool asv_applied = MALI_FALSE;
+#endif
+ static int stay_count = 0; // to prevent frequent switch
+
+ MALI_DEBUG_PRINT(1, ("> mali_dvfs_status: %d \n",utilization));
+#ifdef EXYNOS4_ASV_ENABLED
+ if (asv_applied == MALI_FALSE) {
+ mali_dvfs_table_update();
+ change_mali_dvfs_status(0,0);
+ asv_applied = MALI_TRUE;
+
+ return MALI_TRUE;
+ }
+#endif
+
+ /*decide next step*/
+ curStatus = get_mali_dvfs_status();
+ nextStatus = decideNextStatus(utilization);
+
+ MALI_DEBUG_PRINT(1, ("= curStatus %d, nextStatus %d, maliDvfsStatus.currentStep %d \n", curStatus, nextStatus, maliDvfsStatus.currentStep));
+
+ /*if next status is same with current status, don't change anything*/
+ if ((curStatus!=nextStatus && stay_count==0)) {
+ /*check if boost up or not*/
+ if (nextStatus > maliDvfsStatus.currentStep)
+ boostup = 1;
+
+ /*change mali dvfs status*/
+ if (!change_mali_dvfs_status(nextStatus,boostup)) {
+ MALI_DEBUG_PRINT(1, ("error on change_mali_dvfs_status \n"));
+ return MALI_FALSE;
+ }
+ stay_count = mali_dvfs_staycount[maliDvfsStatus.currentStep].staycount;
+ } else {
+ if (stay_count>0)
+ stay_count--;
+ }
+
+ return MALI_TRUE;
+}
+
+
+
+int mali_dvfs_is_running(void)
+{
+ return bMaliDvfsRun;
+}
+
+
+
+void mali_dvfs_late_resume(void)
+{
+ // set the init clock as low when resume
+ set_mali_dvfs_status(0,0);
+}
+
+
+static void mali_dvfs_work_handler(struct work_struct *w)
+{
+ bMaliDvfsRun=1;
+
+ MALI_DEBUG_PRINT(3, ("=== mali_dvfs_work_handler\n"));
+
+ if (!mali_dvfs_status(mali_dvfs_utilization))
+ MALI_DEBUG_PRINT(1,( "error on mali dvfs status in mali_dvfs_work_handler"));
+
+ bMaliDvfsRun=0;
+}
+
+
+mali_bool init_mali_dvfs_status(int step)
+{
+ /*default status
+ add here with the right function to get initilization value.
+ */
+ if (!mali_dvfs_wq)
+ mali_dvfs_wq = create_singlethread_workqueue("mali_dvfs");
+
+#if MALI_GPU_BOTTOM_LOCK
+ _mali_osk_atomic_init(&bottomlock_status, 0);
+#endif
+
+ /*add a error handling here*/
+ maliDvfsStatus.currentStep = step;
+
+ return MALI_TRUE;
+}
+
+void deinit_mali_dvfs_status(void)
+{
+#if MALI_GPU_BOTTOM_LOCK
+ _mali_osk_atomic_term(&bottomlock_status);
+#endif
+
+ if (mali_dvfs_wq)
+ destroy_workqueue(mali_dvfs_wq);
+ mali_dvfs_wq = NULL;
+}
+
+mali_bool mali_dvfs_handler(u32 utilization)
+{
+ mali_dvfs_utilization = utilization;
+ queue_work_on(0, mali_dvfs_wq,&mali_dvfs_work);
+
+ /*add error handle here*/
+ return MALI_TRUE;
+}
+
+void mali_default_step_set(int step, mali_bool boostup)
+{
+ mali_clk_set_rate(mali_dvfs[step].clock, mali_dvfs[step].freq);
+
+ if (maliDvfsStatus.currentStep == 1)
+ set_mali_dvfs_status(step, boostup);
+}
+
+#if MALI_GPU_BOTTOM_LOCK
+int mali_dvfs_bottom_lock_push(void)
+{
+ int prev_status = _mali_osk_atomic_read(&bottomlock_status);
+
+ if (prev_status < 0) {
+ MALI_PRINT(("gpu bottom lock status is not valid for push"));
+ return -1;
+ }
+
+ if (prev_status == 0) {
+ mali_regulator_set_voltage(mali_dvfs[1].vol, mali_dvfs[1].vol);
+ mali_clk_set_rate(mali_dvfs[1].clock, mali_dvfs[1].freq);
+ set_mali_dvfs_current_step(1);
+ }
+
+ return _mali_osk_atomic_inc_return(&bottomlock_status);
+}
+
+int mali_dvfs_bottom_lock_pop(void)
+{
+ if (_mali_osk_atomic_read(&bottomlock_status) <= 0) {
+ MALI_PRINT(("gpu bottom lock status is not valid for pop"));
+ return -1;
+ }
+
+ return _mali_osk_atomic_dec_return(&bottomlock_status);
+}
+#endif