/**
 * df_rgx_request_burst() - Decides if dfrgx needs to BURST, UNBURST
 * or keep the current frequency level.
 * @pdfrgx_data: Dynamic turbo information
 * @util_percentage: percentage of utilization in active state.
 * Function return value: DFRGX_NO_BURST_REQ, DFRGX_BURST_REQ,
 * DFRGX_UNBURST_REQ.
 */
unsigned int df_rgx_request_burst(struct df_rgx_data_s *pdfrgx_data, int util_percentage)
{
	int current_index = pdfrgx_data->gpu_utilization_record_index;
	unsigned long freq = aAvailableStateFreq[current_index].freq;
	int new_index;
	unsigned int burst = DFRGX_NO_BURST_REQ;
	int n_levels = NUMBER_OF_LEVELS;

	if(!is_tng_a0)
		n_levels = NUMBER_OF_LEVELS_B0;

	new_index = df_rgx_get_util_record_index_by_freq(freq);

	if(new_index < 0)
		goto out;

	/* Decide unbusrt/burst based on utilization*/
	if(util_percentage > aGovernorProfile[pdfrgx_data->g_profile_index].util_th_high && new_index < pdfrgx_data->g_max_freq_index)
	{
		/* Provide recommended burst*/
		pdfrgx_data->gpu_utilization_record_index = new_index+1;
		burst = DFRGX_BURST_REQ;
	}
	else if(util_percentage < aGovernorProfile[pdfrgx_data->g_profile_index].util_th_low && new_index > pdfrgx_data->g_min_freq_index)
	{
		/* Provide recommended unburst*/
		pdfrgx_data->gpu_utilization_record_index = new_index-1;
		burst = DFRGX_UNBURST_REQ;
	}

out:
	return burst;
}
/**
 * tcd_set_cur_state() - thermal cooling
 * device callback set_cur_state.
 * @tcd: Thermal cooling device structure.
 * @cs: Input state.
 *
 * Invoked via interrupt/callback.
 * Function return value: 0 if success, otherwise -error.
 * Execution context: non-atomic
 */
static int tcd_set_cur_state(struct thermal_cooling_device *tcd,
	unsigned long cs)
{
	struct busfreq_data *bfdata;
	struct devfreq *df;
	int ret = 0;

	bfdata = (struct busfreq_data *) tcd->devdata;

	if (cs >= THERMAL_COOLING_DEVICE_MAX_STATE)
		cs = THERMAL_COOLING_DEVICE_MAX_STATE - 1;

	/*If different state*/
	if (bfdata->gbp_cooldv_state_cur != cs) {
		int new_index = -1;

		/* Dynamic turbo is not enabled so try
		* to change the state
		*/
		if (!bfdata->g_dfrgx_data.g_enable) {

			if(!df_rgx_is_active()) {
				return -EBUSY;
			}

			/* If thermal state is specified explicitely
			* then suspend burst/unburst thread
			* because the user needs the GPU to run
			* at specific frequency/thermal state level
			*/

			ret = df_rgx_set_freq_khz(bfdata,
				bfdata->gpudata[cs].freq_limit);
			if (ret <= 0)
				return ret;
		} else {
			/* In this case we want to limit the max_freq
			* to the thermal state limit
			*/
			int b_update_freq = 0;
			df = bfdata->devfreq;

			if (!cs) {
				/* We are back in normal operation so set initial values*/
				df->max_freq = DF_RGX_FREQ_KHZ_MAX;
				df->min_freq = DF_RGX_FREQ_KHZ_MIN;
				b_update_freq = 1;
			}
			else {
				dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 0);
				df->max_freq = bfdata->gpudata[cs].freq_limit;

				if (df->previous_freq > df->max_freq)
					b_update_freq = 1;

				if (bfdata->gpudata[cs].freq_limit < df->min_freq) {
					df->min_freq = bfdata->gpudata[cs].freq_limit;
					new_index = df_rgx_get_util_record_index_by_freq(df->min_freq);

					if (new_index > -1) {
						bfdata->g_dfrgx_data.g_freq_mhz_min = df->min_freq;
						bfdata->g_dfrgx_data.g_min_freq_index = new_index;
					}
					b_update_freq = 1;
				}

				new_index = df_rgx_get_util_record_index_by_freq(df->max_freq);

				if (new_index > -1) {
					bfdata->g_dfrgx_data.g_freq_mhz_max = df->max_freq;
					bfdata->g_dfrgx_data.g_max_freq_index = new_index;
				}

				dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 1);
			}

			if (b_update_freq) {
				/* Pick the min freq this time*/
				bfdata->bf_desired_freq = df->min_freq;
				mutex_lock(&bfdata->lock);
				bfdata->b_need_freq_update = 1;
				mutex_unlock(&bfdata->lock);
			}
		}

		bfdata->gbp_cooldv_state_prev = bfdata->gbp_cooldv_state_cur;
		bfdata->gbp_cooldv_state_cur = cs;

		DFRGX_DPF(DFRGX_DEBUG_HIGH, "Thermal state changed from %d to %d\n",
				bfdata->gbp_cooldv_state_prev,
				bfdata->gbp_cooldv_state_cur);

	}

	return 0;
}
/**
 * df_rgx_bus_target - Request setting of a new frequency.
 * @*p_freq: Input: desired frequency in KHz, output: realized freq in KHz.
 * @flags: DEVFREQ_FLAG_* - not used by this implementation.
 */
