/* Caller must hold the device mutex. */ int kgsl_pwrctrl_sleep(struct kgsl_device *device) { int status = 0; KGSL_PWR_INFO(device, "sleep device %d\n", device->id); /* Work through the legal state transitions */ switch (device->requested_state) { case KGSL_STATE_NAP: if (device->pwrctrl.restore_slumber) { kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); break; } status = _nap(device); break; case KGSL_STATE_SLEEP: if (device->pwrctrl.restore_slumber) status = _slumber(device); else status = _sleep(device); break; case KGSL_STATE_SLUMBER: status = _slumber(device); break; default: KGSL_PWR_INFO(device, "bad state request 0x%x\n", device->requested_state); kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); status = -EINVAL; break; } return status; }
void kgsl_timer(unsigned long data) { struct kgsl_device *device = (struct kgsl_device *) data; KGSL_PWR_INFO(device, "idle timer expired device %d\n", device->id); if (device->requested_state != KGSL_STATE_SUSPEND) { if (device->pwrctrl.restore_slumber) kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER); else kgsl_pwrctrl_request_state(device, KGSL_STATE_SLEEP); /* Have work run in a non-interrupt context. */ queue_work(device->work_queue, &device->idle_check_ws); } }
static int _slumber(struct kgsl_device *device) { switch (device->state) { case KGSL_STATE_ACTIVE: if (!device->ftbl->isidle(device)) { kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); device->pwrctrl.restore_slumber = true; return -EBUSY; } /* fall through */ case KGSL_STATE_NAP: case KGSL_STATE_SLEEP: del_timer_sync(&device->idle_timer); kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL); device->ftbl->suspend_context(device); device->ftbl->stop(device); device->pwrctrl.restore_slumber = true; _sleep_accounting(device); kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER); if (device->idle_wakelock.name) wake_unlock(&device->idle_wakelock); break; case KGSL_STATE_SLUMBER: break; default: KGSL_PWR_WARN(device, "unhandled state %s\n", kgsl_pwrstate_to_str(device->state)); break; } return 0; }
static int _sleep(struct kgsl_device *device) { struct kgsl_pwrctrl *pwr = &device->pwrctrl; switch (device->state) { case KGSL_STATE_ACTIVE: if (!device->ftbl->isidle(device)) { kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); return -EBUSY; } /* fall through */ case KGSL_STATE_NAP: kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF); if (pwr->pwrlevels[0].gpu_freq > 0) clk_set_rate(pwr->grp_clks[0], pwr->pwrlevels[pwr->num_pwrlevels - 1]. gpu_freq); _sleep_accounting(device); kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF); kgsl_pwrctrl_set_state(device, KGSL_STATE_SLEEP); if (device->idle_wakelock.name) wake_unlock(&device->idle_wakelock); break; case KGSL_STATE_SLEEP: case KGSL_STATE_SLUMBER: break; default: KGSL_PWR_WARN(device, "unhandled state %s\n", kgsl_pwrstate_to_str(device->state)); break; } return 0; }
void kgsl_idle_check(struct work_struct *work) { struct kgsl_device *device = container_of(work, struct kgsl_device, idle_check_ws); WARN_ON(device == NULL); if (device == NULL) return; mutex_lock(&device->mutex); if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) { if ((device->requested_state != KGSL_STATE_SLEEP) && (device->requested_state != KGSL_STATE_SLUMBER)) kgsl_pwrscale_idle(device); if (kgsl_pwrctrl_sleep(device) != 0) { 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. */ device->pwrctrl.busy.no_nap_cnt++; if (device->pwrctrl.busy.no_nap_cnt > UPDATE_BUSY) { kgsl_pwrctrl_busy_time(device, true); device->pwrctrl.busy.no_nap_cnt = 0; } } } else if (device->state & (KGSL_STATE_HUNG | KGSL_STATE_DUMP_AND_RECOVER)) { kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); } mutex_unlock(&device->mutex); }
void kgsl_timer(unsigned long data) { struct kgsl_device *device = (struct kgsl_device *) data; KGSL_PWR_INFO(device, "idle timer expired device %d\n", device->id); if (device->requested_state == KGSL_STATE_NONE) { kgsl_pwrctrl_request_state(device, KGSL_STATE_SLEEP); /* Have work run in a non-interrupt context. */ queue_work(device->work_queue, &device->idle_check_ws); } }
/* Caller must hold the device mutex. */ void kgsl_pwrctrl_wake(struct kgsl_device *device) { int status; kgsl_pwrctrl_request_state(device, KGSL_STATE_ACTIVE); switch (device->state) { case KGSL_STATE_SLUMBER: status = device->ftbl->start(device, 0); if (status) { kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); KGSL_DRV_ERR(device, "start failed %d\n", status); break; } /* fall through */ case KGSL_STATE_SLEEP: kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON); kgsl_pwrscale_wake(device); /* fall through */ case KGSL_STATE_NAP: /* Turn on the core clocks */ kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON, KGSL_STATE_ACTIVE); /* Enable state before turning on irq */ kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE); kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON); /* Re-enable HW access */ mod_timer(&device->idle_timer, jiffies + device->pwrctrl.interval_timeout); if (device->idle_wakelock.name) wake_lock(&device->idle_wakelock); case KGSL_STATE_ACTIVE: kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); break; default: KGSL_PWR_WARN(device, "unhandled state %s\n", kgsl_pwrstate_to_str(device->state)); kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); break; } }
static int _nap(struct kgsl_device *device) { switch (device->state) { case KGSL_STATE_ACTIVE: if (!device->ftbl->isidle(device)) { kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); return -EBUSY; } kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF); kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF); kgsl_pwrctrl_set_state(device, KGSL_STATE_NAP); if (device->idle_wakelock.name) wake_unlock(&device->idle_wakelock); case KGSL_STATE_NAP: case KGSL_STATE_SLEEP: case KGSL_STATE_SLUMBER: break; default: kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE); break; } return 0; }