blob: 332f3247181629e6446c6f12589b0eb015f2ba40 [file] [log] [blame]
/**************************************************************************
* Copyright (c) 2012, Intel Corporation.
* All Rights Reserved.
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Authors:
* Dale B. Stimson <dale.b.stimson@intel.com>
* Jari Luoma-aho <jari.luoma-aho@intel.com>
* Jari Nippula <jari.nippula@intel.com>
* Javier Torres Castillo <javier.torres.castillo@intel.com>
*/
#include <linux/devfreq.h>
#include <ospm/gfx_freq.h>
#include <dfrgx_utilstats.h>
#include <rgxdf.h>
#include "dev_freq_debug.h"
#include "df_rgx_defs.h"
#include "df_rgx_burst.h"
#include "dfrgx_interface.h"
#include "dev_freq_attrib.h"
#define MAX_NUM_SAMPLES 10
/*Profiling Information - */
struct gpu_profiling_record a_profiling_info[NUMBER_OF_LEVELS_MAX_FUSE];
/**
* gpu_profiling_records_init() - Initializes profiling array.
*/
static void gpu_profiling_records_init(void)
{
gpu_profiling_records_restart();
}
/**
* gpu_profiling_records_restart() - Memset to 0, profiling stats array.
*/
void gpu_profiling_records_restart(void)
{
memset(a_profiling_info, 0, sizeof(a_profiling_info));
}
/**
* gpu_profiling_records_update_entry() - Updates the profiling calculations.
* @util_index: Frequency level index.
* @is_current_entry: 1 if We want to update the current freq level,
* 0 if it the previous.
* realized frequency in KHz.
*/
static void gpu_profiling_records_update_entry(int util_index ,
int is_current_entry)
{
long long time_diff_ms;
ktime_t time_now = ktime_get();
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: index %d, current %d\n",
__func__, util_index, is_current_entry);
if (is_current_entry) {
a_profiling_info[util_index].last_timestamp_ns = time_now;
} else {
ktime_t time_diff_ns = ktime_sub(time_now,
a_profiling_info[util_index].last_timestamp_ns);
time_diff_ms = ktime_to_ms(time_diff_ns);
a_profiling_info[util_index].time_ms += time_diff_ms;
}
}
/**
* gpu_profiling_records_show() - Shows profiling stats.
* @buf: Buffer for sysfs entry.
*/
int gpu_profiling_records_show(char *buf)
{
int i;
int ret = 0;
int n_levels = NUMBER_OF_LEVELS;
if(df_rgx_is_max_fuse_set())
n_levels = NUMBER_OF_LEVELS_MAX_FUSE;
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s\n",
__func__);
for (i = 0; i < n_levels; i++) {
ret += sprintf((buf+ret), "Time for %lu KHZ : %llu ms\n",
a_available_state_freq[i].freq,
a_profiling_info[i].time_ms);
}
return ret;
}
/**
* set_desired_frequency_khz() - Set gpu frequency.
* @bfdata: Pointer to private data structure
* @freq_khz: Desired frequency in KHz (not MHz).
* Returns: <0 if error, 0 if success, but no frequency update, or
* realized frequency in KHz.
*/
static long set_desired_frequency_khz(struct busfreq_data *bfdata,
unsigned long freq_khz)
{
int sts;
struct devfreq *df;
unsigned long freq_req;
unsigned long freq_limited;
unsigned long freq_mhz;
unsigned long prev_freq = 0;
unsigned int freq_mhz_quantized;
u32 freq_code;
int prev_util_record_index = -1;
int gfx_pcs_result = 0;
sts = 0;
/* Warning - this function may be called from devfreq_add_device,
* but if it is, bfdata->devfreq will not yet be set.
*/
df = bfdata->devfreq;
if (!df) {
/*
* Initial call, so set initial frequency.
* Limits from min_freq
* and max_freq would not have been applied by caller.
*/
freq_req = DF_RGX_INITIAL_FREQ_KHZ;
} else if ((freq_khz == 0) && bfdata->bf_prev_freq_rlzd)
freq_req = bfdata->bf_prev_freq_rlzd;
else
freq_req = freq_khz;
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: entry, caller requesting %luKHz\n",
__func__, freq_khz);
if (freq_req < DF_RGX_FREQ_KHZ_MIN)
freq_limited = DF_RGX_FREQ_KHZ_MIN;
else if (freq_req > DF_RGX_FREQ_KHZ_MAX)
freq_limited = DF_RGX_FREQ_KHZ_MAX;
else
freq_limited = freq_req;
freq_mhz = freq_limited / 1000;
/* follow the right lock order: pvr_power_lock -> bus_freq_data_lock */
gfx_pcs_result = RGXPreClockSpeed();
if (gfx_pcs_result) {
DFRGX_DPF(DFRGX_DEBUG_HIGH,
"%s: Could not perform pre-clock-speed change\n",
__func__);
sts = -EBUSY;
goto out;
}
mutex_lock(&bfdata->lock);
if (bfdata->disabled)
goto lock_out;
freq_code = gpu_freq_mhz_to_code(freq_mhz, &freq_mhz_quantized);
if ((bfdata->bf_freq_mhz_rlzd != freq_mhz_quantized) || bfdata->b_resumed ) {
sts = gpu_freq_set_from_code(freq_code);
if (sts < 0) {
DFRGX_DPF(DFRGX_DEBUG_MED,
"%s: error (%d) from gpu_freq_set_from_code for %dMHz\n",
__func__, sts, freq_mhz_quantized);
goto lock_out;
} else {
bfdata->bf_freq_mhz_rlzd = sts;
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: freq MHz"
"(requested, realized) = %u, %lu\n",
__func__, freq_mhz_quantized,
bfdata->bf_freq_mhz_rlzd);
bfdata->b_resumed = 0;
bfdata->b_need_freq_update = 0;
}
/*
* Inform gfx driver that clock speed changed.
* This operation can't be failed
*/
RGXUpdateClockSpeed((bfdata->bf_freq_mhz_rlzd * 1000000));
if (df) {
prev_freq = bfdata->bf_prev_freq_rlzd;
/*
* Setting df->previous_freq will be redundant
* when called from target dispatch function, but
* not otherwise.
*/
bfdata->bf_prev_freq_rlzd =
bfdata->bf_freq_mhz_rlzd * 1000;
df->previous_freq = bfdata->bf_prev_freq_rlzd;
bfdata->bf_desired_freq =
bfdata->bf_prev_freq_rlzd;
}
}
sts = bfdata->bf_freq_mhz_rlzd * 1000;
/* Update our record accordingly*/
bfdata->g_dfrgx_data.gpu_utilization_record_index =
df_rgx_get_util_record_index_by_freq(sts);
if (bfdata->g_dfrgx_data.g_profiling_enable) {
/* for profiling purposes*/
prev_util_record_index =
df_rgx_get_util_record_index_by_freq(prev_freq);
if ((prev_util_record_index >= 0)
&& (prev_util_record_index < NUMBER_OF_LEVELS_MAX_FUSE))
gpu_profiling_records_update_entry(prev_util_record_index, 0);
if (bfdata->g_dfrgx_data.gpu_utilization_record_index >= 0)
gpu_profiling_records_update_entry(bfdata->g_dfrgx_data.gpu_utilization_record_index, 1);
}
lock_out:
mutex_unlock(&bfdata->lock);
RGXPostClockSpeed();
out:
return sts;
}
/**
* df_rgx_set_freq_khz() - Set gpu frequency, public version of set_desired_frequency_khz .
* @bfdata: Pointer to private data structure
* @freq_khz: Desired frequency in KHz (not MHz).
* Returns: <0 if error, 0 if success, but no frequency update, or
* realized frequency in KHz.
*/
long df_rgx_set_freq_khz(struct busfreq_data *bfdata,
unsigned long freq_khz)
{
struct devfreq *df = bfdata->devfreq;
unsigned long ret = 0;
ret = set_desired_frequency_khz(bfdata, freq_khz);
if (!df)
goto go_ret;
if (!strncmp(df->governor->name, "userspace", DEVFREQ_NAME_LEN)) {
/* update userspace freq*/
struct userspace_gov_data *data = df->data;
data->user_frequency = ret;
}
go_ret:
return ret;
}
/**
* wake_thread() - Wake the work thread.
* @df_rgx_data_s: dfrgx burst handle.
*/
static void dfrgx_add_sample_data(struct df_rgx_data_s *g_dfrgx,
struct gpu_util_stats util_stats_sample)
{
static int num_samples;
static int sum_samples_active;
int ret = 0;
int active_high = util_stats_sample.ui64GpuStatActiveHigh;
/*There might be a case where util_stats_sample.ui64GpuStatCumulative is actually zero
* due to the getutilstats functionality assumes certain conditions in the driver making low
* high and blocked values actually 0 */
if (util_stats_sample.ui64GpuStatCumulative == 0) {
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: Ignoring util stats from gpu!\n",
__func__);
return;
}
/* convert ui64GpuStatActiveHigh time period to a 0.01% precision ratio */
active_high *= 10000;
do_div(active_high, util_stats_sample.ui64GpuStatCumulative);
sum_samples_active += (active_high / 100);
num_samples++;
/* When we collect MAX_NUM_SAMPLES samples we need to decide
* bursting or unbursting
*/
if (num_samples == MAX_NUM_SAMPLES) {
int average_active_util = sum_samples_active / MAX_NUM_SAMPLES;
unsigned int burst = DFRGX_NO_BURST_REQ;
/* Reset */
sum_samples_active = 0;
num_samples = 0;
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: Average Active: %d !\n",
__func__, average_active_util);
burst = df_rgx_request_burst(g_dfrgx, average_active_util);
if (burst == DFRGX_NO_BURST_REQ) {
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: NO BURST REQ!\n",
__func__);
} else if (df_rgx_is_active()) {
ret = set_desired_frequency_khz(g_dfrgx->bus_freq_data,
a_available_state_freq[g_dfrgx->gpu_utilization_record_index].freq);
if (ret <= 0) {
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: Failed to burst/unburst at : %lu !\n",
__func__, a_available_state_freq[g_dfrgx->gpu_utilization_record_index].freq);
}
}
}
}
/**
* wake_thread() - Wake the work thread.
* @df_rgx_data_s: dfrgx burst handle.
*/
static void wake_thread(struct df_rgx_data_s *g_dfrgx)
{
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: time to wake up!\n",
__func__);
if (g_dfrgx->g_task)
wake_up_process(g_dfrgx->g_task);
}
/**
* df_rgx_action() - Perform utilization stats polling and freq burst.
* @df_rgx_data_s: dfrgx burst handle.
*/
static int df_rgx_action(struct df_rgx_data_s *g_dfrgx)
{
struct gpu_util_stats util_stats;
/*Initialize the data*/
memset(&util_stats, 0, sizeof(struct gpu_util_stats));
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: !\n",
__func__);
/*Let's check if We can query the utilisation numbers now*/
if (!g_dfrgx->g_initialized) {
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: Not initialized yet !\n",
__func__);
goto go_out;
}
if (!df_rgx_is_active()) {
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: RGX not Active!\n",
__func__);
goto go_out;
}
/* This will happen when min or max freq are modified or using userpace governor*/
if(g_dfrgx->bus_freq_data->bf_desired_freq && g_dfrgx->bus_freq_data->b_need_freq_update)
{
int ret = 0;
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: desiredfreq: %lu, prevrlzd %lu, prevfreq %lu !\n",
__func__,g_dfrgx->bus_freq_data->bf_desired_freq,
g_dfrgx->bus_freq_data->bf_prev_freq_rlzd,
g_dfrgx->bus_freq_data->devfreq->previous_freq);
ret = set_desired_frequency_khz(g_dfrgx->bus_freq_data,
g_dfrgx->bus_freq_data->bf_desired_freq);
if (ret <= 0) {
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: Failed to set at : %lu !\n",
__func__,g_dfrgx->bus_freq_data->bf_desired_freq);
}
/*freq was changed and We don't need this thread working for now, so let it be disabled*/
else if (dfrgx_burst_is_enabled(g_dfrgx)
&& g_dfrgx->g_profile_index != DFRGX_TURBO_PROFILE_SIMPLE_ON_DEMAND
&& g_dfrgx->g_profile_index != DFRGX_TURBO_PROFILE_CUSTOM) {
/*freq was changed and We don't need this thread
* working for now, so let it be disabled
*/
dfrgx_burst_set_enable(g_dfrgx, 0);
return 1;
}
}
/*So don't need to do any utilization polling when
* simple_on_demand is not the current governor
*/
if (g_dfrgx->g_profile_index != DFRGX_TURBO_PROFILE_SIMPLE_ON_DEMAND
&& g_dfrgx->g_profile_index != DFRGX_TURBO_PROFILE_CUSTOM)
return 1;
if (gpu_rgx_get_util_stats(&util_stats)) {
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: Active: %llu, "
"Blocked: %llu, Idle: %llu !\n",
__func__,
util_stats.ui64GpuStatActiveHigh,
util_stats.ui64GpuStatBlocked,
util_stats.ui64GpuStatIdle);
dfrgx_add_sample_data(g_dfrgx, util_stats);
} else {
DFRGX_DPF(DFRGX_DEBUG_MED, "%s: Invalid Util stats !\n",
__func__);
}
go_out:
return 1;
}
/**
* work_thread_loop() - the main loop for the worker thread.
* @pvd: The "void *" private data provided to kthread_create.
* This can be cast to the gbprv handle.
*
* Upon return, thread will exit.
*/
static int df_rgx_worker_thread(void *pvd)
{
struct df_rgx_data_s *g_dfrgx = (struct df_rgx_data_s *) pvd;
int rva;
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: kernel thread started !\n",
__func__);
for (;;) {
/**
* Synchronization is via a call to:
* int wake_up_process(struct task_struct *p)
*/
set_current_state(TASK_INTERRUPTIBLE);
schedule();
rva = df_rgx_action(g_dfrgx);
if (rva == 0) {
/* Thread exit requested */
break;
}
if (kthread_should_stop())
break;
}
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: kernel thread stopping !\n",
__func__);
return 0;
}
/**
* df_rgx_create_worker_thread() - Create work thread.
* @df_rgx_data_s: dfrgx burst handle.
*
* This thread is not truely a "real-time" thread, in that there will be no
* catastrophe if its execution is somewhat delayed. However, knowing that
* the nominal execution interval for this timer-woken thread is 5 msecs and
* knowing that the thread execution will be very short, it seems appropriate
* to request an elevated scheduling priority. Perhaps a consensus will be
* reached as to whether or not this is truely a good idea.
*
* Function return value: < 0 if error, otherwise 0.
*/
static int df_rgx_create_worker_thread(struct df_rgx_data_s *g_dfrgx)
{
struct task_struct *tskhdl;
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: creating the thread !\n",
__func__);
if (!g_dfrgx->g_task) {
tskhdl = kthread_create(df_rgx_worker_thread,
(void *) g_dfrgx, "kdfrgx");
/* Keep a reference on task structure. */
get_task_struct(tskhdl);
if (IS_ERR(tskhdl) || !tskhdl) {
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: kernel thread"
" create fail !\n",
__func__);
return PTR_ERR(tskhdl);
}
g_dfrgx->g_task = tskhdl;
wake_up_process(tskhdl);
}
return 0;
}
/**
* df_rgx_stop_worker_thread - kill the worker thread.
* @df_rgx_data_s: dfrgx burst handle.
*/
static void df_rgx_stop_worker_thread(struct df_rgx_data_s *g_dfrgx)
{
if (g_dfrgx->g_task) {
/* kthread_stop will not return until the thread is gone. */
kthread_stop(g_dfrgx->g_task);
put_task_struct(g_dfrgx->g_task);
g_dfrgx->g_task = NULL;
}
}
/**
* hrt_start() - start (or restart) timer.
* @df_rgx_data_s: dfrgx burst handle.
*/
static void hrt_start(struct df_rgx_data_s *g_dfrgx)
{
if (g_dfrgx->g_enable) {
if (g_dfrgx->g_timer_is_enabled) {
/* Due to the gbp_timer is auto-restart timer
* in most case, we must use hrtimer_cancel
* it at first if it is in active state, to avoid
* hitting the BUG_ON(timer->state !=
* HRTIMER_STATE_CALLBACK) in hrtimer.c.
*/
hrtimer_cancel(&g_dfrgx->g_timer);
} else {
g_dfrgx->g_timer_is_enabled = 1;
}
hrtimer_start(&g_dfrgx->g_timer, g_dfrgx->g_hrt_period,
HRTIMER_MODE_REL);
}
}
/**
* hrt_cancel() - cancel a timer.
* @df_rgx_data_s: dfrgx burst handle.
*/
static void hrt_cancel(struct df_rgx_data_s *g_dfrgx)
{
/* The timer can be restarted with hrtimer_start. */
hrtimer_cancel(&g_dfrgx->g_timer);
g_dfrgx->g_timer_is_enabled = 0;
}
/**
* hrt_event_processor() - Process timer-driven things.
* Called by kernel hrtimer system when the timer expires.
* @hrthdl: Pointer to the associated hrtimer struct.
*
* Execution context: hard irq level.
* Invoked via interrupt/callback.
*/
static enum hrtimer_restart hrt_event_processor(struct hrtimer *hrthdl)
{
struct df_rgx_data_s *g_dfrgx =
container_of(hrthdl, struct df_rgx_data_s, g_timer);
ktime_t mc_now;
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: time is up! -- init %d,"
" enable %d, suspended %d !\n",
__func__,
g_dfrgx->g_initialized,
g_dfrgx->g_enable,
g_dfrgx->g_suspended);
if (g_dfrgx->g_initialized && g_dfrgx->g_enable &&
!g_dfrgx->g_suspended) {
wake_thread(g_dfrgx);
}
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: time is up! --"
" timer_enabled %d !\n",
__func__,
g_dfrgx->g_timer_is_enabled);
if (!g_dfrgx->g_timer_is_enabled)
return HRTIMER_NORESTART;
mc_now = ktime_get();
hrtimer_forward(hrthdl, mc_now, g_dfrgx->g_hrt_period);
return HRTIMER_RESTART;
}
/**
* dfrgx_burst_resume() - Callback for gfx hw transition from state D3.
* @df_rgx_data_s: dfrgx burst handle.
*
* Device power on. Assume the device has retained no state.
* Invoked via interrupt/callback.
* Execution context: non-atomic
*/
static int dfrgx_burst_resume(struct df_rgx_data_s *g_dfrgx)
{
if (!g_dfrgx || !g_dfrgx->g_initialized || !g_dfrgx->g_enable)
return 0;
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: resume!\n",
__func__);
g_dfrgx->g_suspended = 0;
/*Need to update the freq after coming back from D0i3/S0i3*/
mutex_lock(&g_dfrgx->bus_freq_data->lock);
g_dfrgx->bus_freq_data->b_resumed = 1;
g_dfrgx->bus_freq_data->b_need_freq_update = 1;
mutex_unlock(&g_dfrgx->bus_freq_data->lock);
hrt_start(g_dfrgx);
return 0;
}
/**
* dfrgx_burst_suspend() - Callback for gfx hw transition from state D3.
* @df_rgx_data_s: dfrgx burst handle.
*
* Device power on. Assume the device has retained no state.
* Invoked via interrupt/callback.
* Execution context: non-atomic
*/
static int dfrgx_burst_suspend(struct df_rgx_data_s *g_dfrgx)
{
if (!g_dfrgx || !g_dfrgx->g_initialized || !g_dfrgx->g_enable)
return 0;
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s: suspend!\n",
__func__);
g_dfrgx->g_suspended = 1;
hrt_cancel(g_dfrgx);
return 0;
}
/**
* frgx_burst_set_enable() - Is burst enabled?.
* @df_rgx_data_s: dfrgx burst handle.
*
* Device power on. Assume the device has retained no state.
* Invoked via interrupt/callback.
* Execution context: non-atomic
*/
void dfrgx_burst_set_enable(struct df_rgx_data_s *g_dfrgx, int enable)
{
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s enable: %d\n",
__func__, enable);
if (!g_dfrgx || !g_dfrgx->g_initialized)
return;
mutex_lock(&g_dfrgx->g_mutex_sts);
if (g_dfrgx->g_enable != enable) {
g_dfrgx->g_enable = enable;
if (g_dfrgx->g_enable)
hrt_start(g_dfrgx);
else
hrt_cancel(g_dfrgx);
}
mutex_unlock(&g_dfrgx->g_mutex_sts);
}
/**
* dfrgx_burst_is_enabled() - Is burst enabled?.
* @df_rgx_data_s: dfrgx burst handle.
*
* Device power on. Assume the device has retained no state.
* Invoked via interrupt/callback.
* Execution context: non-atomic
*/
int dfrgx_burst_is_enabled(struct df_rgx_data_s *g_dfrgx)
{
int enabled;
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s\n",
__func__);
if (!g_dfrgx || !g_dfrgx->g_initialized)
return 0;
mutex_lock(&g_dfrgx->g_mutex_sts);
enabled = g_dfrgx->g_enable;
mutex_unlock(&g_dfrgx->g_mutex_sts);
return enabled;
}
/**
* dfrgx_profiling_is_enabled() - Is profiling enabled?.
* @df_rgx_data_s: dfrgx burst handle.
*
* Device power on. Assume the device has retained no state.
* Invoked via interrupt/callback.
* Execution context: non-atomic
*/
void dfrgx_profiling_set_enable(struct df_rgx_data_s *g_dfrgx, int enable)
{
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s enable: %d\n",
__func__,
enable);
if (!g_dfrgx || !g_dfrgx->g_initialized)
return;
if (g_dfrgx->g_profiling_enable != enable)
g_dfrgx->g_profiling_enable = enable;
}
/**
* dfrgx_profiling_is_enabled() - Is profiling enabled?.
* @df_rgx_data_s: dfrgx burst handle.
*
* Device power on. Assume the device has retained no state.
* Invoked via interrupt/callback.
* Execution context: non-atomic
*/
int dfrgx_profiling_is_enabled(struct df_rgx_data_s *g_dfrgx)
{
int enabled;
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s\n",
__func__);
if (!g_dfrgx || !g_dfrgx->g_initialized)
return 0;
enabled = g_dfrgx->g_profiling_enable;
return enabled;
}
/**
* dfrgx_power_state_set() - Callback informing of gfx
* hw power state change.
* @df_rgx_data_s: dfrgx burst handle.
* @st_on: 1 if powering on, 0 if powering down.
*/
static void dfrgx_power_state_set(struct df_rgx_data_s *g_dfrgx,
int st_on)
{
if (g_dfrgx->g_enable) {
if (st_on)
dfrgx_burst_resume(g_dfrgx);
else
dfrgx_burst_suspend(g_dfrgx);
}
}
/**
* dfrgx_burst_init() - dfrgx burst module initialization.
* @df_rgx_data_s: dfrgx burst handle.
*
* Invokes sub-function to initialize. If failure, invokes cleanup.
*
* Function return value: negative to abort module installation.
*/
int dfrgx_burst_init(struct df_rgx_data_s *g_dfrgx)
{
int sts = 0;
unsigned int error = 0;
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s:gpu burst mode initialization"
" -- begin !\n",
__func__);
mutex_init(&g_dfrgx->g_mutex_sts);
/* No 3D activity is initialized to run until kernel finish
* its initialization. so here make the gburst status consistent
* with HW
* */
g_dfrgx->g_suspended = 1;
g_dfrgx->g_hrt_period = ktime_set(0,
DFRGX_BURST_TIMER_PERIOD_DEFAULT_USECS * NSEC_PER_USEC);
{
struct dfrgx_interface_s dfrgx_interface;
dfrgx_interface.dfrgx_priv = g_dfrgx;
dfrgx_interface.dfrgx_power_state_set = dfrgx_power_state_set;
dfrgx_interface_set_data(&dfrgx_interface);
}
error = dev_freq_add_attributes_to_sysfs(g_dfrgx->bus_freq_data->dev);
if (error)
goto attrib_creation_failed;
/* Initialize profiling info*/
g_dfrgx->g_profiling_enable = 0;
gpu_profiling_records_init();
/* Initialize timer. This does not start the timer. */
hrtimer_init(&g_dfrgx->g_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
g_dfrgx->g_timer.function = &hrt_event_processor;
/* FIXME: Need to re-think this*/
msleep(500);
error = gpu_rgx_utilstats_init_obj();
if (error) {
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s:gpu_rgx_utilstats_init_obj"
" -- failed!\n",
__func__);
sts = -EAGAIN;
goto error_init_obj;
}
if (g_dfrgx->g_enable) {
hrt_start(g_dfrgx);
sts = df_rgx_create_worker_thread(g_dfrgx);
if (sts < 0) {
/* abort init if unable to create thread. */
DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s:thread creation"
" failed %d !\n",
__func__,
-sts);
goto err_thread_creation;
}
}
g_dfrgx->g_initialized = 1;
/* Initialize to suspended state */
/* Allows system to enter sleep states while charging */
dfrgx_burst_suspend(g_dfrgx);
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s:gpu burst mode initialization"
" -- done!\n",
__func__);
return 0;
error_init_obj:
df_rgx_stop_worker_thread(g_dfrgx);
attrib_creation_failed:
err_thread_creation:
{
struct dfrgx_interface_s dfrgx_interface;
memset(&dfrgx_interface, 0, sizeof(struct dfrgx_interface_s));
dfrgx_interface_set_data(&dfrgx_interface);
}
mutex_destroy(&g_dfrgx->g_mutex_sts);
return sts;
}
/**
* dfrgx_burst_deinit() - dfrgx burst module initialization.
* @df_rgx_data_s: dfrgx burst handle.
*
* Invokes sub-function to initialize. If failure, invokes cleanup.
*
* Function return value: negative to abort module installation.
*/
void dfrgx_burst_deinit(struct df_rgx_data_s *g_dfrgx)
{
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s:gpu burst mode deinitialization"
" -- begin !\n",
__func__);
{
struct dfrgx_interface_s dfrgx_interface;
memset(&dfrgx_interface, 0, sizeof(struct dfrgx_interface_s));
dfrgx_interface_set_data(&dfrgx_interface);
}
dev_freq_remove_attributes_on_sysfs(g_dfrgx->bus_freq_data->dev);
gpu_rgx_utilstats_deinit_obj();
hrt_cancel(g_dfrgx);
g_dfrgx->g_timer.function = NULL;
df_rgx_stop_worker_thread(g_dfrgx);
mutex_destroy(&g_dfrgx->g_mutex_sts);
g_dfrgx->g_initialized = 0;
DFRGX_DPF(DFRGX_DEBUG_LOW, "%s:gpu burst mode deinitialization"
" -- done!\n",
__func__);
return;
}