static int df_rgx_bus_target(struct device *dev, unsigned long *p_freq,
			      u32 flags)
{
	struct platform_device	*pdev;
	struct busfreq_data	*bfdata;
	struct df_rgx_data_s	*pdfrgx_data;
	struct devfreq		*df;
	unsigned long desired_freq = 0;
	int ret = 0;
	int adjust_curfreq = 0;
	int set_freq = 0;
	(void) flags;

	pdev = container_of(dev, struct platform_device, dev);
	bfdata = platform_get_drvdata(pdev);

	if (bfdata && bfdata->devfreq) {
		int gpu_defer_req = 0;
		df = bfdata->devfreq;
		pdfrgx_data = &bfdata->g_dfrgx_data;
		if (!pdfrgx_data || !pdfrgx_data->g_initialized)
			goto out;

		desired_freq = *p_freq;

		/* Governor changed, will be updated after updatedevfreq() */
		if (strncmp(df->governor->name,
			bfdata->prev_governor, DEVFREQ_NAME_LEN)) {
			DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: Governor changed,"
				" prev : %s, current : %s!\n",
				__func__,
				bfdata->prev_governor,
				df->governor->name);

			if (dfrgx_burst_is_enabled(pdfrgx_data))
				dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 0);

			df_rgx_set_governor_profile(df->governor->name, pdfrgx_data);
			strncpy(bfdata->prev_governor, df->governor->name, DEVFREQ_NAME_LEN);

			DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s: Governors should be "
					"the same now, prev : %s, current : %s!\n",
					__func__,
					bfdata->prev_governor,
					df->governor->name);

			set_freq = 1;
		} else if (df->min_freq != pdfrgx_data->g_freq_mhz_min) {
			int new_index = -1;

			if (dfrgx_burst_is_enabled(pdfrgx_data))
				dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 0);

			new_index = df_rgx_get_util_record_index_by_freq(df->min_freq);
			if (new_index > -1) {
				mutex_lock(&pdfrgx_data->g_mutex_sts);
				pdfrgx_data->g_freq_mhz_min = df->min_freq;
				bfdata->gbp_cooldv_latest_freq_min = df->min_freq;
				pdfrgx_data->g_min_freq_index = new_index;
				mutex_unlock(&pdfrgx_data->g_mutex_sts);
			}

			DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s:Min freq changed!,"
					" prev_freq %lu, min_freq %lu\n",
					__func__,
					df->previous_freq,
					df->min_freq);

			if (df->previous_freq < df->min_freq) {
				desired_freq = df->min_freq;
				adjust_curfreq = 1;
			}
		} else if (df->max_freq != pdfrgx_data->g_freq_mhz_max) {
			int new_index = -1;

			if (dfrgx_burst_is_enabled(pdfrgx_data))
				dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 0);

			new_index = df_rgx_get_util_record_index_by_freq(df->max_freq);
			if (new_index > -1) {
				mutex_lock(&pdfrgx_data->g_mutex_sts);
				pdfrgx_data->g_freq_mhz_max = df->max_freq;
				bfdata->gbp_cooldv_latest_freq_max = df->max_freq;
				pdfrgx_data->g_max_freq_index = new_index;
				mutex_unlock(&pdfrgx_data->g_mutex_sts);
			}

			DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s:Max freq changed!,"
				" prev_freq %lu, max_freq %lu\n",
				__func__,
				df->previous_freq,
				df->max_freq);

			if (df->previous_freq > df->max_freq) {
				desired_freq = df->max_freq;
				adjust_curfreq  = 1;
			}
		} else if (!strncmp(df->governor->name,
				"simple_ondemand", DEVFREQ_NAME_LEN)) {
			*p_freq = df->previous_freq;
			goto out;
		}

		/* set_freq changed on userspace governor*/
		if (!strncmp(df->governor->name, "userspace", DEVFREQ_NAME_LEN)) {
			/* update userspace freq*/
			struct userspace_gov_data *data = df->data;

			DFRGX_DPF(DFRGX_DEBUG_HIGH, "%s:userspace governor,"
				" desired %lu, data->user_frequency %lu, input_freq = %lu\n",
				__func__,
				desired_freq,
				data->user_frequency,
				*p_freq);

			data->valid = 1;
			data->user_frequency = desired_freq;
			set_freq = 1;
		}

		if (adjust_curfreq)
			set_freq = 1;

		if (set_freq) {
			/* Freq will be reflected once GPU is back on*/
			if (!df_rgx_is_active()) {
				bfdata->bf_desired_freq = desired_freq;
				mutex_lock(&bfdata->lock);
				bfdata->b_need_freq_update = 1;
				mutex_unlock(&bfdata->lock);
				*p_freq = desired_freq;
				gpu_defer_req = 1;
			} else {
				ret = df_rgx_set_freq_khz(bfdata, desired_freq);
				if (ret > 0) {
					*p_freq = ret;
					ret = 0;
				}
			}
		} else {
			*p_freq = df->previous_freq;
		}

		if ((!strncmp(df->governor->name,
				"simple_ondemand", DEVFREQ_NAME_LEN)
			&& !dfrgx_burst_is_enabled(&bfdata->g_dfrgx_data))
			|| gpu_defer_req)
			dfrgx_burst_set_enable(&bfdata->g_dfrgx_data, 1);
	}

out:
	return ret;
}