[PATCH] drm/bridge: nwl-dsi: Correct MIPI DSI horizontal sync timing

Sebastian Krzyszkowiak posted 1 patch 2 weeks, 2 days ago
There is a newer version of this series
drivers/gpu/drm/bridge/nwl-dsi.c | 66 ++++++++++++++++++++++++++++++++++++++--
1 file changed, 63 insertions(+), 3 deletions(-)
[PATCH] drm/bridge: nwl-dsi: Correct MIPI DSI horizontal sync timing
Posted by Sebastian Krzyszkowiak 2 weeks, 2 days ago
From: Robert Chiras <robert.chiras@nxp.com>

The NWL MIPI Host controller registers set the number of bytes for the
horzontal front porch, sync pulse, and back porch, not the number of
pixels. The formula converts the hfp, hsa, and hbp to bytes then subtracts
the number of packet overhead bytes in the horizontal line which totals 32.
The overhead is split into three proportional chunks and subtracted from
fp, hsa, and hbp.

Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
Signed-off-by: Oliver F. Brown <oliver.brown@oss.nxp.com>
Fixes: 44cfc6233447 ("drm/bridge: Add NWL MIPI DSI host controller support")
Signed-off-by: Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
---
Taken from the NXP linux-imx fork. This makes it possible to e.g.
correctly drive the Librem 5's internal DSI panel at 60 Hz.
---
 drivers/gpu/drm/bridge/nwl-dsi.c | 66 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c b/drivers/gpu/drm/bridge/nwl-dsi.c
index 2f7429b24fc2..18e4bfe66b4b 100644
--- a/drivers/gpu/drm/bridge/nwl-dsi.c
+++ b/drivers/gpu/drm/bridge/nwl-dsi.c
@@ -260,6 +260,10 @@ static int nwl_dsi_config_dpi(struct nwl_dsi *dsi)
 	bool burst_mode;
 	int hfront_porch, hback_porch, vfront_porch, vback_porch;
 	int hsync_len, vsync_len;
+	int hfp, hbp, hsa;
+	unsigned long long pclk_period;
+	unsigned long long hs_period;
+	int h_blank, pkt_hdr_len, pkt_len;
 
 	hfront_porch = dsi->mode.hsync_start - dsi->mode.hdisplay;
 	hsync_len = dsi->mode.hsync_end - dsi->mode.hsync_start;
@@ -313,9 +317,65 @@ static int nwl_dsi_config_dpi(struct nwl_dsi *dsi)
 			      dsi->mode.hdisplay);
 	}
 
-	nwl_dsi_write(dsi, NWL_DSI_HFP, hfront_porch);
-	nwl_dsi_write(dsi, NWL_DSI_HBP, hback_porch);
-	nwl_dsi_write(dsi, NWL_DSI_HSA, hsync_len);
+	pclk_period = ALIGN(PSEC_PER_SEC, dsi->mode.clock * 1000);
+	do_div(pclk_period, dsi->mode.clock * 1000);
+	DRM_DEV_DEBUG_DRIVER(dsi->dev, "pclk_period: %llu\n", pclk_period);
+
+	hs_period = ALIGN(PSEC_PER_SEC, dsi->phy_cfg.mipi_dphy.hs_clk_rate);
+	do_div(hs_period, dsi->phy_cfg.mipi_dphy.hs_clk_rate);
+	DRM_DEV_DEBUG_DRIVER(dsi->dev, "hs_period: %llu\n", hs_period);
+
+	/*
+	 * Calculate the bytes needed, according to the RM formula:
+	 * Time of DPI event = time to transmit x number of bytes on the DSI
+	 * interface
+	 * dpi_event_size * dpi_pclk_period = dsi_bytes * 8 * hs_bit_period /
+	 * num_lanes
+	 * ===>
+	 * dsi_bytes = dpi_event_size * dpi_pclk_period * num_lanes /
+	 * (8 * hs_bit_period)
+	 */
+	hfp = hfront_porch * pclk_period * dsi->lanes / (8 * hs_period);
+	hbp = hback_porch * pclk_period * dsi->lanes / (8 * hs_period);
+	hsa = hsync_len * pclk_period * dsi->lanes / (8 * hs_period);
+
+	/* Make sure horizontal blankins are even numbers */
+	hfp = roundup(hfp, 2);
+	hbp = roundup(hbp, 2);
+	hsa = roundup(hsa, 2);
+
+	/*
+	 * We need to subtract the packet header length: 32
+	 * In order to make sure we don't get negative values,
+	 * subtract a proportional value to the total length of the
+	 * horizontal blanking duration.
+	 */
+	h_blank = hfp + hbp + hsa;
+
+	pkt_len = roundup(((hfp * 100 / h_blank) * 32) / 100, 2);
+	pkt_hdr_len = pkt_len;
+	hfp -= pkt_len;
+
+	pkt_len = roundup(((hbp * 100 / h_blank) * 32) / 100, 2);
+	pkt_hdr_len += pkt_len;
+	hbp -= pkt_len;
+
+	hsa -= (32 - pkt_hdr_len);
+
+	if (dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_NO_HFP)
+		hfp = hfront_porch;
+	if (dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_NO_HBP)
+		hbp = hback_porch;
+	if (dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_NO_HSA)
+		hsa = hsync_len;
+
+	DRM_DEV_DEBUG_DRIVER(dsi->dev, "Actual hfp: %d\n", hfp);
+	DRM_DEV_DEBUG_DRIVER(dsi->dev, "Actual hbp: %d\n", hbp);
+	DRM_DEV_DEBUG_DRIVER(dsi->dev, "Actual hsa: %d\n", hsa);
+
+	nwl_dsi_write(dsi, NWL_DSI_HFP, hfp);
+	nwl_dsi_write(dsi, NWL_DSI_HBP, hbp);
+	nwl_dsi_write(dsi, NWL_DSI_HSA, hsa);
 
 	nwl_dsi_write(dsi, NWL_DSI_ENABLE_MULT_PKTS, 0x0);
 	nwl_dsi_write(dsi, NWL_DSI_BLLP_MODE, 0x1);

