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; }
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; }
static void compute_pll_divisors(const display_mode ¤t, 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); }
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 {
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)); } }
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); }