forked from rubenslte/android_kernel_samsung_msm8226
msm: kgsl: improve active_cnt and ACTIVE state management
Require any code path which intends to touch the hardware to take a reference on active_cnt with kgsl_active_count_get() and release it with kgsl_active_count_put() when finished. These functions now do the wake / sleep steps that were previously handled by kgsl_check_suspended() and kgsl_check_idle(). Additionally, kgsl_pre_hwaccess() will no longer turn on the clocks, it just enforces via BUG_ON that the clocks are enabled before a register is touched. Change-Id: I31b0d067e6d600f0228450dbd73f69caa919ce13 Signed-off-by: Jeremy Gebben <jgebben@codeaurora.org>
This commit is contained in:
@@ -2773,7 +2773,7 @@ static unsigned int adreno_isidle(struct kgsl_device *device)
|
||||
if (device->state == KGSL_STATE_ACTIVE) {
|
||||
/* Is the ring buffer is empty? */
|
||||
GSL_RB_GET_READPTR(rb, &rb->rptr);
|
||||
if (!device->active_cnt && (rb->rptr == rb->wptr)) {
|
||||
if (rb->rptr == rb->wptr) {
|
||||
/*
|
||||
* Are there interrupts pending? If so then pretend we
|
||||
* are not idle - this avoids the possiblity that we go
|
||||
@@ -3591,15 +3591,20 @@ static void adreno_power_stats(struct kgsl_device *device,
|
||||
{
|
||||
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
|
||||
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
|
||||
unsigned int cycles;
|
||||
unsigned int cycles = 0;
|
||||
|
||||
/* Get the busy cycles counted since the counter was last reset */
|
||||
/* Calling this function also resets and restarts the counter */
|
||||
/*
|
||||
* Get the busy cycles counted since the counter was last reset.
|
||||
* If we're not currently active, there shouldn't have been
|
||||
* any cycles since the last time this function was called.
|
||||
*/
|
||||
if (device->state == KGSL_STATE_ACTIVE)
|
||||
cycles = adreno_dev->gpudev->busy_cycles(adreno_dev);
|
||||
|
||||
cycles = adreno_dev->gpudev->busy_cycles(adreno_dev);
|
||||
|
||||
/* In order to calculate idle you have to have run the algorithm *
|
||||
* at least once to get a start time. */
|
||||
/*
|
||||
* In order to calculate idle you have to have run the algorithm
|
||||
* at least once to get a start time.
|
||||
*/
|
||||
if (pwr->time != 0) {
|
||||
s64 tmp = ktime_to_us(ktime_get());
|
||||
stats->total_time = tmp - pwr->time;
|
||||
|
||||
@@ -93,4 +93,7 @@ void adreno_debugfs_init(struct kgsl_device *device)
|
||||
adreno_dev->ft_pf_policy = KGSL_FT_PAGEFAULT_DEFAULT_POLICY;
|
||||
debugfs_create_u32("ft_pagefault_policy", 0644, device->d_debugfs,
|
||||
&adreno_dev->ft_pf_policy);
|
||||
|
||||
debugfs_create_u32("active_cnt", 0444, device->d_debugfs,
|
||||
&device->active_cnt);
|
||||
}
|
||||
|
||||
+48
-52
@@ -413,27 +413,6 @@ kgsl_context_destroy(struct kref *kref)
|
||||
kfree(context);
|
||||
}
|
||||
|
||||
static void kgsl_check_idle_locked(struct kgsl_device *device)
|
||||
{
|
||||
if (device->pwrctrl.nap_allowed == true &&
|
||||
device->state == KGSL_STATE_ACTIVE &&
|
||||
device->requested_state == KGSL_STATE_NONE) {
|
||||
kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
|
||||
kgsl_pwrscale_idle(device);
|
||||
if (kgsl_pwrctrl_sleep(device) != 0)
|
||||
mod_timer(&device->idle_timer,
|
||||
jiffies +
|
||||
device->pwrctrl.interval_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
static void kgsl_check_idle(struct kgsl_device *device)
|
||||
{
|
||||
mutex_lock(&device->mutex);
|
||||
kgsl_check_idle_locked(device);
|
||||
mutex_unlock(&device->mutex);
|
||||
}
|
||||
|
||||
struct kgsl_device *kgsl_get_device(int dev_idx)
|
||||
{
|
||||
int i;
|
||||
@@ -496,13 +475,12 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state)
|
||||
policy_saved = device->pwrscale.policy;
|
||||
device->pwrscale.policy = NULL;
|
||||
kgsl_pwrctrl_request_state(device, KGSL_STATE_SUSPEND);
|
||||
/* Make sure no user process is waiting for a timestamp *
|
||||
* before supending */
|
||||
if (device->active_cnt != 0) {
|
||||
mutex_unlock(&device->mutex);
|
||||
wait_for_completion(&device->suspend_gate);
|
||||
mutex_lock(&device->mutex);
|
||||
}
|
||||
/*
|
||||
* Make sure no user process is waiting for a timestamp
|
||||
* before supending.
|
||||
*/
|
||||
kgsl_active_count_wait(device);
|
||||
|
||||
/* Don't let the timer wake us during suspended sleep. */
|
||||
del_timer_sync(&device->idle_timer);
|
||||
switch (device->state) {
|
||||
@@ -513,6 +491,8 @@ static int kgsl_suspend_device(struct kgsl_device *device, pm_message_t state)
|
||||
device->ftbl->idle(device);
|
||||
case KGSL_STATE_NAP:
|
||||
case KGSL_STATE_SLEEP:
|
||||
/* make sure power is on to stop the device */
|
||||
kgsl_pwrctrl_enable(device);
|
||||
/* Get the completion ready to be waited upon. */
|
||||
INIT_COMPLETION(device->hwaccess_gate);
|
||||
device->ftbl->suspend_context(device);
|
||||
@@ -632,9 +612,16 @@ void kgsl_late_resume_driver(struct early_suspend *h)
|
||||
device->pwrctrl.restore_slumber = false;
|
||||
if (device->pwrscale.policy == NULL)
|
||||
kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
|
||||
kgsl_pwrctrl_wake(device);
|
||||
if (kgsl_pwrctrl_wake(device) != 0)
|
||||
return;
|
||||
/*
|
||||
* We don't have a way to go directly from
|
||||
* a deeper sleep state to NAP, which is
|
||||
* the desired state here.
|
||||
*/
|
||||
kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
|
||||
kgsl_pwrctrl_sleep(device);
|
||||
mutex_unlock(&device->mutex);
|
||||
kgsl_check_idle(device);
|
||||
KGSL_PWR_WARN(device, "late resume end\n");
|
||||
}
|
||||
EXPORT_SYMBOL(kgsl_late_resume_driver);
|
||||
@@ -745,7 +732,7 @@ static int kgsl_release(struct inode *inodep, struct file *filep)
|
||||
filep->private_data = NULL;
|
||||
|
||||
mutex_lock(&device->mutex);
|
||||
kgsl_check_suspended(device);
|
||||
kgsl_active_count_get(device);
|
||||
|
||||
while (1) {
|
||||
context = idr_get_next(&device->context_idr, &next);
|
||||
@@ -767,10 +754,17 @@ static int kgsl_release(struct inode *inodep, struct file *filep)
|
||||
|
||||
device->open_count--;
|
||||
if (device->open_count == 0) {
|
||||
BUG_ON(device->active_cnt > 1);
|
||||
result = device->ftbl->stop(device);
|
||||
kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
|
||||
/*
|
||||
* active_cnt special case: we just stopped the device,
|
||||
* so no need to use kgsl_active_count_put()
|
||||
*/
|
||||
device->active_cnt--;
|
||||
} else {
|
||||
kgsl_active_count_put(device);
|
||||
}
|
||||
|
||||
mutex_unlock(&device->mutex);
|
||||
kfree(dev_priv);
|
||||
|
||||
@@ -816,9 +810,14 @@ static int kgsl_open(struct inode *inodep, struct file *filep)
|
||||
filep->private_data = dev_priv;
|
||||
|
||||
mutex_lock(&device->mutex);
|
||||
kgsl_check_suspended(device);
|
||||
|
||||
if (device->open_count == 0) {
|
||||
/*
|
||||
* active_cnt special case: we are starting up for the first
|
||||
* time, so use this sequence instead of the kgsl_pwrctrl_wake()
|
||||
* which will be called by kgsl_active_count_get().
|
||||
*/
|
||||
device->active_cnt++;
|
||||
kgsl_sharedmem_set(&device->memstore, 0, 0,
|
||||
device->memstore.size);
|
||||
|
||||
@@ -831,6 +830,7 @@ static int kgsl_open(struct inode *inodep, struct file *filep)
|
||||
goto err_freedevpriv;
|
||||
|
||||
kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
|
||||
kgsl_active_count_put(device);
|
||||
}
|
||||
device->open_count++;
|
||||
mutex_unlock(&device->mutex);
|
||||
@@ -856,10 +856,15 @@ err_stop:
|
||||
mutex_lock(&device->mutex);
|
||||
device->open_count--;
|
||||
if (device->open_count == 0) {
|
||||
/* make sure power is on to stop the device */
|
||||
kgsl_pwrctrl_enable(device);
|
||||
result = device->ftbl->stop(device);
|
||||
kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
|
||||
}
|
||||
err_freedevpriv:
|
||||
/* only the first open takes an active count */
|
||||
if (device->open_count == 0)
|
||||
device->active_cnt--;
|
||||
mutex_unlock(&device->mutex);
|
||||
filep->private_data = NULL;
|
||||
kfree(dev_priv);
|
||||
@@ -1073,10 +1078,6 @@ static long _device_waittimestamp(struct kgsl_device_private *dev_priv,
|
||||
struct kgsl_device *device = dev_priv->device;
|
||||
unsigned int context_id = context ? context->id : KGSL_MEMSTORE_GLOBAL;
|
||||
|
||||
/* Set the active count so that suspend doesn't do the wrong thing */
|
||||
|
||||
device->active_cnt++;
|
||||
|
||||
trace_kgsl_waittimestamp_entry(device, context_id,
|
||||
kgsl_readtimestamp(device, context,
|
||||
KGSL_TIMESTAMP_RETIRED),
|
||||
@@ -1090,9 +1091,6 @@ static long _device_waittimestamp(struct kgsl_device_private *dev_priv,
|
||||
KGSL_TIMESTAMP_RETIRED),
|
||||
result);
|
||||
|
||||
/* Fire off any pending suspend operations that are in flight */
|
||||
kgsl_active_count_put(dev_priv->device);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1887,7 +1885,6 @@ static long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv,
|
||||
|
||||
trace_kgsl_mem_map(entry, param->fd);
|
||||
|
||||
kgsl_check_idle(dev_priv->device);
|
||||
return result;
|
||||
|
||||
error_unmap:
|
||||
@@ -1907,7 +1904,6 @@ error_put_file_ptr:
|
||||
}
|
||||
error:
|
||||
kfree(entry);
|
||||
kgsl_check_idle(dev_priv->device);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -2035,7 +2031,6 @@ _gpumem_alloc(struct kgsl_device_private *dev_priv,
|
||||
|
||||
entry->memtype = KGSL_MEM_ENTRY_KERNEL;
|
||||
|
||||
kgsl_check_idle(dev_priv->device);
|
||||
*ret_entry = entry;
|
||||
return result;
|
||||
err:
|
||||
@@ -2370,7 +2365,7 @@ static const struct {
|
||||
kgsl_ioctl_cff_user_event, 0),
|
||||
KGSL_IOCTL_FUNC(IOCTL_KGSL_TIMESTAMP_EVENT,
|
||||
kgsl_ioctl_timestamp_event,
|
||||
KGSL_IOCTL_LOCK),
|
||||
KGSL_IOCTL_LOCK | KGSL_IOCTL_WAKE),
|
||||
KGSL_IOCTL_FUNC(IOCTL_KGSL_SETPROPERTY,
|
||||
kgsl_ioctl_device_setproperty,
|
||||
KGSL_IOCTL_LOCK | KGSL_IOCTL_WAKE),
|
||||
@@ -2462,14 +2457,19 @@ static long kgsl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
|
||||
|
||||
if (lock) {
|
||||
mutex_lock(&dev_priv->device->mutex);
|
||||
if (use_hw)
|
||||
kgsl_check_suspended(dev_priv->device);
|
||||
if (use_hw) {
|
||||
ret = kgsl_active_count_get(dev_priv->device);
|
||||
if (ret < 0)
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
|
||||
ret = func(dev_priv, cmd, uptr);
|
||||
|
||||
unlock:
|
||||
if (lock) {
|
||||
kgsl_check_idle_locked(dev_priv->device);
|
||||
if (use_hw)
|
||||
kgsl_active_count_put(dev_priv->device);
|
||||
mutex_unlock(&dev_priv->device->mutex);
|
||||
}
|
||||
|
||||
@@ -3032,11 +3032,7 @@ int kgsl_postmortem_dump(struct kgsl_device *device, int manual)
|
||||
/* For a manual dump, make sure that the system is idle */
|
||||
|
||||
if (manual) {
|
||||
if (device->active_cnt != 0) {
|
||||
mutex_unlock(&device->mutex);
|
||||
wait_for_completion(&device->suspend_gate);
|
||||
mutex_lock(&device->mutex);
|
||||
}
|
||||
kgsl_active_count_wait(device);
|
||||
|
||||
if (device->state == KGSL_STATE_ACTIVE)
|
||||
kgsl_idle(device);
|
||||
|
||||
@@ -454,23 +454,4 @@ kgsl_context_put(struct kgsl_context *context)
|
||||
kref_put(&context->refcount, kgsl_context_destroy);
|
||||
}
|
||||
|
||||
/**
|
||||
* kgsl_active_count_put - Decrease the device active count
|
||||
* @device: Pointer to a KGSL device
|
||||
*
|
||||
* Decrease the active count for the KGSL device and trigger the suspend_gate
|
||||
* completion if it hits zero
|
||||
*/
|
||||
static inline void
|
||||
kgsl_active_count_put(struct kgsl_device *device)
|
||||
{
|
||||
if (device->active_cnt == 1)
|
||||
INIT_COMPLETION(device->suspend_gate);
|
||||
|
||||
device->active_cnt--;
|
||||
|
||||
if (device->active_cnt == 0)
|
||||
complete(&device->suspend_gate);
|
||||
}
|
||||
|
||||
#endif /* __KGSL_DEVICE_H */
|
||||
|
||||
@@ -51,6 +51,7 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
|
||||
void (*cb)(struct kgsl_device *, void *, u32, u32), void *priv,
|
||||
void *owner)
|
||||
{
|
||||
int ret;
|
||||
struct kgsl_event *event;
|
||||
unsigned int cur_ts;
|
||||
struct kgsl_context *context = NULL;
|
||||
@@ -82,6 +83,16 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
|
||||
if (event == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Increase the active count on the device to avoid going into power
|
||||
* saving modes while events are pending
|
||||
*/
|
||||
ret = kgsl_active_count_get_light(device);
|
||||
if (ret < 0) {
|
||||
kfree(event);
|
||||
return ret;
|
||||
}
|
||||
|
||||
event->context = context;
|
||||
event->timestamp = ts;
|
||||
event->priv = priv;
|
||||
@@ -112,13 +123,6 @@ int kgsl_add_event(struct kgsl_device *device, u32 id, u32 ts,
|
||||
} else
|
||||
_add_event_to_list(&device->events, event);
|
||||
|
||||
/*
|
||||
* Increase the active count on the device to avoid going into power
|
||||
* saving modes while events are pending
|
||||
*/
|
||||
|
||||
device->active_cnt++;
|
||||
|
||||
queue_work(device->work_queue, &device->ts_expired_ws);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+161
-49
@@ -1055,6 +1055,14 @@ void kgsl_pwrctrl_close(struct kgsl_device *device)
|
||||
pwr->power_flags = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* kgsl_idle_check() - Work function for GPU interrupts and idle timeouts.
|
||||
* @device: The device
|
||||
*
|
||||
* This function is called for work that is queued by the interrupt
|
||||
* handler or the idle timer. It attempts to transition to a clocks
|
||||
* off state if the active_cnt is 0 and the hardware is idle.
|
||||
*/
|
||||
void kgsl_idle_check(struct work_struct *work)
|
||||
{
|
||||
struct kgsl_device *device = container_of(work, struct kgsl_device,
|
||||
@@ -1064,15 +1072,22 @@ void kgsl_idle_check(struct work_struct *work)
|
||||
return;
|
||||
|
||||
mutex_lock(&device->mutex);
|
||||
if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) {
|
||||
kgsl_pwrscale_idle(device);
|
||||
|
||||
if (kgsl_pwrctrl_sleep(device) != 0) {
|
||||
kgsl_pwrscale_idle(device);
|
||||
|
||||
if (device->state == KGSL_STATE_ACTIVE
|
||||
|| device->state == KGSL_STATE_NAP) {
|
||||
if (device->active_cnt > 0 || kgsl_pwrctrl_sleep(device) != 0) {
|
||||
|
||||
kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
|
||||
|
||||
mod_timer(&device->idle_timer,
|
||||
jiffies +
|
||||
device->pwrctrl.interval_timeout);
|
||||
/* If the GPU has been too busy to sleep, make sure *
|
||||
* that is acurately reflected in the % busy numbers. */
|
||||
/*
|
||||
* If the GPU has been too busy to sleep, make sure
|
||||
* that is acurately reflected in the % busy numbers.
|
||||
*/
|
||||
device->pwrctrl.clk_stats.no_nap_cnt++;
|
||||
if (device->pwrctrl.clk_stats.no_nap_cnt >
|
||||
UPDATE_BUSY) {
|
||||
@@ -1104,54 +1119,26 @@ void kgsl_timer(unsigned long data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* kgsl_pre_hwaccess - Enforce preconditions for touching registers
|
||||
* @device: The device
|
||||
*
|
||||
* This function ensures that the correct lock is held and that the GPU
|
||||
* clock is on immediately before a register is read or written. Note
|
||||
* that this function does not check active_cnt because the registers
|
||||
* must be accessed during device start and stop, when the active_cnt
|
||||
* may legitimately be 0.
|
||||
*/
|
||||
void kgsl_pre_hwaccess(struct kgsl_device *device)
|
||||
{
|
||||
/* In order to touch a register you must hold the device mutex...*/
|
||||
BUG_ON(!mutex_is_locked(&device->mutex));
|
||||
switch (device->state) {
|
||||
case KGSL_STATE_ACTIVE:
|
||||
return;
|
||||
case KGSL_STATE_NAP:
|
||||
case KGSL_STATE_SLEEP:
|
||||
case KGSL_STATE_SLUMBER:
|
||||
kgsl_pwrctrl_wake(device);
|
||||
break;
|
||||
case KGSL_STATE_SUSPEND:
|
||||
kgsl_check_suspended(device);
|
||||
break;
|
||||
case KGSL_STATE_INIT:
|
||||
case KGSL_STATE_HUNG:
|
||||
case KGSL_STATE_DUMP_AND_FT:
|
||||
if (test_bit(KGSL_PWRFLAGS_CLK_ON,
|
||||
&device->pwrctrl.power_flags))
|
||||
break;
|
||||
else
|
||||
KGSL_PWR_ERR(device,
|
||||
"hw access while clocks off from state %d\n",
|
||||
device->state);
|
||||
break;
|
||||
default:
|
||||
KGSL_PWR_ERR(device, "hw access while in unknown state %d\n",
|
||||
device->state);
|
||||
break;
|
||||
}
|
||||
/* and have the clock on! */
|
||||
BUG_ON(!test_bit(KGSL_PWRFLAGS_CLK_ON, &device->pwrctrl.power_flags));
|
||||
}
|
||||
EXPORT_SYMBOL(kgsl_pre_hwaccess);
|
||||
|
||||
void kgsl_check_suspended(struct kgsl_device *device)
|
||||
{
|
||||
if (device->requested_state == KGSL_STATE_SUSPEND ||
|
||||
device->state == KGSL_STATE_SUSPEND) {
|
||||
mutex_unlock(&device->mutex);
|
||||
wait_for_completion(&device->hwaccess_gate);
|
||||
mutex_lock(&device->mutex);
|
||||
} else if (device->state == KGSL_STATE_DUMP_AND_FT) {
|
||||
mutex_unlock(&device->mutex);
|
||||
wait_for_completion(&device->ft_gate);
|
||||
mutex_lock(&device->mutex);
|
||||
} else if (device->state == KGSL_STATE_SLUMBER)
|
||||
kgsl_pwrctrl_wake(device);
|
||||
}
|
||||
|
||||
static int
|
||||
_nap(struct kgsl_device *device)
|
||||
{
|
||||
@@ -1230,6 +1217,8 @@ _slumber(struct kgsl_device *device)
|
||||
case KGSL_STATE_NAP:
|
||||
case KGSL_STATE_SLEEP:
|
||||
del_timer_sync(&device->idle_timer);
|
||||
/* make sure power is on to stop the device*/
|
||||
kgsl_pwrctrl_enable(device);
|
||||
device->ftbl->suspend_context(device);
|
||||
device->ftbl->stop(device);
|
||||
_sleep_accounting(device);
|
||||
@@ -1278,9 +1267,9 @@ EXPORT_SYMBOL(kgsl_pwrctrl_sleep);
|
||||
|
||||
/******************************************************************/
|
||||
/* Caller must hold the device mutex. */
|
||||
void kgsl_pwrctrl_wake(struct kgsl_device *device)
|
||||
int kgsl_pwrctrl_wake(struct kgsl_device *device)
|
||||
{
|
||||
int status;
|
||||
int status = 0;
|
||||
unsigned int context_id;
|
||||
unsigned int state = device->state;
|
||||
unsigned int ts_processed = 0xdeaddead;
|
||||
@@ -1329,8 +1318,10 @@ void kgsl_pwrctrl_wake(struct kgsl_device *device)
|
||||
KGSL_PWR_WARN(device, "unhandled state %s\n",
|
||||
kgsl_pwrstate_to_str(device->state));
|
||||
kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
|
||||
status = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(kgsl_pwrctrl_wake);
|
||||
|
||||
@@ -1396,3 +1387,124 @@ const char *kgsl_pwrstate_to_str(unsigned int state)
|
||||
}
|
||||
EXPORT_SYMBOL(kgsl_pwrstate_to_str);
|
||||
|
||||
|
||||
/**
|
||||
* kgsl_active_count_get() - Increase the device active count
|
||||
* @device: Pointer to a KGSL device
|
||||
*
|
||||
* Increase the active count for the KGSL device and turn on
|
||||
* clocks if this is the first reference. Code paths that need
|
||||
* to touch the hardware or wait for the hardware to complete
|
||||
* an operation must hold an active count reference until they
|
||||
* are finished. An error code will be returned if waking the
|
||||
* device fails. The device mutex must be held while *calling
|
||||
* this function.
|
||||
*/
|
||||
int kgsl_active_count_get(struct kgsl_device *device)
|
||||
{
|
||||
int ret = 0;
|
||||
BUG_ON(!mutex_is_locked(&device->mutex));
|
||||
|
||||
if (device->active_cnt == 0) {
|
||||
if (device->requested_state == KGSL_STATE_SUSPEND ||
|
||||
device->state == KGSL_STATE_SUSPEND) {
|
||||
mutex_unlock(&device->mutex);
|
||||
wait_for_completion(&device->hwaccess_gate);
|
||||
mutex_lock(&device->mutex);
|
||||
} else if (device->state == KGSL_STATE_DUMP_AND_FT) {
|
||||
mutex_unlock(&device->mutex);
|
||||
wait_for_completion(&device->ft_gate);
|
||||
mutex_lock(&device->mutex);
|
||||
}
|
||||
ret = kgsl_pwrctrl_wake(device);
|
||||
}
|
||||
if (ret == 0)
|
||||
device->active_cnt++;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(kgsl_active_count_get);
|
||||
|
||||
/**
|
||||
* kgsl_active_count_get_light() - Increase the device active count
|
||||
* @device: Pointer to a KGSL device
|
||||
*
|
||||
* Increase the active count for the KGSL device WITHOUT
|
||||
* turning on the clocks. Currently this is only used for creating
|
||||
* kgsl_events. The device mutex must be held while calling this function.
|
||||
*/
|
||||
int kgsl_active_count_get_light(struct kgsl_device *device)
|
||||
{
|
||||
BUG_ON(!mutex_is_locked(&device->mutex));
|
||||
|
||||
if (device->state != KGSL_STATE_ACTIVE) {
|
||||
dev_WARN_ONCE(device->dev, 1, "device in unexpected state %s\n",
|
||||
kgsl_pwrstate_to_str(device->state));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (device->active_cnt == 0) {
|
||||
dev_WARN_ONCE(device->dev, 1, "active count is 0!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
device->active_cnt++;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(kgsl_active_count_get_light);
|
||||
|
||||
/**
|
||||
* kgsl_active_count_put() - Decrease the device active count
|
||||
* @device: Pointer to a KGSL device
|
||||
*
|
||||
* Decrease the active count for the KGSL device and turn off
|
||||
* clocks if there are no remaining references. This function will
|
||||
* transition the device to NAP if there are no other pending state
|
||||
* changes. It also completes the suspend gate. The device mutex must
|
||||
* be held while calling this function.
|
||||
*/
|
||||
void kgsl_active_count_put(struct kgsl_device *device)
|
||||
{
|
||||
BUG_ON(!mutex_is_locked(&device->mutex));
|
||||
BUG_ON(device->active_cnt == 0);
|
||||
|
||||
kgsl_pwrscale_idle(device);
|
||||
if (device->active_cnt > 1) {
|
||||
device->active_cnt--;
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_COMPLETION(device->suspend_gate);
|
||||
|
||||
if (device->pwrctrl.nap_allowed == true &&
|
||||
(device->state == KGSL_STATE_ACTIVE &&
|
||||
device->requested_state == KGSL_STATE_NONE)) {
|
||||
kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
|
||||
if (kgsl_pwrctrl_sleep(device) != 0)
|
||||
mod_timer(&device->idle_timer,
|
||||
jiffies
|
||||
+ device->pwrctrl.interval_timeout);
|
||||
}
|
||||
device->active_cnt--;
|
||||
|
||||
if (device->active_cnt == 0)
|
||||
complete(&device->suspend_gate);
|
||||
}
|
||||
EXPORT_SYMBOL(kgsl_active_count_put);
|
||||
|
||||
/**
|
||||
* kgsl_active_count_wait() - Wait for activity to finish.
|
||||
* @device: Pointer to a KGSL device
|
||||
*
|
||||
* Block until all active_cnt users put() their reference.
|
||||
*/
|
||||
void kgsl_active_count_wait(struct kgsl_device *device)
|
||||
{
|
||||
BUG_ON(!mutex_is_locked(&device->mutex));
|
||||
|
||||
if (device->active_cnt != 0) {
|
||||
mutex_unlock(&device->mutex);
|
||||
wait_for_completion(&device->suspend_gate);
|
||||
mutex_lock(&device->mutex);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(kgsl_active_count_wait);
|
||||
|
||||
@@ -99,9 +99,8 @@ void kgsl_pwrctrl_close(struct kgsl_device *device);
|
||||
void kgsl_timer(unsigned long data);
|
||||
void kgsl_idle_check(struct work_struct *work);
|
||||
void kgsl_pre_hwaccess(struct kgsl_device *device);
|
||||
void kgsl_check_suspended(struct kgsl_device *device);
|
||||
int kgsl_pwrctrl_sleep(struct kgsl_device *device);
|
||||
void kgsl_pwrctrl_wake(struct kgsl_device *device);
|
||||
int kgsl_pwrctrl_wake(struct kgsl_device *device);
|
||||
void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
|
||||
unsigned int level);
|
||||
int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device);
|
||||
@@ -115,4 +114,10 @@ static inline unsigned long kgsl_get_clkrate(struct clk *clk)
|
||||
|
||||
void kgsl_pwrctrl_set_state(struct kgsl_device *device, unsigned int state);
|
||||
void kgsl_pwrctrl_request_state(struct kgsl_device *device, unsigned int state);
|
||||
|
||||
int kgsl_active_count_get(struct kgsl_device *device);
|
||||
int kgsl_active_count_get_light(struct kgsl_device *device);
|
||||
void kgsl_active_count_put(struct kgsl_device *device);
|
||||
void kgsl_active_count_wait(struct kgsl_device *device);
|
||||
|
||||
#endif /* __KGSL_PWRCTRL_H */
|
||||
|
||||
Reference in New Issue
Block a user