---
base-commit: 0f2acd3148e0ef42bdacbd477f90e8533f96b2ac
change-id: 20260216-nwl-sync-timing-78902ab690bd

Best regards,
-- 
Sebastian Krzyszkowiak <sebastian.krzyszkowiak@puri.sm>
Re: [PATCH] drm/bridge: nwl-dsi: Correct MIPI DSI horizontal sync timing
Posted by kernel test robot 2 weeks, 1 day ago
Hi Sebastian,

kernel test robot noticed the following build errors:

[auto build test ERROR on 0f2acd3148e0ef42bdacbd477f90e8533f96b2ac]

url:    https://github.com/intel-lab-lkp/linux/commits/Sebastian-Krzyszkowiak/drm-bridge-nwl-dsi-Correct-MIPI-DSI-horizontal-sync-timing/20260217-015316
base:   0f2acd3148e0ef42bdacbd477f90e8533f96b2ac
patch link:    https://lore.kernel.org/r/20260216-nwl-sync-timing-v1-1-b0ff6ecf204a%40puri.sm
patch subject: [PATCH] drm/bridge: nwl-dsi: Correct MIPI DSI horizontal sync timing
config: arm-randconfig-r112-20260217 (https://download.01.org/0day-ci/archive/20260217/202602171616.QToUwfFa-lkp@intel.com/config)
compiler: clang version 23.0.0git (https://github.com/llvm/llvm-project e86750b29fa0ff207cd43213d66dabe565417638)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260217/202602171616.QToUwfFa-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602171616.QToUwfFa-lkp@intel.com/

All errors (new ones prefixed by >>):

>> ld.lld: error: undefined symbol: __aeabi_uldivmod
   >>> referenced by nwl-dsi.c
   >>>               drivers/gpu/drm/bridge/nwl-dsi.o:(nwl_dsi_bridge_mode_set) in archive vmlinux.a
   >>> referenced by nwl-dsi.c
   >>>               drivers/gpu/drm/bridge/nwl-dsi.o:(nwl_dsi_bridge_mode_set) in archive vmlinux.a
   >>> referenced by nwl-dsi.c
   >>>               drivers/gpu/drm/bridge/nwl-dsi.o:(nwl_dsi_bridge_mode_set) in archive vmlinux.a
   >>> did you mean: __aeabi_uidivmod
   >>> defined in: vmlinux.a(arch/arm/lib/lib1funcs.o)

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH] drm/bridge: nwl-dsi: Correct MIPI DSI horizontal sync timing
Posted by kernel test robot 2 weeks, 1 day ago
Hi Sebastian,

kernel test robot noticed the following build errors:

[auto build test ERROR on 0f2acd3148e0ef42bdacbd477f90e8533f96b2ac]

url:    https://github.com/intel-lab-lkp/linux/commits/Sebastian-Krzyszkowiak/drm-bridge-nwl-dsi-Correct-MIPI-DSI-horizontal-sync-timing/20260217-015316
base:   0f2acd3148e0ef42bdacbd477f90e8533f96b2ac
patch link:    https://lore.kernel.org/r/20260216-nwl-sync-timing-v1-1-b0ff6ecf204a%40puri.sm
patch subject: [PATCH] drm/bridge: nwl-dsi: Correct MIPI DSI horizontal sync timing
config: i386-randconfig-006-20260217 (https://download.01.org/0day-ci/archive/20260217/202602171326.PoWR3Iye-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260217/202602171326.PoWR3Iye-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602171326.PoWR3Iye-lkp@intel.com/

All errors (new ones prefixed by >>):

   ld: drivers/gpu/drm/bridge/nwl-dsi.o: in function `nwl_dsi_config_dpi':
>> drivers/gpu/drm/bridge/nwl-dsi.c:338:(.text+0x1dcb): undefined reference to `__udivdi3'
>> ld: drivers/gpu/drm/bridge/nwl-dsi.c:339:(.text+0x1e2e): undefined reference to `__udivdi3'
   ld: drivers/gpu/drm/bridge/nwl-dsi.c:340:(.text+0x1e8f): undefined reference to `__udivdi3'


vim +338 drivers/gpu/drm/bridge/nwl-dsi.c

   255	
   256	static int nwl_dsi_config_dpi(struct nwl_dsi *dsi)
   257	{
   258		u32 mode;
   259		int color_format;
   260		bool burst_mode;
   261		int hfront_porch, hback_porch, vfront_porch, vback_porch;
   262		int hsync_len, vsync_len;
   263		int hfp, hbp, hsa;
   264		unsigned long long pclk_period;
   265		unsigned long long hs_period;
   266		int h_blank, pkt_hdr_len, pkt_len;
   267	
   268		hfront_porch = dsi->mode.hsync_start - dsi->mode.hdisplay;
   269		hsync_len = dsi->mode.hsync_end - dsi->mode.hsync_start;
   270		hback_porch = dsi->mode.htotal - dsi->mode.hsync_end;
   271	
   272		vfront_porch = dsi->mode.vsync_start - dsi->mode.vdisplay;
   273		vsync_len = dsi->mode.vsync_end - dsi->mode.vsync_start;
   274		vback_porch = dsi->mode.vtotal - dsi->mode.vsync_end;
   275	
   276		DRM_DEV_DEBUG_DRIVER(dsi->dev, "hfront_porch = %d\n", hfront_porch);
   277		DRM_DEV_DEBUG_DRIVER(dsi->dev, "hback_porch = %d\n", hback_porch);
   278		DRM_DEV_DEBUG_DRIVER(dsi->dev, "hsync_len = %d\n", hsync_len);
   279		DRM_DEV_DEBUG_DRIVER(dsi->dev, "hdisplay = %d\n", dsi->mode.hdisplay);
   280		DRM_DEV_DEBUG_DRIVER(dsi->dev, "vfront_porch = %d\n", vfront_porch);
   281		DRM_DEV_DEBUG_DRIVER(dsi->dev, "vback_porch = %d\n", vback_porch);
   282		DRM_DEV_DEBUG_DRIVER(dsi->dev, "vsync_len = %d\n", vsync_len);
   283		DRM_DEV_DEBUG_DRIVER(dsi->dev, "vactive = %d\n", dsi->mode.vdisplay);
   284		DRM_DEV_DEBUG_DRIVER(dsi->dev, "clock = %d kHz\n", dsi->mode.clock);
   285	
   286		color_format = nwl_dsi_get_dpi_pixel_format(dsi->format);
   287		if (color_format < 0) {
   288			DRM_DEV_ERROR(dsi->dev, "Invalid color format 0x%x\n",
   289				      dsi->format);
   290			return color_format;
   291		}
   292		DRM_DEV_DEBUG_DRIVER(dsi->dev, "pixel fmt = %d\n", dsi->format);
   293	
   294		nwl_dsi_write(dsi, NWL_DSI_INTERFACE_COLOR_CODING, NWL_DSI_DPI_24_BIT);
   295		nwl_dsi_write(dsi, NWL_DSI_PIXEL_FORMAT, color_format);
   296		nwl_dsi_write(dsi, NWL_DSI_VSYNC_POLARITY,
   297			      dsi->mode.flags & DRM_MODE_FLAG_PVSYNC ?
   298			      NWL_DSI_VSYNC_POLARITY_ACTIVE_HIGH :
   299			      NWL_DSI_VSYNC_POLARITY_ACTIVE_LOW);
   300		nwl_dsi_write(dsi, NWL_DSI_HSYNC_POLARITY,
   301			      dsi->mode.flags & DRM_MODE_FLAG_PHSYNC ?
   302			      NWL_DSI_HSYNC_POLARITY_ACTIVE_HIGH :
   303			      NWL_DSI_HSYNC_POLARITY_ACTIVE_LOW);
   304	
   305		burst_mode = (dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_BURST) &&
   306			     !(dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE);
   307	
   308		if (burst_mode) {
   309			nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, NWL_DSI_VM_BURST_MODE);
   310			nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL, 256);
   311		} else {
   312			mode = ((dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) ?
   313					NWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSES :
   314					NWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_EVENTS);
   315			nwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, mode);
   316			nwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL,
   317				      dsi->mode.hdisplay);
   318		}
   319	
   320		pclk_period = ALIGN(PSEC_PER_SEC, dsi->mode.clock * 1000);
   321		do_div(pclk_period, dsi->mode.clock * 1000);
   322		DRM_DEV_DEBUG_DRIVER(dsi->dev, "pclk_period: %llu\n", pclk_period);
   323	
   324		hs_period = ALIGN(PSEC_PER_SEC, dsi->phy_cfg.mipi_dphy.hs_clk_rate);
   325		do_div(hs_period, dsi->phy_cfg.mipi_dphy.hs_clk_rate);
   326		DRM_DEV_DEBUG_DRIVER(dsi->dev, "hs_period: %llu\n", hs_period);
   327	
   328		/*
   329		 * Calculate the bytes needed, according to the RM formula:
   330		 * Time of DPI event = time to transmit x number of bytes on the DSI
   331		 * interface
   332		 * dpi_event_size * dpi_pclk_period = dsi_bytes * 8 * hs_bit_period /
   333		 * num_lanes
   334		 * ===>
   335		 * dsi_bytes = dpi_event_size * dpi_pclk_period * num_lanes /
   336		 * (8 * hs_bit_period)
   337		 */
 > 338		hfp = hfront_porch * pclk_period * dsi->lanes / (8 * hs_period);
 > 339		hbp = hback_porch * pclk_period * dsi->lanes / (8 * hs_period);
   340		hsa = hsync_len * pclk_period * dsi->lanes / (8 * hs_period);
   341	
   342		/* Make sure horizontal blankins are even numbers */
   343		hfp = roundup(hfp, 2);
   344		hbp = roundup(hbp, 2);
   345		hsa = roundup(hsa, 2);
   346	
   347		/*
   348		 * We need to subtract the packet header length: 32
   349		 * In order to make sure we don't get negative values,
   350		 * subtract a proportional value to the total length of the
   351		 * horizontal blanking duration.
   352		 */
   353		h_blank = hfp + hbp + hsa;
   354	
   355		pkt_len = roundup(((hfp * 100 / h_blank) * 32) / 100, 2);
   356		pkt_hdr_len = pkt_len;
   357		hfp -= pkt_len;
   358	
   359		pkt_len = roundup(((hbp * 100 / h_blank) * 32) / 100, 2);
   360		pkt_hdr_len += pkt_len;
   361		hbp -= pkt_len;
   362	
   363		hsa -= (32 - pkt_hdr_len);
   364	
   365		if (dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_NO_HFP)
   366			hfp = hfront_porch;
   367		if (dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_NO_HBP)
   368			hbp = hback_porch;
   369		if (dsi->dsi_mode_flags & MIPI_DSI_MODE_VIDEO_NO_HSA)
   370			hsa = hsync_len;
   371	
   372		DRM_DEV_DEBUG_DRIVER(dsi->dev, "Actual hfp: %d\n", hfp);
   373		DRM_DEV_DEBUG_DRIVER(dsi->dev, "Actual hbp: %d\n", hbp);
   374		DRM_DEV_DEBUG_DRIVER(dsi->dev, "Actual hsa: %d\n", hsa);
   375	
   376		nwl_dsi_write(dsi, NWL_DSI_HFP, hfp);
   377		nwl_dsi_write(dsi, NWL_DSI_HBP, hbp);
   378		nwl_dsi_write(dsi, NWL_DSI_HSA, hsa);
   379	
   380		nwl_dsi_write(dsi, NWL_DSI_ENABLE_MULT_PKTS, 0x0);
   381		nwl_dsi_write(dsi, NWL_DSI_BLLP_MODE, 0x1);
   382		nwl_dsi_write(dsi, NWL_DSI_USE_NULL_PKT_BLLP, 0x0);
   383		nwl_dsi_write(dsi, NWL_DSI_VC, 0x0);
   384	
   385		nwl_dsi_write(dsi, NWL_DSI_PIXEL_PAYLOAD_SIZE, dsi->mode.hdisplay);
   386		nwl_dsi_write(dsi, NWL_DSI_VACTIVE, dsi->mode.vdisplay - 1);
   387		nwl_dsi_write(dsi, NWL_DSI_VBP, vback_porch);
   388		nwl_dsi_write(dsi, NWL_DSI_VFP, vfront_porch);
   389	
   390		return nwl_dsi_clear_error(dsi);
   391	}
   392	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki