void nvhost_scale_emc_calibrate_emc(struct nvhost_emc_params *emc_params,
				  struct clk *clk_3d, struct clk *clk_3d_emc,
				  bool linear_emc)
{
	long correction;
	unsigned long max_emc;
	unsigned long min_emc;
	unsigned long min_rate_3d;
	unsigned long max_rate_3d;

	max_emc = clk_round_rate(clk_3d_emc, UINT_MAX);
	max_emc = INT_TO_FX(HZ_TO_MHZ(max_emc));

	min_emc = clk_round_rate(clk_3d_emc, 0);
	min_emc = INT_TO_FX(HZ_TO_MHZ(min_emc));

	max_rate_3d = clk_round_rate(clk_3d, UINT_MAX);
	max_rate_3d = INT_TO_FX(HZ_TO_MHZ(max_rate_3d));

	min_rate_3d = clk_round_rate(clk_3d, 0);
	min_rate_3d = INT_TO_FX(HZ_TO_MHZ(min_rate_3d));

	emc_params->emc_slope =
		FXDIV((max_emc - min_emc), (max_rate_3d - min_rate_3d));
	emc_params->emc_offset = max_emc -
		FXMUL(emc_params->emc_slope, max_rate_3d);
	/* Guarantee max 3d rate maps to max emc rate */
	emc_params->emc_offset += max_emc -
		(FXMUL(emc_params->emc_slope, max_rate_3d) +
		emc_params->emc_offset);

	emc_params->linear = linear_emc;
	if (linear_emc)
		return;

	emc_params->emc_dip_offset = (max_emc - min_emc) / 4;
	emc_params->emc_dip_slope =
		-FXDIV(emc_params->emc_slope, max_rate_3d - min_rate_3d);
	emc_params->emc_xmid = (max_rate_3d + min_rate_3d) / 2;
	correction =
		emc_params->emc_dip_offset +
			FXMUL(emc_params->emc_dip_slope,
			FXMUL(max_rate_3d - emc_params->emc_xmid,
				max_rate_3d - emc_params->emc_xmid));
	emc_params->emc_dip_offset -= correction;
}
static void nvhost_scale3d_calibrate_emc(void)
{
	long correction;
	unsigned long max_emc;
	unsigned long min_emc;
	unsigned long min_rate_3d;
	unsigned long max_rate_3d;

	max_emc = clk_round_rate(power_profile.clk_3d_emc, UINT_MAX);
	max_emc = INT_TO_FX(HZ_TO_MHZ(max_emc));

	min_emc = clk_round_rate(power_profile.clk_3d_emc, 0);
	min_emc = INT_TO_FX(HZ_TO_MHZ(min_emc));

	max_rate_3d = INT_TO_FX(HZ_TO_MHZ(power_profile.max_rate_3d));
	min_rate_3d = INT_TO_FX(HZ_TO_MHZ(power_profile.min_rate_3d));

	power_profile.emc_slope =
		FXDIV((max_emc - min_emc), (max_rate_3d - min_rate_3d));
	power_profile.emc_offset = max_emc -
		FXMUL(power_profile.emc_slope, max_rate_3d);
	/* Guarantee max 3d rate maps to max emc rate */
	power_profile.emc_offset += max_emc -
		(FXMUL(power_profile.emc_slope, max_rate_3d) +
		power_profile.emc_offset);

	power_profile.emc_dip_offset = (max_emc - min_emc) / 4;
	power_profile.emc_dip_slope =
		-4 * FXDIV(power_profile.emc_dip_offset,
		(FXMUL(max_rate_3d - min_rate_3d,
			max_rate_3d - min_rate_3d)));
	power_profile.emc_xmid = (max_rate_3d + min_rate_3d) / 2;
	correction =
		power_profile.emc_dip_offset +
			FXMUL(power_profile.emc_dip_slope,
			FXMUL(max_rate_3d - power_profile.emc_xmid,
				max_rate_3d - power_profile.emc_xmid));
	power_profile.emc_dip_offset -= correction;
}
static int nvhost_scale3d_target(struct device *d, unsigned long *freq,
				u32 flags)
{
	long hz;
	long after;

	/* Inform that the clock is disabled */
	if (!tegra_is_clk_enabled(power_profile.clk_3d)) {
		*freq = 0;
		return 0;
	}

	/* Limit the frequency */
	if (*freq < power_profile.min_rate_3d)
		*freq = power_profile.min_rate_3d;
	else if (*freq > power_profile.max_rate_3d)
		*freq = power_profile.max_rate_3d;

	/* Check if we're already running at the desired speed */
	if (*freq == clk_get_rate(power_profile.clk_3d))
		return 0;

	/* Set GPU clockrate */
	if (tegra_get_chipid() == TEGRA_CHIPID_TEGRA3)
		nvhost_module_set_devfreq_rate(power_profile.dev,
					clk_to_idx(power_profile.clk_3d2), 0);
	nvhost_module_set_devfreq_rate(power_profile.dev,
				clk_to_idx(power_profile.clk_3d), *freq);

	/* Set EMC clockrate */
	after = (long) clk_get_rate(power_profile.clk_3d);
	after = INT_TO_FX(HZ_TO_MHZ(after));
	hz = FXMUL(after, power_profile.emc_slope) +
		power_profile.emc_offset;

	hz -= FXMUL(power_profile.emc_dip_slope,
		FXMUL(after - power_profile.emc_xmid,
			after - power_profile.emc_xmid)) +
		power_profile.emc_dip_offset;

	hz = MHZ_TO_HZ(FX_TO_INT(hz + FX_HALF)); /* round to nearest */
	hz = (hz < 0) ? 0 : hz;

	nvhost_module_set_devfreq_rate(power_profile.dev,
			clk_to_idx(power_profile.clk_3d_emc), hz);

	/* Get the new clockrate */
	*freq = clk_get_rate(power_profile.clk_3d);

	return 0;
}
long nvhost_scale_emc_get_emc_rate(struct nvhost_emc_params *emc_params,
				 long freq)
{
	long hz;

	freq = INT_TO_FX(HZ_TO_MHZ(freq));
	hz = FXMUL(freq, emc_params->emc_slope) + emc_params->emc_offset;

	if (!emc_params->linear)
		hz -= FXMUL(emc_params->emc_dip_slope,
			FXMUL(freq - emc_params->emc_xmid,
				freq - emc_params->emc_xmid)) +
			emc_params->emc_dip_offset;

	hz = MHZ_TO_HZ(FX_TO_INT(hz + FX_HALF)); /* round to nearest */
	hz = (hz < 0) ? 0 : hz;

	return hz;
}