drivers/gpu/drm/bridge/nwl-dsi.c | 66 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-)
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>
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
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
© 2016 - 2026 Red Hat, Inc.