Intel-GFX Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Ander Conselvan De Oliveira <conselvan2@gmail.com>
To: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
	intel-gfx@lists.freedesktop.org
Subject: Re: [PATCH 6/8] drm/i915: Don't calculate a new clock in ILK+ code if it is already set
Date: Mon, 14 Mar 2016 15:01:03 +0200	[thread overview]
Message-ID: <1457960463.2711.14.camel@gmail.com> (raw)
In-Reply-To: <56E6A5CA.7000506@linux.intel.com>

[-- Attachment #1: Type: text/plain, Size: 1020 bytes --]

On Mon, 2016-03-14 at 12:51 +0100, Maarten Lankhorst wrote:
> Op 14-03-16 om 09:55 schreef Ander Conselvan de Oliveira:
> > Remove the clock calculation from ironlake_crtc_compute_clock() when the
> > encoder compute_config() already set one. The value was just thrown away
> > in that case.
> > 
> > Signed-off-by: Ander Conselvan de Oliveira <
> > ander.conselvan.de.oliveira@intel.com>
> > 
> It was thrown away, but it could still reject based on the limits, which this
> patch changes.
> This might be made more clear in the commit message.

Good point. To be honest, I didn't very this as carefully as I should have
before sending and missed that detail. It turns out that change is safe. To
verify I extracted the relevant code and run it with all possible port clocks we
could have with either the sdvo or the dp encoder setting the clock. See the
attached C file. I was too lazy to actually understand what the
g4x_find_best_dpll() does.

Anyway, I'll send another version with a note about this.

Thanks,
Ander

[-- Attachment #2: ilk-pll.c --]
[-- Type: text/x-csrc, Size: 11354 bytes --]

/*
 * Copyright © 2006-2007 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *	Eric Anholt <eric@anholt.net>
 */

#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

typedef struct dpll {
	/* given values */
	int n;
	int m1, m2;
	int p1, p2;
	/* derived values */
	int	dot;
	int	vco;
	int	m;
	int	p;
} intel_clock_t;

typedef struct {
	int	min, max;
} intel_range_t;

typedef struct {
	int	dot_limit;
	int	p2_slow, p2_fast;
} intel_p2_t;

typedef struct intel_limit intel_limit_t;
struct intel_limit {
	intel_range_t   dot, vco, n, m, m1, m2, p, p1;
	intel_p2_t	    p2;
};

/* Ironlake / Sandybridge
 *
 * We calculate clock using (register_value + 2) for N/M1/M2, so here
 * the range value for them is (actual_value - 2).
 */
static const intel_limit_t intel_limits_ironlake_dac = {
	.dot = { .min = 25000, .max = 350000 },
	.vco = { .min = 1760000, .max = 3510000 },
	.n = { .min = 1, .max = 5 },
	.m = { .min = 79, .max = 127 },
	.m1 = { .min = 12, .max = 22 },
	.m2 = { .min = 5, .max = 9 },
	.p = { .min = 5, .max = 80 },
	.p1 = { .min = 1, .max = 8 },
	.p2 = { .dot_limit = 225000,
		.p2_slow = 10, .p2_fast = 5 },
};

static const intel_limit_t intel_limits_ironlake_single_lvds = {
	.dot = { .min = 25000, .max = 350000 },
	.vco = { .min = 1760000, .max = 3510000 },
	.n = { .min = 1, .max = 3 },
	.m = { .min = 79, .max = 118 },
	.m1 = { .min = 12, .max = 22 },
	.m2 = { .min = 5, .max = 9 },
	.p = { .min = 28, .max = 112 },
	.p1 = { .min = 2, .max = 8 },
	.p2 = { .dot_limit = 225000,
		.p2_slow = 14, .p2_fast = 14 },
};

static const intel_limit_t intel_limits_ironlake_dual_lvds = {
	.dot = { .min = 25000, .max = 350000 },
	.vco = { .min = 1760000, .max = 3510000 },
	.n = { .min = 1, .max = 3 },
	.m = { .min = 79, .max = 127 },
	.m1 = { .min = 12, .max = 22 },
	.m2 = { .min = 5, .max = 9 },
	.p = { .min = 14, .max = 56 },
	.p1 = { .min = 2, .max = 8 },
	.p2 = { .dot_limit = 225000,
		.p2_slow = 7, .p2_fast = 7 },
};

/* LVDS 100mhz refclk limits. */
static const intel_limit_t intel_limits_ironlake_single_lvds_100m = {
	.dot = { .min = 25000, .max = 350000 },
	.vco = { .min = 1760000, .max = 3510000 },
	.n = { .min = 1, .max = 2 },
	.m = { .min = 79, .max = 126 },
	.m1 = { .min = 12, .max = 22 },
	.m2 = { .min = 5, .max = 9 },
	.p = { .min = 28, .max = 112 },
	.p1 = { .min = 2, .max = 8 },
	.p2 = { .dot_limit = 225000,
		.p2_slow = 14, .p2_fast = 14 },
};

static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
	.dot = { .min = 25000, .max = 350000 },
	.vco = { .min = 1760000, .max = 3510000 },
	.n = { .min = 1, .max = 3 },
	.m = { .min = 79, .max = 126 },
	.m1 = { .min = 12, .max = 22 },
	.m2 = { .min = 5, .max = 9 },
	.p = { .min = 14, .max = 42 },
	.p1 = { .min = 2, .max = 6 },
	.p2 = { .dot_limit = 225000,
		.p2_slow = 7, .p2_fast = 7 },
};

/* stubs */

#define WARN_ON(x)	(x)

#define INTEL_OUTPUT_LVDS 1

struct drm_device {
	void *dev_private;
};

struct drm_crtc {
	struct drm_device *dev;
};

struct intel_crtc_state {
	struct {
		struct drm_crtc *crtc;
	} base;

	int port_clock;
	struct dpll dpll;
	bool clock_set;
};

static bool
intel_pipe_will_have_type(const void *a, int b)
{
	return false;
}

static bool
intel_is_dual_link_lvds(const void *a)
{
	return false;
}

static bool
intel_panel_use_ssc(const void *b)
{
	return false;
}

/* --------------------- */

/*
 * Divide positive or negative dividend by positive divisor and round
 * to closest integer. Result is undefined for negative divisors and
 * for negative dividends if the divisor variable type is unsigned.
 */
#define DIV_ROUND_CLOSEST(x, divisor)(			\
{							\
	typeof(x) __x = x;				\
	typeof(divisor) __d = divisor;			\
	(((typeof(x))-1) > 0 ||				\
	 ((typeof(divisor))-1) > 0 || (__x) > 0) ?	\
		(((__x) + ((__d) / 2)) / (__d)) :	\
		(((__x) - ((__d) / 2)) / (__d));	\
}							\
)

