dri-devel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Steve Longerbeam <slongerbeam@gmail.com>
To: dri-devel@lists.freedesktop.org
Subject: [PATCH 31/72] gpu: ipu-di: Set rate of DI pre clock
Date: Fri, 31 Oct 2014 15:54:14 -0700	[thread overview]
Message-ID: <1414796095-10107-32-git-send-email-steve_longerbeam@mentor.com> (raw)
In-Reply-To: <1414796095-10107-1-git-send-email-steve_longerbeam@mentor.com>

This patch sets the rate of the DI pre clock to support a much wider
range of pixel clock frequencies.

It does this by calculating two values for the pre-clk rate: a
rate that is a whole integer multiple of the pixel clock, and a rate
that is a half-integer multiple. It then programs whichever rate
comes closest to generating the desired pixel clock, and uses the
corresponding integer or half-integer divider.

The reason only whole or half integer DI dividers are used is because
of a chip bug in the fractional part of DI Base Sync Clock Gen 0 register
(see discussion in FSL community site at
https://community.freescale.com/thread/308577).

Quoting from that thread:

"Pixel IPU clock for Display has high jitter and does not have 50% duty cycle.
This is expected behavior, when the clock divider is set to a value that is not
an integer. When the divider is set to a non-integer value, the average
frequency of the clock will be correct, but the clock will jitter."

According to our experimentation, only 0x8 for the fractional part seems
to generate clock waveforms that are tolerated by most displays (this
hasn't been proved officially).

Signed-off-by: Steve Longerbeam <steve_longerbeam@mentor.com>
Signed-off-by: Jiada Wang <jiada_wang@mentor.com>
---
 drivers/gpu/ipu-v3/ipu-di.c |  145 +++++++++++++++++++++++++++++++++----------
 1 file changed, 111 insertions(+), 34 deletions(-)

diff --git a/drivers/gpu/ipu-v3/ipu-di.c b/drivers/gpu/ipu-v3/ipu-di.c
index b306f07..8da9c9f 100644
--- a/drivers/gpu/ipu-v3/ipu-di.c
+++ b/drivers/gpu/ipu-v3/ipu-di.c
@@ -398,12 +398,105 @@ static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
 		ipu_di_sync_config(di, cfg_vga, 0, ARRAY_SIZE(cfg_vga));
 }
 
-static void ipu_di_config_clock(struct ipu_di *di,
-	const struct ipu_di_signal_cfg *sig)
+
+/*
+ * We need to use the DI divider. We should really have a flag here
+ * indicating whether the bridge can cope with a fractional divider
+ * or not.
+ *
+ * For now, assume the chip bug exists in the fractional divider
+ * which causes bad DI pixel clock waveforms for all values of the
+ * fractional part other than 0 (no fraction) or 0x8 (0.5). So this
+ * function returns a divider with only 0x0 or 0x8 in the fractional
+ * part.
+ *
+ * This function reprograms the clock rate of the parent-parent of
+ * the DI clock in order to get as close as possible to the requested
+ * pixel clock. It calculates two values for the parent-parent rate: a
+ * rate that is a whole integer multiple of the pixel clock, and a rate
+ * that is a half-integer multiple. It then programs whichever rate
+ * comes closest to generating the desired rate, and returns the
+ * corresponding integer or half-integer divider (times 16).
+ */
+static int set_di_pre_clk_rate(struct ipu_di *di,
+			       const struct ipu_di_signal_cfg *sig)
+{
+	struct clk *di_pre_clk;
+	unsigned long pre_clk_N_round, pre_clk_N_0_5_round;
+	unsigned long pre_clk_N, pre_clk_N_0_5;
+	unsigned long pixclk_N, pixclk_N_0_5;
+	unsigned div_N, div_N_0_5, error_N, error_N_0_5;
+	int ret;
+
+	di_pre_clk = clk_get_parent(clk_get_parent(di->clk_di));
+	if (IS_ERR(di_pre_clk)) {
+		dev_err(di->ipu->dev, "failed to get di_pre clock\n");
+		return PTR_ERR(di_pre_clk);
+	}
+
+	/*
+	 * calc pre_clk_N and pre_clk_N_0_5, which are an integer multiple
+	 * and half-integer multiple of sig->pixelclock, respectively.
+	 */
+	pre_clk_N = sig->pixelclock;
+	pre_clk_N_round = clk_round_rate(di_pre_clk, pre_clk_N);
+	pre_clk_N *= (pre_clk_N_round / pre_clk_N);
+	pre_clk_N_0_5 = pre_clk_N + sig->pixelclock / 2;
+
+	if (pre_clk_N < pre_clk_N_round)
+		pre_clk_N += sig->pixelclock;
+	if (pre_clk_N_0_5 < pre_clk_N_round)
+		pre_clk_N_0_5 += sig->pixelclock;
+
+	/*
+	 * now get the rounded pre_clk_N and pre_clk_N_0_5, i.e. what the
+	 * pre-clk can actually generate.
+	 */
+	pre_clk_N_round = clk_round_rate(di_pre_clk, pre_clk_N);
+	pre_clk_N_0_5_round = clk_round_rate(di_pre_clk, pre_clk_N_0_5);
+
+	/*
+	 * finally we can determine whether the integer multiple or
+	 * half-integer multiple pre-clk comes closest to generating
+	 * the desired pixel clock. Set pre-clk rate to whichever comes
+	 * closest.
+	 */
+	div_N = (pre_clk_N_round << 4) / sig->pixelclock;
+	div_N = (div_N + 0x8) & ~0xf; /* round to nearest int */
+	pixclk_N = (pre_clk_N_round << 4) / div_N;
+
+	div_N_0_5 = (pre_clk_N_0_5_round << 4) / sig->pixelclock;
+	div_N_0_5 = (div_N_0_5 + 0x4) & ~0x7; /* round to nearest half-int */
+	pixclk_N_0_5 = (pre_clk_N_0_5_round << 4) / div_N_0_5;
+
+	error_N = pixclk_N / (sig->pixelclock / 1000);
+	error_N_0_5 = pixclk_N_0_5 / (sig->pixelclock / 1000);
+
+	if (abs(error_N - 1000) < abs(error_N_0_5 - 1000)) {
+		ret = div_N;
+		clk_set_rate(di_pre_clk, pre_clk_N_round);
+	} else {
+		ret = div_N_0_5;
+		clk_set_rate(di_pre_clk, pre_clk_N_0_5_round);
+	}
+
+	dev_dbg(di->ipu->dev,
+		"di%d: pixclk want %lu, can do %lu / %lu, div 0x%02x / 0x%02x, error %d.%u%% / %d.%u%%\n",
+		di->id, sig->pixelclock, pixclk_N, pixclk_N_0_5,
+		div_N, div_N_0_5,
+		(signed)(error_N - 1000) / 10, error_N % 10,
+		(signed)(error_N_0_5 - 1000) / 10, error_N_0_5 % 10);
+
+	return ret;
+}
+
+static int ipu_di_config_clock(struct ipu_di *di,
+			       const struct ipu_di_signal_cfg *sig)
 {
 	struct clk *clk;
 	unsigned clkgen0;
 	uint32_t val;
+	int ret;
 
 	if (sig->clkflags & IPU_DI_CLKMODE_EXT) {
 		/*
@@ -423,24 +516,10 @@ static void ipu_di_config_clock(struct ipu_di *di,
 			 */
 			clkgen0 = 1 << 4;
 		} else {
-			/*
-			 * We can use the divider.  We should really have
-			 * a flag here indicating whether the bridge can
-			 * cope with a fractional divider or not.  For the
-			 * time being, let's go for simplicitly and
-			 * reliability.
-			 */
-			unsigned long in_rate;
-			unsigned div;
-
-			clk_set_rate(clk, sig->pixelclock);
-
-			in_rate = clk_get_rate(clk);
-			div = (in_rate + sig->pixelclock / 2) / sig->pixelclock;
-			if (div == 0)
-				div = 1;
-
-			clkgen0 = div << 4;
+			ret = set_di_pre_clk_rate(di, sig);
+			if (ret < 0)
+				return ret;
+			clkgen0 = ret;
 		}
 	} else {
 		/*
@@ -470,19 +549,12 @@ static void ipu_di_config_clock(struct ipu_di *di,
 
 			clkgen0 = div << 4;
 		} else {
-			unsigned long in_rate;
-			unsigned div;
-
 			clk = di->clk_di;
 
-			clk_set_rate(clk, sig->pixelclock);
-
-			in_rate = clk_get_rate(clk);
-			div = (in_rate + sig->pixelclock / 2) / sig->pixelclock;
-			if (div == 0)
-				div = 1;
-
-			clkgen0 = div << 4;
+			ret = set_di_pre_clk_rate(di, sig);
+			if (ret < 0)
+				return ret;
+			clkgen0 = ret;
 		}
 	}
 
@@ -513,6 +585,8 @@ static void ipu_di_config_clock(struct ipu_di *di,
 		clk_get_rate(di->clk_di),
 		clk == di->clk_di ? "DI" : "IPU",
 		clk_get_rate(di->clk_di_pixel) / (clkgen0 >> 4));
+
+	return 0;
 }
 
 /*
@@ -546,6 +620,7 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 	u32 reg;
 	u32 di_gen, vsync_cnt;
 	u32 div;
+	int ret = 0;
 
 	dev_dbg(di->ipu->dev, "di%d: panel size = %d x %d\n",
 		di->id, sig->width, sig->height);
@@ -564,7 +639,9 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 
 	mutex_lock(&di_mutex);
 
-	ipu_di_config_clock(di, sig);
+	ret = ipu_di_config_clock(di, sig);
+	if (ret)
+		goto unlock;
 
 	div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff;
 	div = div / 16;		/* Now divider is integer portion */
@@ -641,9 +718,9 @@ int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
 
 	ipu_di_write(di, reg, DI_POL);
 
+unlock:
 	mutex_unlock(&di_mutex);
-
-	return 0;
+	return ret;
 }
 EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel);
 
-- 
1.7.9.5

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

  parent reply	other threads:[~2014-10-31 22:57 UTC|newest]

Thread overview: 115+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-10-31 22:53 [PATCH 00/72] staging imx-drm new features and fixes Steve Longerbeam
2014-10-31 22:53 ` [PATCH 01/72] ARM: dts: imx6qdl-sabrelite: Add HDMI device Steve Longerbeam
2014-10-31 22:53 ` [PATCH 02/72] ARM: dts: imx6qdl-sabreauto: " Steve Longerbeam
2014-10-31 22:53 ` [PATCH 03/72] ARM: dts: imx6qdl: Create imx-drm crtc nodes Steve Longerbeam
2014-11-03 12:31   ` Philipp Zabel
2014-11-03 17:09     ` Steve Longerbeam
2014-10-31 22:53 ` [PATCH 04/72] ARM: i.MX6: use pll2_pfd0_352m as clock root of ipu_di Steve Longerbeam
2014-11-03 12:30   ` Philipp Zabel
2014-11-03 19:10     ` Steve Longerbeam
2014-10-31 22:53 ` [PATCH 05/72] ARM: i.MX6: select pll3_usb_otg for ldb_di for rev 1.0 chip Steve Longerbeam
2014-11-03 12:30   ` Philipp Zabel
2014-11-03 19:17     ` Steve Longerbeam
2014-11-03 20:06       ` Fabio Estevam
2014-11-04  2:38         ` Steve Longerbeam
2014-10-31 22:53 ` [PATCH 06/72] gpu: ipu-cpmem: Add ipu_cpmem_set_uv_offset() Steve Longerbeam
2014-11-03 12:30   ` Philipp Zabel
2014-11-03 22:52     ` Steve Longerbeam
2014-10-31 22:53 ` [PATCH 07/72] gpu: ipu-v3: Add ipu_dp_set_chroma_key() Steve Longerbeam
2014-11-03 12:31   ` Philipp Zabel
2014-11-04  1:55     ` Steve Longerbeam
2014-10-31 22:53 ` [PATCH 08/72] gpu: ipu-v3: Add ipu_dp_set_gamma_correction() Steve Longerbeam
2014-10-31 22:53 ` [PATCH 09/72] gpu: ipu-v3: Add support for dynamic DC interface pixel maps Steve Longerbeam
2014-10-31 22:53 ` [PATCH 10/72] gpu: ipu-v3: Add ipu_dc_uninit_sync() Steve Longerbeam
2014-10-31 22:53 ` [PATCH 11/72] gpu: ipu-v3: Pass struct ipu_dc to enable/disable Steve Longerbeam
2014-10-31 22:53 ` [PATCH 12/72] gpu: ipu-v3: Add ipu_dp_uninit_channel() Steve Longerbeam
2014-10-31 22:53 ` [PATCH 13/72] gpu: ipu-v3: Pass struct ipu_dp to enable/disable Steve Longerbeam
2014-10-31 22:53 ` [PATCH 14/72] gpu: ipu-v3: Implement use counter for ipu_dc_enable(), ipu_dc_disable() Steve Longerbeam
2014-10-31 22:53 ` [PATCH 15/72] gpu: ipu-v3: fix HDMI timing issues Steve Longerbeam
2014-10-31 22:53 ` [PATCH 16/72] gpu: ipu-v3: Add DMFC/DP/DC/DI module enable/disable debug Steve Longerbeam
2014-10-31 22:54 ` [PATCH 17/72] gpu: ipu-v3: Add ipu_di_uninit_sync_panel() Steve Longerbeam
2014-10-31 22:54 ` [PATCH 18/72] gpu: ipu-v3: Split out DI clock enable/disable Steve Longerbeam
2014-10-31 22:54 ` [PATCH 19/72] gpu: ipu-v3: Protect more CM reg access with IPU lock Steve Longerbeam
2014-11-04 17:51   ` Philipp Zabel
2014-11-05  1:54     ` Steve Longerbeam
2014-11-05  1:55     ` Steve Longerbeam
2014-10-31 22:54 ` [PATCH 20/72] gpu: ipu-v3: Move DI waveform counter enable/disable to ipu-di Steve Longerbeam
2014-10-31 22:54 ` [PATCH 21/72] gpu: ipu-v3: Update DP sync SRM always in ipu_dp_enable_channel() Steve Longerbeam
2014-10-31 22:54 ` [PATCH 22/72] gpu: ipu-v3: Fix indent/ws in ipu-dmfc Steve Longerbeam
2014-10-31 22:54 ` [PATCH 23/72] gpu: ipu-v3: Allow burstsize of 20 in ipu_dmfc_setup_channel() Steve Longerbeam
2014-10-31 22:54 ` [PATCH 24/72] gpu: ipu-v3: Remove ipu_dmfc_init_channel() Steve Longerbeam
2014-10-31 22:54 ` [PATCH 25/72] gpu: ipu-v3: Consolidate mutex lock in ipu_dmfc_alloc_bandwidth() Steve Longerbeam
2014-10-31 22:54 ` [PATCH 26/72] gpu: ipu-v3: Enumerate the DC channel names Steve Longerbeam
2014-10-31 22:54 ` [PATCH 27/72] gpu: ipu-di: Move ipu pointer init Steve Longerbeam
2014-10-31 22:54 ` [PATCH 28/72] gpu: ipu-di: Add and improve debug/error messages Steve Longerbeam
2014-10-31 22:54 ` [PATCH 29/72] gpu: ipu-v3: Change signal names in struct ipu_di_signal_cfg Steve Longerbeam
2014-10-31 22:54 ` [PATCH 30/72] gpu: ipu-v3: Remove IPU client registration Steve Longerbeam
2014-10-31 22:54 ` Steve Longerbeam [this message]
2014-10-31 22:54 ` [PATCH 32/72] gpu: ipu-v3: Add RGB666 interface pixel map Steve Longerbeam
2014-10-31 22:54 ` [PATCH 33/72] gpu: ipu-cpmem: Pass drm fourcc to ipu_cpmem_set_yuv_* Steve Longerbeam
2014-10-31 22:54 ` [PATCH 34/72] gpu: ipu-v3: Add ipu_drm_fourcc_is_planar() Steve Longerbeam
2014-10-31 22:54 ` [PATCH 35/72] gpu: ipu-v3: Add IDMA channel linking support Steve Longerbeam
2014-10-31 22:54 ` [PATCH 36/72] gpu: ipu-cpmem: Support YVU422 Steve Longerbeam
2014-10-31 22:54 ` [PATCH 37/72] gpu: ipu-cpmem: Add ipu_cpmem_get_burstsize() Steve Longerbeam
2014-10-31 22:54 ` [PATCH 38/72] imx-drm: Crtcs moved to device tree Steve Longerbeam
2014-10-31 22:54 ` [PATCH 39/72] imx-drm: hdmi: optimize i2c write wait Steve Longerbeam
2014-10-31 22:54 ` [PATCH 40/72] imx-drm: parallel-display: Support RGB666 pixel fmt Steve Longerbeam
2014-10-31 22:54 ` [PATCH 41/72] imx-drm: imx-ldb: Add debug to connector/encoder entry points Steve Longerbeam
2014-10-31 22:54 ` [PATCH 42/72] imx-drm: imx-ldb: Implement imx_ldb_encoder_dpms() Steve Longerbeam
2014-10-31 22:54 ` [PATCH 43/72] imx-drm: parallel-display: Fix typo when setting mode type Steve Longerbeam
2014-10-31 22:54 ` [PATCH 44/72] imx-drm: ipuv3-plane: Fix planar formats Steve Longerbeam
2014-10-31 22:54 ` [PATCH 45/72] imx-drm: ipuv3-plane: Allow YUV space for background plane Steve Longerbeam
2014-10-31 22:54 ` [PATCH 46/72] imx-drm: ipuv3-plane: Add more supported pixel formats Steve Longerbeam
2014-10-31 22:54 ` [PATCH 47/72] imx-drm: ipuv3-plane: Implement global alpha and colorkey properties Steve Longerbeam
2014-10-31 22:54 ` [PATCH 48/72] imx-drm: hdmi: rework irq request/free Steve Longerbeam
2014-10-31 22:54 ` [PATCH 49/72] imx-drm: imx-ldb: Add DDC support Steve Longerbeam
2014-11-04 17:52   ` Philipp Zabel
2014-10-31 22:54 ` [PATCH 50/72] imx-drm: Fix separate primary plane objects Steve Longerbeam
2014-10-31 22:54 ` [PATCH 51/72] imx-drm: Move page flip handling to plane driver Steve Longerbeam
2014-10-31 22:54 ` [PATCH 52/72] imx-drm: Reset ipu unit pointers to NULL on errors Steve Longerbeam
2014-10-31 22:54 ` [PATCH 53/72] drm: implement page flipping support for planes Steve Longerbeam
2014-11-01 13:25   ` Rob Clark
2014-10-31 22:54 ` [PATCH 54/72] imx-drm: ipuv3-plane: Assign page_flip method to drm_plane_funcs Steve Longerbeam
2014-10-31 22:54 ` [PATCH 55/72] imx-drm: Implement DRM gamma set Steve Longerbeam
2014-10-31 22:54 ` [PATCH 56/72] imx-drm: Implement custom ioctl to set gamma Steve Longerbeam
2014-10-31 22:54 ` [PATCH 57/72] imx-drm: Add support for interface pixel maps Steve Longerbeam
2014-10-31 22:54 ` [PATCH 58/72] imx-drm: parallel-display: Add interface-pix-map DT property Steve Longerbeam
2014-10-31 22:54 ` [PATCH 59/72] imx-drm: hdmi: set DI clock source to DI pre clock Steve Longerbeam
2014-10-31 22:54 ` [PATCH 60/72] imx-drm: ipuv3-crtc: Set the crtc device name Steve Longerbeam
2014-10-31 22:54 ` [PATCH 61/72] imx-drm: hdmi: Save ipu/di mux for later iomux setup Steve Longerbeam
2014-10-31 22:54 ` [PATCH 62/72] imx-drm: ipuv3-plane: Assign correct dmfc burst size Steve Longerbeam
2014-10-31 22:54 ` [PATCH 63/72] drm_modes: videomode: add pos/neg pixel clock polarity flag Steve Longerbeam
2014-10-31 22:54 ` [PATCH 64/72] imx-drm: ipuv3-crtc: Use DRM mode flags to configure pixel clock polarity Steve Longerbeam
2014-10-31 22:54 ` [PATCH 65/72] imx-drm: imx-ldb: Add all defined of video modes Steve Longerbeam
2014-10-31 22:54 ` [PATCH 66/72] imx-drm: parallel-display: " Steve Longerbeam
2014-10-31 22:54 ` [PATCH 67/72] imx-drm: ipuv3-crtc: Disable fb on crtc unbind Steve Longerbeam
2014-10-31 22:54 ` [PATCH 68/72] imx-drm: imx-ldb: Use DDC probe as connection detect Steve Longerbeam
2014-11-04 17:57   ` Philipp Zabel
2014-10-31 22:54 ` [PATCH 69/72] imx-drm: ipuv3-crtc: Implement mode_set_base Steve Longerbeam
2014-10-31 22:54 ` [PATCH 70/72] imx-drm: Cancel pending page flip events at preclose Steve Longerbeam
2014-10-31 22:54 ` [PATCH 71/72] imx-drm: ipuv3-crtc: Disable overlay plane during crtc disable Steve Longerbeam
2014-10-31 22:54 ` [PATCH 72/72] imx-drm: ipuv3-plane: Enable 8 burst locking Steve Longerbeam
2014-11-01  0:09 ` [PATCH 00/72] staging imx-drm new features and fixes Fabio Estevam
2014-11-01  0:19   ` Steve Longerbeam
2014-11-03 13:12     ` Fabio Estevam
2014-11-03 13:17       ` Fabio Estevam
2014-11-03 13:20       ` Fabio Estevam
2014-11-03 14:13         ` Fabio Estevam
2014-11-03 22:41           ` Steve Longerbeam
2014-11-04 15:15             ` Fabio Estevam
2014-11-02  3:03   ` Steve Longerbeam
2014-11-03 11:17 ` Zubair Lutfullah Kakakhel
2014-11-03 16:04   ` Rob Clark
2014-11-03 16:12 ` Daniel Vetter
2014-11-03 16:14   ` Greg KH
2014-11-03 17:17     ` Daniel Vetter
2014-11-03 17:48       ` Greg KH
2014-11-03 18:58         ` Steve Longerbeam
2014-11-04  2:32           ` Alex Deucher
2014-11-03 18:59         ` Steve Longerbeam
2014-11-03 20:00           ` Fabio Estevam
2014-11-03 22:11             ` Steve Longerbeam
2014-11-04  9:35           ` Philipp Zabel
2014-11-05  1:20             ` Steve Longerbeam
2015-01-17 19:45               ` Fabio Estevam
2015-01-21 17:22 ` Rob Clark

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=1414796095-10107-32-git-send-email-steve_longerbeam@mentor.com \
    --to=slongerbeam@gmail.com \
    --cc=dri-devel@lists.freedesktop.org \
    /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