int
nva3_pm_clock_get(struct drm_device *dev, u32 id)
{
	u32 src0, src1, ctrl, coef;
	struct pll_lims pll;
	int ret, off;
	int P, N, M;

	ret = get_pll_limits(dev, id, &pll);
	if (ret)
		return ret;

	off = nva3_pm_pll_offset(id);
	if (off < 0)
		return off;

	src0 = nv_rd32(dev, 0x4120 + (off * 4));
	src1 = nv_rd32(dev, 0x4160 + (off * 4));
	ctrl = nv_rd32(dev, pll.reg + 0);
	coef = nv_rd32(dev, pll.reg + 4);
	NV_DEBUG(dev, "PLL %02x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
		      id, src0, src1, ctrl, coef);

	if (ctrl & 0x00000008) {
		u32 div = ((src1 & 0x003c0000) >> 18) + 1;
		return (pll.refclk * 2) / div;
	}
Esempio n. 2
0
void *
nv04_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
		  u32 id, int khz)
{
	struct nv04_pm_state *state;
	int ret;

	state = kzalloc(sizeof(*state), GFP_KERNEL);
	if (!state)
		return ERR_PTR(-ENOMEM);

	ret = get_pll_limits(dev, id, &state->pll);
	if (ret) {
		kfree(state);
		return (ret == -ENOENT) ? NULL : ERR_PTR(ret);
	}

	ret = nouveau_calc_pll_mnp(dev, &state->pll, khz, &state->calc);
	if (!ret) {
		kfree(state);
		return ERR_PTR(-EINVAL);
	}

	return state;
}
static int
calc_pll(struct drm_device *dev, u32 id, int khz, struct nv04_pm_clock *clk)
{
	int ret;

	ret = get_pll_limits(dev, id, &clk->pll);
	if (ret)
		return ret;

	ret = nouveau_calc_pll_mnp(dev, &clk->pll, khz, &clk->calc);
	if (!ret)
		return -EINVAL;

	return 0;
}
Esempio n. 4
0
static void
compute_pll_divisors(const display_mode &current, pll_divisors& divisors,
	bool isLVDS)
{
	float requestedPixelClock = current.timing.pixel_clock / 1000.0f;
	float referenceClock
		= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
	pll_limits limits;
	get_pll_limits(limits);

	TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock);

	if (isLVDS) {
		if ((read32(INTEL_DISPLAY_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
				== LVDS_CLKB_POWER_UP)
			divisors.post2 = LVDS_POST2_RATE_FAST;
		else
			divisors.post2 = LVDS_POST2_RATE_SLOW;
	} else {
		if (current.timing.pixel_clock < limits.min_post2_frequency) {
			// slow DAC timing
			divisors.post2 = limits.min.post2;
			divisors.post2_high = limits.min.post2_high;
		} else {
			// fast DAC timing
			divisors.post2 = limits.max.post2;
			divisors.post2_high = limits.max.post2_high;
		}
	}

	float best = requestedPixelClock;
	pll_divisors bestDivisors;

	bool is_igd = gInfo->shared_info->device_type.InGroup(INTEL_TYPE_IGD);
	for (divisors.m1 = limits.min.m1; divisors.m1 <= limits.max.m1;
			divisors.m1++) {
		for (divisors.m2 = limits.min.m2; divisors.m2 <= limits.max.m2
				&& ((divisors.m2 < divisors.m1) || is_igd); divisors.m2++) {
			for (divisors.n = limits.min.n; divisors.n <= limits.max.n;
					divisors.n++) {
				for (divisors.post1 = limits.min.post1;
						divisors.post1 <= limits.max.post1; divisors.post1++) {
					divisors.m = 5 * divisors.m1 + divisors.m2;
					divisors.post = divisors.post1 * divisors.post2;

					if (!valid_pll_divisors(divisors, limits))
						continue;

					float error = fabs(requestedPixelClock
						- ((referenceClock * divisors.m) / divisors.n)
						/ divisors.post);
					if (error < best) {
						best = error;
						bestDivisors = divisors;

						if (error == 0)
							break;
					}
				}
			}
		}
	}

	divisors = bestDivisors;

	TRACE("%s: found: %g MHz, p = %lu (p1 = %lu, p2 = %lu), n = %lu, m = %lu "
		"(m1 = %lu, m2 = %lu)\n", __func__,
		((referenceClock * divisors.m) / divisors.n) / divisors.post,
		divisors.post, divisors.post1, divisors.post2, divisors.n,
		divisors.m, divisors.m1, divisors.m2);
}
Esempio n. 5
0
void
retrieve_current_mode(display_mode& mode, uint32 pllRegister)
{
	uint32 pll = read32(pllRegister);
	uint32 pllDivisor;
	uint32 hTotalRegister;
	uint32 vTotalRegister;
	uint32 hSyncRegister;
	uint32 vSyncRegister;
	uint32 imageSizeRegister;
	uint32 controlRegister;

	if (pllRegister == INTEL_DISPLAY_A_PLL) {
		pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0
			? INTEL_DISPLAY_A_PLL_DIVISOR_1 : INTEL_DISPLAY_A_PLL_DIVISOR_0);

		hTotalRegister = INTEL_DISPLAY_A_HTOTAL;
		vTotalRegister = INTEL_DISPLAY_A_VTOTAL;
		hSyncRegister = INTEL_DISPLAY_A_HSYNC;
		vSyncRegister = INTEL_DISPLAY_A_VSYNC;
		imageSizeRegister = INTEL_DISPLAY_A_IMAGE_SIZE;
		controlRegister = INTEL_DISPLAY_A_CONTROL;
	} else if (pllRegister == INTEL_DISPLAY_B_PLL) {
		pllDivisor = read32((pll & DISPLAY_PLL_DIVISOR_1) != 0
			? INTEL_DISPLAY_B_PLL_DIVISOR_1 : INTEL_DISPLAY_B_PLL_DIVISOR_0);

		hTotalRegister = INTEL_DISPLAY_B_HTOTAL;
		vTotalRegister = INTEL_DISPLAY_B_VTOTAL;
		hSyncRegister = INTEL_DISPLAY_B_HSYNC;
		vSyncRegister = INTEL_DISPLAY_B_VSYNC;
		imageSizeRegister = INTEL_DISPLAY_B_IMAGE_SIZE;
		controlRegister = INTEL_DISPLAY_B_CONTROL;
	} else {
		// TODO: not supported
		return;
	}

	pll_divisors divisors;
	divisors.m1 = (pllDivisor & DISPLAY_PLL_M1_DIVISOR_MASK)
		>> DISPLAY_PLL_M1_DIVISOR_SHIFT;
	divisors.m2 = (pllDivisor & DISPLAY_PLL_M2_DIVISOR_MASK)
		>> DISPLAY_PLL_M2_DIVISOR_SHIFT;
	divisors.n = (pllDivisor & DISPLAY_PLL_N_DIVISOR_MASK)
		>> DISPLAY_PLL_N_DIVISOR_SHIFT;

	pll_limits limits;
	get_pll_limits(limits);

	if (gInfo->shared_info->device_type.InFamily(INTEL_TYPE_9xx)) {
		divisors.post1 = (pll & DISPLAY_PLL_9xx_POST1_DIVISOR_MASK)
			>> DISPLAY_PLL_POST1_DIVISOR_SHIFT;

		if (pllRegister == INTEL_DISPLAY_B_PLL
			&& !gInfo->shared_info->device_type.InGroup(INTEL_TYPE_96x)) {
			// TODO: Fix this? Need to support dual channel LVDS.
			divisors.post2 = LVDS_POST2_RATE_SLOW;
		} else {
			if ((pll & DISPLAY_PLL_DIVIDE_HIGH) != 0)
				divisors.post2 = limits.max.post2;
			else
				divisors.post2 = limits.min.post2;
		}
	} else {
Esempio n. 6
0
static void
setPLL_double_lowregs(struct drm_device *dev, uint32_t NMNMreg,
		      struct nouveau_pll_vals *pv)
{
	/* When setting PLLs, there is a merry game of disabling and enabling
	 * various bits of hardware during the process. This function is a
	 * synthesis of six nv4x traces, nearly each card doing a subtly
	 * different thing. With luck all the necessary bits for each card are
	 * combined herein. Without luck it deviates from each card's formula
	 * so as to not work on any :)
	 */

	uint32_t Preg = NMNMreg - 4;
	bool mpll = Preg == 0x4020;
	uint32_t oldPval = nvReadMC(dev, Preg);
	uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
	uint32_t Pval = (oldPval & (mpll ? ~(0x77 << 16) : ~(7 << 16))) |
			0xc << 28 | pv->log2P << 16;
	uint32_t saved4600 = 0;
	/* some cards have different maskc040s */
	uint32_t maskc040 = ~(3 << 14), savedc040;
	bool single_stage = !pv->NM2 || pv->N2 == pv->M2;

	if (nvReadMC(dev, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval)
		return;

	if (Preg == 0x4000)
		maskc040 = ~0x333;
	if (Preg == 0x4058)
		maskc040 = ~(0xc << 24);

	if (mpll) {
		struct pll_lims pll_lim;
		uint8_t Pval2;

		if (get_pll_limits(dev, Preg, &pll_lim))
			return;

		Pval2 = pv->log2P + pll_lim.log2p_bias;
		if (Pval2 > pll_lim.max_log2p)
			Pval2 = pll_lim.max_log2p;
		Pval |= 1 << 28 | Pval2 << 20;

		saved4600 = nvReadMC(dev, 0x4600);
		nvWriteMC(dev, 0x4600, saved4600 | 8 << 28);
	}
	if (single_stage)
		Pval |= mpll ? 1 << 12 : 1 << 8;

	nvWriteMC(dev, Preg, oldPval | 1 << 28);
	nvWriteMC(dev, Preg, Pval & ~(4 << 28));
	if (mpll) {
		Pval |= 8 << 20;
		nvWriteMC(dev, 0x4020, Pval & ~(0xc << 28));
		nvWriteMC(dev, 0x4038, Pval & ~(0xc << 28));
	}

	savedc040 = nvReadMC(dev, 0xc040);
	nvWriteMC(dev, 0xc040, savedc040 & maskc040);

	nvWriteMC(dev, NMNMreg, NMNM);
	if (NMNMreg == 0x4024)
		nvWriteMC(dev, 0x403c, NMNM);

	nvWriteMC(dev, Preg, Pval);
	if (mpll) {
		Pval &= ~(8 << 20);
		nvWriteMC(dev, 0x4020, Pval);
		nvWriteMC(dev, 0x4038, Pval);
		nvWriteMC(dev, 0x4600, saved4600);
	}

	nvWriteMC(dev, 0xc040, savedc040);

	if (mpll) {
		nvWriteMC(dev, 0x4020, Pval & ~(1 << 28));
		nvWriteMC(dev, 0x4038, Pval & ~(1 << 28));
	}
}
Esempio n. 7
0
void
compute_pll_divisors(display_mode* current, pll_divisors* divisors,
	bool isLVDS)
{
	float requestedPixelClock = current->timing.pixel_clock / 1000.0f;
	float referenceClock
		= gInfo->shared_info->pll_info.reference_frequency / 1000.0f;
	pll_limits limits;
	get_pll_limits(&limits, isLVDS);

	TRACE("%s: required MHz: %g\n", __func__, requestedPixelClock);

	// Calculate p2
	if (isLVDS) {
		if (requestedPixelClock > 112.999
			|| (read32(INTEL_DIGITAL_LVDS_PORT) & LVDS_CLKB_POWER_MASK)
				== LVDS_CLKB_POWER_UP) {
			// fast DAC timing via 2 channels
			divisors->post2 = limits.max.post2;
			divisors->post2_high = limits.max.post2_high;
		} else {
			// slow DAC timing
			divisors->post2 = limits.min.post2;
			divisors->post2_high = limits.min.post2_high;
		}
	} else {
		if (current->timing.pixel_clock < limits.min_post2_frequency) {
			// slow DAC timing
			divisors->post2 = limits.min.post2;
			divisors->post2_high = limits.min.post2_high;
		} else {
			// fast DAC timing
			divisors->post2 = limits.max.post2;
			divisors->post2_high = limits.max.post2_high;
		}
	}

	float best = requestedPixelClock;
	pll_divisors bestDivisors;

	bool is_pine = gInfo->shared_info->device_type.InGroup(INTEL_GROUP_PIN);
	for (divisors->m1 = limits.min.m1; divisors->m1 <= limits.max.m1;
			divisors->m1++) {
		for (divisors->m2 = limits.min.m2; divisors->m2 <= limits.max.m2
				&& ((divisors->m2 < divisors->m1) || is_pine); divisors->m2++) {
			for (divisors->n = limits.min.n; divisors->n <= limits.max.n;
					divisors->n++) {
				for (divisors->post1 = limits.min.post1;
						divisors->post1 <= limits.max.post1; divisors->post1++) {
					divisors->m = compute_pll_m(divisors);
					divisors->post = compute_pll_p(divisors);

					if (!valid_pll_divisors(divisors, &limits))
						continue;

					float error = fabs(requestedPixelClock
						- ((referenceClock * divisors->m) / divisors->n)
						/ divisors->post);
					if (error < best) {
						best = error;
						bestDivisors = *divisors;

						if (error == 0)
							break;
					}
				}
			}
		}
	}

	*divisors = bestDivisors;

	TRACE("%s: found: %g MHz, p = %" B_PRId32 " (p1 = %" B_PRId32 ", "
		"p2 = %" B_PRId32 "), n = %" B_PRId32 ", m = %" B_PRId32 " "
		"(m1 = %" B_PRId32 ", m2 = %" B_PRId32 ")\n", __func__,
		((referenceClock * divisors->m) / divisors->n) / divisors->post,
		divisors->post, divisors->post1, divisors->post2, divisors->n,
		divisors->m, divisors->m1, divisors->m2);
}