#define INTELPllInvalid(s)   do { /* DRM_DEBUG(s); */ return false; } while (0)
/**
 * Returns whether the given set of divisors are valid for a given refclk with
 * the given connectors.
 */

static bool intel_PLL_is_valid(struct drm_device *dev,
			       const intel_limit_t *limit,
			       const intel_clock_t *clock)
{
	if (clock->n   < limit->n.min   || limit->n.max   < clock->n)
		INTELPllInvalid("n out of range\n");
	if (clock->p1  < limit->p1.min  || limit->p1.max  < clock->p1)
		INTELPllInvalid("p1 out of range\n");
	if (clock->m2  < limit->m2.min  || limit->m2.max  < clock->m2)
		INTELPllInvalid("m2 out of range\n");
	if (clock->m1  < limit->m1.min  || limit->m1.max  < clock->m1)
		INTELPllInvalid("m1 out of range\n");

		if (clock->m1 <= clock->m2)
			INTELPllInvalid("m1 <= m2\n");

		if (clock->p < limit->p.min || limit->p.max < clock->p)
			INTELPllInvalid("p out of range\n");
		if (clock->m < limit->m.min || limit->m.max < clock->m)
			INTELPllInvalid("m out of range\n");

	if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
		INTELPllInvalid("vco out of range\n");
	/* XXX: We may need to be checking "Dot clock" depending on the multiplier,
	 * connector, etc., rather than just a single range.
	 */
	if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
		INTELPllInvalid("dot out of range\n");

	return true;
}

static uint32_t i9xx_dpll_compute_m(struct dpll *dpll)
{
	return 5 * (dpll->m1 + 2) + (dpll->m2 + 2);
}

static int i9xx_calc_dpll_params(int refclk, intel_clock_t *clock)
{
	clock->m = i9xx_dpll_compute_m(clock);
	clock->p = clock->p1 * clock->p2;
	if (WARN_ON(clock->n + 2 == 0 || clock->p == 0))
		return 0;
	clock->vco = DIV_ROUND_CLOSEST(refclk * clock->m, clock->n + 2);
	clock->dot = DIV_ROUND_CLOSEST(clock->vco, clock->p);

	return clock->dot;
}

static int
i9xx_select_p2_div(const intel_limit_t *limit,
		   const struct intel_crtc_state *crtc_state,
		   int target)
{
	struct drm_device *dev = crtc_state->base.crtc->dev;

	if (intel_pipe_will_have_type(crtc_state, INTEL_OUTPUT_LVDS)) {
		/*
		 * For LVDS just rely on its current settings for dual-channel.
		 * We haven't figured out how to reliably set up different
		 * single/dual channel state, if we even can.
		 */
		if (intel_is_dual_link_lvds(dev))
			return limit->p2.p2_fast;
		else
			return limit->p2.p2_slow;
	} else {
		if (target < limit->p2.dot_limit)
			return limit->p2.p2_slow;
		else
			return limit->p2.p2_fast;
	}
}

static bool
g4x_find_best_dpll(const intel_limit_t *limit,
		   struct intel_crtc_state *crtc_state,
		   int target, int refclk, intel_clock_t *match_clock,
		   intel_clock_t *best_clock)
{
	struct drm_device *dev = crtc_state->base.crtc->dev;
	intel_clock_t clock;
	int max_n;
	bool found = false;
	/* approximately equals target * 0.00585 */
	int err_most = (target >> 8) + (target >> 9);

	memset(best_clock, 0, sizeof(*best_clock));

	clock.p2 = i9xx_select_p2_div(limit, crtc_state, target);

	max_n = limit->n.max;
	/* based on hardware requirement, prefer smaller n to precision */
	for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
		/* based on hardware requirement, prefere larger m1,m2 */
		for (clock.m1 = limit->m1.max;
		     clock.m1 >= limit->m1.min; clock.m1--) {
			for (clock.m2 = limit->m2.max;
			     clock.m2 >= limit->m2.min; clock.m2--) {
				for (clock.p1 = limit->p1.max;
				     clock.p1 >= limit->p1.min; clock.p1--) {
					int this_err;

					i9xx_calc_dpll_params(refclk, &clock);
					if (!intel_PLL_is_valid(dev, limit,
								&clock))
						continue;

					this_err = abs(clock.dot - target);
					if (this_err < err_most) {
						*best_clock = clock;
						err_most = this_err;
						max_n = clock.n;
						found = true;
					}
				}
			}
		}
	}
	return found;
}


static bool ironlake_compute_clocks(struct drm_crtc *crtc,
				    struct intel_crtc_state *crtc_state,
				    intel_clock_t *clock,
				    bool *has_reduced_clock,
				    intel_clock_t *reduced_clock)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	int refclk;
	const intel_limit_t *limit;
	bool ret;

	refclk = 120000;

	if (intel_pipe_will_have_type(crtc_state, INTEL_OUTPUT_LVDS)) {
		if (intel_is_dual_link_lvds(dev)) {
			if (refclk == 100000)
				limit = &intel_limits_ironlake_dual_lvds_100m;
			else
				limit = &intel_limits_ironlake_dual_lvds;
		} else {
			if (refclk == 100000)
				limit = &intel_limits_ironlake_single_lvds_100m;
			else
				limit = &intel_limits_ironlake_single_lvds;
		}
	} else {
		limit = &intel_limits_ironlake_dac;
	}

	/*
	 * Returns a set of divisors for the desired target clock with the given
	 * refclk, or FALSE.  The returned values represent the clock equation:
	 * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
	 */
	ret = g4x_find_best_dpll(limit, crtc_state, crtc_state->port_clock,
				 refclk, NULL, clock);
	if (!ret)
		return false;

	return true;
}

static void i9xx_adjust_sdvo_tv_clock(struct intel_crtc_state *pipe_config)
{
	unsigned dotclock = pipe_config->port_clock;
	struct dpll *clock = &pipe_config->dpll;

	/* SDVO TV has fixed PLL values depend on its clock range,
	   this mirrors vbios setting. */
	if (dotclock >= 100000 && dotclock < 140500) {
		clock->p1 = 2;
		clock->p2 = 10;
		clock->n = 3;
		clock->m1 = 16;
		clock->m2 = 8;
	} else if (dotclock >= 140500 && dotclock <= 200000) {
		clock->p1 = 1;
		clock->p2 = 10;
		clock->n = 6;
		clock->m1 = 12;
		clock->m2 = 8;
	} else {
		fprintf(stderr, "SDVO TV clock out of range: %i\n", dotclock);
	}

	pipe_config->clock_set = true;
}

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

int
main()
{
	struct intel_crtc_state crtc_state;
	struct drm_crtc crtc;

	int test_clocks[] = { 100000, 140500, 200000, 162000, 270000};
	intel_clock_t clock;

	crtc_state.base.crtc = &crtc;

	for (int i = 0; i < ARRAY_SIZE(test_clocks); i++) {
		bool ok;

		crtc_state.port_clock = test_clocks[i];
		ok = ironlake_compute_clocks(&crtc, &crtc_state, &clock,
					     NULL, NULL);

		printf("clock %d: %s\n",
		       test_clocks[i], ok ? "passed" : "failed");
	}

	for (int i = 100000; i <= 270000; i++) {
		bool ok;

		crtc_state.port_clock = i;
		ok = ironlake_compute_clocks(&crtc, &crtc_state, &clock,
					     NULL, NULL);
		if (!ok)
			printf("clock %d: %s\n",
			       i, ok ? "passed" : "failed");
	}

	return 0;
}



[-- Attachment #3: Type: text/plain, Size: 160 bytes --]

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

  reply	other threads:[~2016-03-14 13:01 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-03-14  8:55 [PATCH 0/8] Clean up ironlake clock computation code Ander Conselvan de Oliveira
2016-03-14  8:55 ` [PATCH 1/8] drm/i915: Remove checks for clone config with LVDS in ILK+ dpll code Ander Conselvan de Oliveira
2016-03-14 13:51   ` Ville Syrjälä
2016-03-14 13:55     ` Conselvan De Oliveira, Ander
2016-03-14 14:02       ` Ville Syrjälä
2016-03-14  8:55 ` [PATCH 2/8] drm/i915: Merge ironlake_get_refclk() into its only caller Ander Conselvan de Oliveira
2016-03-14 13:55   ` Ville Syrjälä
2016-03-14 14:02     ` Conselvan De Oliveira, Ander
2016-03-14  8:55 ` [PATCH 3/8] drm/i915: Fold intel_ironlake_limit() into clock computation function Ander Conselvan de Oliveira
2016-03-14 11:46   ` Maarten Lankhorst
2016-03-14 11:53     ` Ander Conselvan De Oliveira
2016-03-14 11:59       ` Maarten Lankhorst
2016-03-14  8:55 ` [PATCH 4/8] drm/i915: Call g4x_find_best_dpll() directly from ILK+ code Ander Conselvan de Oliveira
2016-03-14  8:55 ` [PATCH 5/8] drm/i915: Simplify ironlake reduced clock logic a bit Ander Conselvan de Oliveira
2016-03-14  8:55 ` [PATCH 6/8] drm/i915: Don't calculate a new clock in ILK+ code if it is already set Ander Conselvan de Oliveira
2016-03-14 11:51   ` Maarten Lankhorst
2016-03-14 13:01     ` Ander Conselvan De Oliveira [this message]
2016-03-14 13:15       ` Maarten Lankhorst
2016-03-14  8:55 ` [PATCH 7/8] drm/i915: Remove PCH type checks from ironlake_crtc_compute_clock() Ander Conselvan de Oliveira
2016-03-14  8:55 ` [PATCH 8/8] drm/i915: Simplify ironlake_crtc_compute_clock() CPU eDP case Ander Conselvan de Oliveira
2016-03-14 12:01   ` Maarten Lankhorst
  -- strict thread matches above, loose matches on Subject: below --
2016-03-11 14:52 [PATCH 0/8] Clean up ironlake clock computation code Ander Conselvan de Oliveira
2016-03-11 14:52 ` [PATCH 6/8] drm/i915: Don't calculate a new clock in ILK+ code if it is already set Ander Conselvan de Oliveira

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1457960463.2711.14.camel@gmail.com \
    --to=conselvan2@gmail.com \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=maarten.lankhorst@linux.intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox