_mali_osk_errcode_t _mali_ukk_gp_start_job(void *ctx, _mali_uk_gp_start_job_s *uargs)
{
	struct mali_session_data *session;
	struct mali_gp_job *job;

	MALI_DEBUG_ASSERT_POINTER(uargs);
	MALI_DEBUG_ASSERT_POINTER(ctx);

	session = (struct mali_session_data*)ctx;

	job = mali_gp_job_create(session, uargs, mali_scheduler_get_new_id());
	if (NULL == job)
	{
		return _MALI_OSK_ERR_NOMEM;
	}

#if PROFILING_SKIP_PP_AND_GP_JOBS
#warning GP jobs will not be executed
	mali_gp_scheduler_return_job_to_user(job, MALI_TRUE);
	return _MALI_OSK_ERR_OK;
#endif

	mali_pm_core_event(MALI_CORE_EVENT_GP_START);

	mali_gp_scheduler_lock();
	_mali_osk_list_addtail(&job->list, &job_queue);
	mali_gp_scheduler_unlock();

	MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Job %u (0x%08X) queued\n", mali_gp_job_get_id(job), job));

	mali_gp_scheduler_schedule();

	return _MALI_OSK_ERR_OK;
}
_mali_osk_errcode_t mali_group_start_pp_job(struct mali_group *group, struct mali_pp_job *job, u32 sub_job)
{
	struct mali_session_data *session;
	enum mali_group_activate_pd_status activate_status;

	MALI_DEBUG_ASSERT(MALI_GROUP_CORE_STATE_IDLE == group->pp_state);

	mali_pm_core_event(MALI_CORE_EVENT_PP_START);

	session = mali_pp_job_get_session(job);

	mali_group_lock(group);

	mali_cluster_l2_cache_invalidate_all(group->cluster, mali_pp_job_get_id(job));

	activate_status = mali_group_activate_page_directory(group, session);
	if (MALI_GROUP_ACTIVATE_PD_STATUS_FAILED != activate_status)
	{
		/* if session is NOT kept Zapping is done as part of session switch */
		if (MALI_GROUP_ACTIVATE_PD_STATUS_OK_KEPT_PD == activate_status)
		{
			MALI_DEBUG_PRINT(3, ("PP starting job PD_Switch 0 Flush 1 Zap 1\n"));
			mali_mmu_zap_tlb_without_stall(group->mmu);
		}
		mali_pp_job_start(group->pp_core, job, sub_job);
		group->pp_running_job = job;
		group->pp_running_sub_job = sub_job;
		group->pp_state = MALI_GROUP_CORE_STATE_WORKING;

		mali_group_unlock(group);

		return _MALI_OSK_ERR_OK;
	}

#if defined(USING_MALI200)
	group->pagedir_activation_failed = MALI_TRUE;
#endif

	mali_group_unlock(group);

	mali_pm_core_event(MALI_CORE_EVENT_PP_STOP); /* Failed to start, so "cancel" the MALI_CORE_EVENT_PP_START */
	return _MALI_OSK_ERR_FAULT;
}
void mali_gp_scheduler_job_done(struct mali_group *group, struct mali_gp_job *job, mali_bool success)
{
	MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Job %u (0x%08X) completed (%s)\n", mali_gp_job_get_id(job), job, success ? "success" : "failure"));

	mali_gp_scheduler_return_job_to_user(job, success);

	mali_gp_scheduler_lock();

	/* Mark slot as idle again */
	slot.state = MALI_GP_SLOT_STATE_IDLE;

	/* If paused, then this was the last job, so wake up sleeping workers */
	if (pause_count > 0)
	{
		_mali_osk_wait_queue_wake_up(gp_scheduler_working_wait_queue);
	}

	mali_gp_scheduler_schedule_on_group(group);

	/* It is ok to do this after schedule, since START/STOP is simply ++ and -- anyways */
	mali_pm_core_event(MALI_CORE_EVENT_GP_STOP);
}
void mali_gp_scheduler_abort_session(struct mali_session_data *session)
{
	struct mali_gp_job *job, *tmp;

	mali_gp_scheduler_lock();
	MALI_DEBUG_PRINT(3, ("Mali GP scheduler: Aborting all jobs from session 0x%08x\n", session));

	/* Check queue for jobs and remove */
	_MALI_OSK_LIST_FOREACHENTRY(job, tmp, &job_queue, struct mali_gp_job, list)
	{
		if (mali_gp_job_get_session(job) == session)
		{
			MALI_DEBUG_PRINT(4, ("Mali GP scheduler: Removing GP job 0x%08x from queue\n", job));
			_mali_osk_list_del(&(job->list));
			mali_gp_job_delete(job);

			mali_pm_core_event(MALI_CORE_EVENT_GP_STOP);
		}
	}

	mali_gp_scheduler_unlock();

	mali_group_abort_session(slot.group, session);
}
/* Group lock need to be taken before calling mali_group_complete_jobs. Will release the lock here. */
static void mali_group_complete_jobs(struct mali_group *group, mali_bool complete_gp, mali_bool complete_pp, bool success)
{
	mali_bool need_group_reset = MALI_FALSE;
	mali_bool gp_success = success;
	mali_bool pp_success = success;

	MALI_ASSERT_GROUP_LOCKED(group);

	if (complete_gp && !complete_pp)
	{
		MALI_DEBUG_ASSERT_POINTER(group->gp_core);
		if (_MALI_OSK_ERR_OK == mali_gp_reset(group->gp_core))
		{
			struct mali_gp_job *gp_job_to_return = group->gp_running_job;
			group->gp_state = MALI_GROUP_CORE_STATE_IDLE;
			group->gp_running_job = NULL;

			MALI_DEBUG_ASSERT_POINTER(gp_job_to_return);

			mali_group_deactivate_page_directory(group, mali_gp_job_get_session(gp_job_to_return));

			if(mali_group_other_reschedule_needed(group))
			{
				mali_group_unlock(group);
				mali_pp_scheduler_do_schedule();
			}
			else
			{
				mali_group_unlock(group);
			}

			mali_gp_scheduler_job_done(group, gp_job_to_return, gp_success);
			mali_pm_core_event(MALI_CORE_EVENT_GP_STOP); /* It is ok to do this after schedule, since START/STOP is simply ++ and -- anyways */

			return;
		}
		else
		{
			need_group_reset = MALI_TRUE;
			MALI_DEBUG_PRINT(3, ("Mali group: Failed to reset GP, need to reset entire group\n"));
			pp_success = MALI_FALSE; /* This might kill PP as well, so this should fail */
		}
	}
	if (complete_pp && !complete_gp)
	{
		MALI_DEBUG_ASSERT_POINTER(group->pp_core);
		if (_MALI_OSK_ERR_OK == mali_pp_reset(group->pp_core))
		{
			struct mali_pp_job *pp_job_to_return = group->pp_running_job;
			u32 pp_sub_job_to_return = group->pp_running_sub_job;
			group->pp_state = MALI_GROUP_CORE_STATE_IDLE;
			group->pp_running_job = NULL;

			MALI_DEBUG_ASSERT_POINTER(pp_job_to_return);

			mali_group_deactivate_page_directory(group, mali_pp_job_get_session(pp_job_to_return));

			if(mali_group_other_reschedule_needed(group))
			{
				mali_group_unlock(group);
				mali_gp_scheduler_do_schedule();
			}
			else
			{
				mali_group_unlock(group);
			}

			mali_pp_scheduler_job_done(group, pp_job_to_return, pp_sub_job_to_return, pp_success);
			mali_pm_core_event(MALI_CORE_EVENT_PP_STOP); /* It is ok to do this after schedule, since START/STOP is simply ++ and -- anyways */

			return;
		}
		else
		{
			need_group_reset = MALI_TRUE;
			MALI_DEBUG_PRINT(3, ("Mali group: Failed to reset PP, need to reset entire group\n"));
			gp_success = MALI_FALSE; /* This might kill GP as well, so this should fail */
		}
	}
	else if (complete_gp && complete_pp)
	{
		need_group_reset = MALI_TRUE;
	}

	if (MALI_TRUE == need_group_reset)
	{
		struct mali_gp_job *gp_job_to_return = group->gp_running_job;
		struct mali_pp_job *pp_job_to_return = group->pp_running_job;
		u32 pp_sub_job_to_return = group->pp_running_sub_job;
		mali_bool schedule_other = MALI_FALSE;

		MALI_DEBUG_PRINT(3, ("Mali group: Resetting entire group\n"));

		group->gp_state = MALI_GROUP_CORE_STATE_IDLE;
		group->gp_running_job = NULL;
		if (NULL != gp_job_to_return)
		{
			mali_group_deactivate_page_directory(group, mali_gp_job_get_session(gp_job_to_return));
		}

		group->pp_state = MALI_GROUP_CORE_STATE_IDLE;
		group->pp_running_job = NULL;
		if (NULL != pp_job_to_return)
		{
			mali_group_deactivate_page_directory(group, mali_pp_job_get_session(pp_job_to_return));
		}

		/* The reset has to be done after mali_group_deactivate_page_directory() */
		mali_group_recovery_reset(group);

		if (mali_group_other_reschedule_needed(group) && (NULL == gp_job_to_return || NULL == pp_job_to_return))
		{
			schedule_other = MALI_TRUE;
		}

		mali_group_unlock(group);

		if (NULL != gp_job_to_return)
		{
			mali_gp_scheduler_job_done(group, gp_job_to_return, gp_success);
			mali_pm_core_event(MALI_CORE_EVENT_GP_STOP); /* It is ok to do this after schedule, since START/STOP is simply ++ and -- anyways */
		}
		else if (schedule_other)
		{
			mali_pp_scheduler_do_schedule();
		}

		if (NULL != pp_job_to_return)
		{
			mali_pp_scheduler_job_done(group, pp_job_to_return, pp_sub_job_to_return, pp_success);
			mali_pm_core_event(MALI_CORE_EVENT_PP_STOP); /* It is ok to do this after schedule, since START/STOP is simply ++ and -- anyways */
		}
		else if (schedule_other)
		{
			mali_gp_scheduler_do_schedule();
		}

		return;
	}

	mali_group_unlock(group);
}