drivers/gpu/drm/i915/display/intel_pch_refclk.c

Source file repositories/reference/linux-study-clean/drivers/gpu/drm/i915/display/intel_pch_refclk.c

File Facts

System
Linux kernel
Corpus path
drivers/gpu/drm/i915/display/intel_pch_refclk.c
Extension
.c
Size
18126 bytes
Lines
684
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct iclkip_params {
	u32 iclk_virtual_root_freq;
	u32 iclk_pi_range;
	u32 divsel, phaseinc, auxdiv, phasedir, desired_divisor;
};

static void iclkip_params_init(struct iclkip_params *p)
{
	memset(p, 0, sizeof(*p));

	p->iclk_virtual_root_freq = 172800 * 1000;
	p->iclk_pi_range = 64;
}

static int lpt_iclkip_freq(struct iclkip_params *p)
{
	return DIV_ROUND_CLOSEST(p->iclk_virtual_root_freq,
				 p->desired_divisor << p->auxdiv);
}

static void lpt_compute_iclkip(struct iclkip_params *p, int clock)
{
	iclkip_params_init(p);

	/* The iCLK virtual clock root frequency is in MHz,
	 * but the adjusted_mode->crtc_clock in KHz. To get the
	 * divisors, it is necessary to divide one by another, so we
	 * convert the virtual clock precision to KHz here for higher
	 * precision.
	 */
	for (p->auxdiv = 0; p->auxdiv < 2; p->auxdiv++) {
		p->desired_divisor = DIV_ROUND_CLOSEST(p->iclk_virtual_root_freq,
						       clock << p->auxdiv);
		p->divsel = (p->desired_divisor / p->iclk_pi_range) - 2;
		p->phaseinc = p->desired_divisor % p->iclk_pi_range;

		/*
		 * Near 20MHz is a corner case which is
		 * out of range for the 7-bit divisor
		 */
		if (p->divsel <= 0x7f)
			break;
	}
}

int lpt_iclkip(const struct intel_crtc_state *crtc_state)
{
	struct iclkip_params p;

	lpt_compute_iclkip(&p, crtc_state->hw.adjusted_mode.crtc_clock);

	return lpt_iclkip_freq(&p);
}

/* Program iCLKIP clock to the desired frequency */
void lpt_program_iclkip(const struct intel_crtc_state *crtc_state)
{
	struct intel_display *display = to_intel_display(crtc_state);
	int clock = crtc_state->hw.adjusted_mode.crtc_clock;
	struct iclkip_params p;
	u32 temp;

	lpt_disable_iclkip(display);

	lpt_compute_iclkip(&p, clock);
	drm_WARN_ON(display->drm, lpt_iclkip_freq(&p) != clock);

	/* This should not happen with any sane values */
	drm_WARN_ON(display->drm, SBI_SSCDIVINTPHASE_DIVSEL(p.divsel) &
		    ~SBI_SSCDIVINTPHASE_DIVSEL_MASK);
	drm_WARN_ON(display->drm, SBI_SSCDIVINTPHASE_DIR(p.phasedir) &
		    ~SBI_SSCDIVINTPHASE_INCVAL_MASK);

	drm_dbg_kms(display->drm,
		    "iCLKIP clock: found settings for %dKHz refresh rate: auxdiv=%x, divsel=%x, phasedir=%x, phaseinc=%x\n",
		    clock, p.auxdiv, p.divsel, p.phasedir, p.phaseinc);

	intel_sbi_lock(display);

	/* Program SSCDIVINTPHASE6 */
	temp = intel_sbi_read(display, SBI_SSCDIVINTPHASE6, SBI_ICLK);
	temp &= ~SBI_SSCDIVINTPHASE_DIVSEL_MASK;
	temp |= SBI_SSCDIVINTPHASE_DIVSEL(p.divsel);
	temp &= ~SBI_SSCDIVINTPHASE_INCVAL_MASK;
	temp |= SBI_SSCDIVINTPHASE_INCVAL(p.phaseinc);
	temp |= SBI_SSCDIVINTPHASE_DIR(p.phasedir);
	temp |= SBI_SSCDIVINTPHASE_PROPAGATE;
	intel_sbi_write(display, SBI_SSCDIVINTPHASE6, temp, SBI_ICLK);

	/* Program SSCAUXDIV */

Annotation

Implementation Notes