* [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements
@ 2023-08-22 16:19 Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 01/12] drm/tegra: rgb: Parameterize V- and H-sync polarities Tomi Valkeinen
` (12 more replies)
0 siblings, 13 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Aradhya Bhatia, Tomi Valkeinen, linux-kernel, dri-devel,
Thierry Reding, Thierry Reding
This series contains various fixes and cleanups for TC358768. The target
of this work is to get TC358768 working on Toradex's AM62 based board,
which has the following display pipeline:
AM62 DPI -> TC358768 -> LT8912B -> HDMI connector
The main thing the series does is to improve the DSI HSW, HFP and VSDly
calculations.
Tomi
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
Changes in v3:
- Add Peter's reviewed-bys
- Move "Default to positive h/v syncs" earlier in the series to avoid
regression in the middle of the series
- Link to v2: https://lore.kernel.org/r/20230816-tc358768-v2-0-242b9d5f703a@ideasonboard.com
Changes in v2:
- Add "drm/tegra: rgb: Parameterize V- and H-sync polarities" so that
Tegra can configure the polarities correctly.
- Add "drm/bridge: tc358768: Default to positive h/v syncs" as we don't
(necessarily) have the polarities set in the mode.
- Drop "drm/bridge: tc358768: Add DRM_BRIDGE_ATTACH_NO_CONNECTOR
support" as it's not needed for DRM_BRIDGE_ATTACH_NO_CONNECTOR
support.
- Link to v1: https://lore.kernel.org/r/20230804-tc358768-v1-0-1afd44b7826b@ideasonboard.com
---
Thierry Reding (1):
drm/tegra: rgb: Parameterize V- and H-sync polarities
Tomi Valkeinen (11):
drm/bridge: tc358768: Fix use of uninitialized variable
drm/bridge: tc358768: Default to positive h/v syncs
drm/bridge: tc358768: Fix bit updates
drm/bridge: tc358768: Cleanup PLL calculations
drm/bridge: tc358768: Use struct videomode
drm/bridge: tc358768: Print logical values, not raw register values
drm/bridge: tc358768: Use dev for dbg prints, not priv->dev
drm/bridge: tc358768: Rename dsibclk to hsbyteclk
drm/bridge: tc358768: Clean up clock period code
drm/bridge: tc358768: Fix tc358768_ns_to_cnt()
drm/bridge: tc358768: Attempt to fix DSI horizontal timings
drivers/gpu/drm/bridge/tc358768.c | 381 ++++++++++++++++++++++++++++----------
drivers/gpu/drm/tegra/rgb.c | 16 +-
2 files changed, 295 insertions(+), 102 deletions(-)
---
base-commit: 25205087df1ffe06ccea9302944ed1f77dc68c6f
change-id: 20230804-tc358768-1b6949ef2e3d
Best regards,
--
Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v3 01/12] drm/tegra: rgb: Parameterize V- and H-sync polarities
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 02/12] drm/bridge: tc358768: Fix use of uninitialized variable Tomi Valkeinen
` (11 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Aradhya Bhatia, Tomi Valkeinen, linux-kernel, dri-devel,
Thierry Reding, Thierry Reding
From: Thierry Reding <treding@nvidia.com>
The polarities of the V- and H-sync signals are encoded as flags in the
display mode, so use the existing information to setup the signals for
the RGB interface.
Signed-off-by: Thierry Reding <treding@nvidia.com>
Cc: Thierry Reding <thierry.reding@gmail.com>
[tomi.valkeinen@ideasonboard.com: default to positive sync]
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/tegra/rgb.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c
index 79566c9ea8ff..fc66bbd913b2 100644
--- a/drivers/gpu/drm/tegra/rgb.c
+++ b/drivers/gpu/drm/tegra/rgb.c
@@ -99,6 +99,7 @@ static void tegra_rgb_encoder_disable(struct drm_encoder *encoder)
static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
{
+ struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
struct tegra_output *output = encoder_to_output(encoder);
struct tegra_rgb *rgb = to_rgb(output);
u32 value;
@@ -108,10 +109,19 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder)
value = DE_SELECT_ACTIVE | DE_CONTROL_NORMAL;
tegra_dc_writel(rgb->dc, value, DC_DISP_DATA_ENABLE_OPTIONS);
- /* XXX: parameterize? */
+ /* configure H- and V-sync signal polarities */
value = tegra_dc_readl(rgb->dc, DC_COM_PIN_OUTPUT_POLARITY(1));
- value &= ~LVS_OUTPUT_POLARITY_LOW;
- value &= ~LHS_OUTPUT_POLARITY_LOW;
+
+ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+ value |= LHS_OUTPUT_POLARITY_LOW;
+ else
+ value &= ~LHS_OUTPUT_POLARITY_LOW;
+
+ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+ value |= LVS_OUTPUT_POLARITY_LOW;
+ else
+ value &= ~LVS_OUTPUT_POLARITY_LOW;
+
tegra_dc_writel(rgb->dc, value, DC_COM_PIN_OUTPUT_POLARITY(1));
/* XXX: parameterize? */
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 02/12] drm/bridge: tc358768: Fix use of uninitialized variable
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 01/12] drm/tegra: rgb: Parameterize V- and H-sync polarities Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 03/12] drm/bridge: tc358768: Default to positive h/v syncs Tomi Valkeinen
` (10 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Tomi Valkeinen, linux-kernel, dri-devel, Aradhya Bhatia
smatch reports:
drivers/gpu/drm/bridge/tc358768.c:223 tc358768_update_bits() error: uninitialized symbol 'orig'.
Fix this by bailing out from tc358768_update_bits() if the
tc358768_read() produces an error.
Fixes: ff1ca6397b1d ("drm/bridge: Add tc358768 driver")
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/bridge/tc358768.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 819a4b6ec2a0..bc97a837955b 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -216,6 +216,10 @@ static void tc358768_update_bits(struct tc358768_priv *priv, u32 reg, u32 mask,
u32 tmp, orig;
tc358768_read(priv, reg, &orig);
+
+ if (priv->error)
+ return;
+
tmp = orig & ~mask;
tmp |= val & mask;
if (tmp != orig)
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 03/12] drm/bridge: tc358768: Default to positive h/v syncs
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 01/12] drm/tegra: rgb: Parameterize V- and H-sync polarities Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 02/12] drm/bridge: tc358768: Fix use of uninitialized variable Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 04/12] drm/bridge: tc358768: Fix bit updates Tomi Valkeinen
` (9 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Tomi Valkeinen, linux-kernel, dri-devel, Aradhya Bhatia
As the TC358768 is a DPI to DSI bridge, the DSI side does not need to
define h/v sync polarities. This means that sometimes we have a mode
without defined sync polarities, which does not work on the DPI side.
Add a mode_fixup hook to default to positive sync polarities.
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/bridge/tc358768.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index bc97a837955b..963ac550509b 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -963,9 +963,27 @@ tc358768_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
return input_fmts;
}
+static bool tc358768_mode_fixup(struct drm_bridge *bridge,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ /* Default to positive sync */
+
+ if (!(adjusted_mode->flags &
+ (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC)))
+ adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC;
+
+ if (!(adjusted_mode->flags &
+ (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC)))
+ adjusted_mode->flags |= DRM_MODE_FLAG_PVSYNC;
+
+ return true;
+}
+
static const struct drm_bridge_funcs tc358768_bridge_funcs = {
.attach = tc358768_bridge_attach,
.mode_valid = tc358768_bridge_mode_valid,
+ .mode_fixup = tc358768_mode_fixup,
.pre_enable = tc358768_bridge_pre_enable,
.enable = tc358768_bridge_enable,
.disable = tc358768_bridge_disable,
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 04/12] drm/bridge: tc358768: Fix bit updates
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
` (2 preceding siblings ...)
2023-08-22 16:19 ` [PATCH v3 03/12] drm/bridge: tc358768: Default to positive h/v syncs Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 05/12] drm/bridge: tc358768: Cleanup PLL calculations Tomi Valkeinen
` (8 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Tomi Valkeinen, linux-kernel, dri-devel, Aradhya Bhatia
The driver has a few places where it does:
if (thing_is_enabled_in_config)
update_thing_bit_in_hw()
This means that if the thing is _not_ enabled, the bit never gets
cleared. This affects the h/vsyncs and continuous DSI clock bits.
Fix the driver to always update the bit.
Fixes: ff1ca6397b1d ("drm/bridge: Add tc358768 driver")
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/bridge/tc358768.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 963ac550509b..99992af23f1e 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -794,8 +794,8 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
val |= BIT(i + 1);
tc358768_write(priv, TC358768_HSTXVREGEN, val);
- if (!(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS))
- tc358768_write(priv, TC358768_TXOPTIONCNTRL, 0x1);
+ tc358768_write(priv, TC358768_TXOPTIONCNTRL,
+ (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0 : BIT(0));
/* TXTAGOCNT[26:16] RXTASURECNT[10:0] */
val = tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk * 4);
@@ -861,11 +861,12 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
tc358768_write(priv, TC358768_DSI_HACT, hact);
/* VSYNC polarity */
- if (!(mode->flags & DRM_MODE_FLAG_NVSYNC))
- tc358768_update_bits(priv, TC358768_CONFCTL, BIT(5), BIT(5));
+ tc358768_update_bits(priv, TC358768_CONFCTL, BIT(5),
+ (mode->flags & DRM_MODE_FLAG_PVSYNC) ? BIT(5) : 0);
+
/* HSYNC polarity */
- if (mode->flags & DRM_MODE_FLAG_PHSYNC)
- tc358768_update_bits(priv, TC358768_PP_MISC, BIT(0), BIT(0));
+ tc358768_update_bits(priv, TC358768_PP_MISC, BIT(0),
+ (mode->flags & DRM_MODE_FLAG_PHSYNC) ? BIT(0) : 0);
/* Start DSI Tx */
tc358768_write(priv, TC358768_DSI_START, 0x1);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 05/12] drm/bridge: tc358768: Cleanup PLL calculations
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
` (3 preceding siblings ...)
2023-08-22 16:19 ` [PATCH v3 04/12] drm/bridge: tc358768: Fix bit updates Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 06/12] drm/bridge: tc358768: Use struct videomode Tomi Valkeinen
` (7 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Tomi Valkeinen, linux-kernel, dri-devel, Aradhya Bhatia
As is quite common, some of TC358768's PLL register fields are to be
programmed with (value - 1). Specifically, the FBD and PRD, multiplier
and divider, are such fields.
However, what the driver currently does is that it considers that the
formula used for PLL rate calculation is:
RefClk * [(FBD + 1)/ (PRD + 1)] * [1 / (2^FRS)]
where FBD and PRD are values directly from the registers, while a more
sensible way to look at it is:
RefClk * FBD / PRD * (1 / (2^FRS))
and when the FBD and PRD values are written to the registers, they will
be subtracted by one.
Change the driver accordingly, as it simplifies the PLL code.
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/bridge/tc358768.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 99992af23f1e..a465674f1e2e 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -316,7 +316,7 @@ static int tc358768_calc_pll(struct tc358768_priv *priv,
target_pll = tc358768_pclk_to_pll(priv, mode->clock * 1000);
- /* pll_clk = RefClk * [(FBD + 1)/ (PRD + 1)] * [1 / (2^FRS)] */
+ /* pll_clk = RefClk * FBD / PRD * (1 / (2^FRS)) */
for (i = 0; i < ARRAY_SIZE(frs_limits); i++)
if (target_pll >= frs_limits[i])
@@ -336,19 +336,19 @@ static int tc358768_calc_pll(struct tc358768_priv *priv,
best_prd = 0;
best_fbd = 0;
- for (prd = 0; prd < 16; ++prd) {
- u32 divisor = (prd + 1) * (1 << frs);
+ for (prd = 1; prd <= 16; ++prd) {
+ u32 divisor = prd * (1 << frs);
u32 fbd;
- for (fbd = 0; fbd < 512; ++fbd) {
+ for (fbd = 1; fbd <= 512; ++fbd) {
u32 pll, diff, pll_in;
- pll = (u32)div_u64((u64)refclk * (fbd + 1), divisor);
+ pll = (u32)div_u64((u64)refclk * fbd, divisor);
if (pll >= max_pll || pll < min_pll)
continue;
- pll_in = (u32)div_u64((u64)refclk, prd + 1);
+ pll_in = (u32)div_u64((u64)refclk, prd);
if (pll_in < 4000000)
continue;
@@ -611,7 +611,7 @@ static int tc358768_setup_pll(struct tc358768_priv *priv,
mode->clock * 1000);
/* PRD[15:12] FBD[8:0] */
- tc358768_write(priv, TC358768_PLLCTL0, (prd << 12) | fbd);
+ tc358768_write(priv, TC358768_PLLCTL0, ((prd - 1) << 12) | (fbd - 1));
/* FRS[11:10] LBWS[9:8] CKEN[4] RESETB[1] EN[0] */
tc358768_write(priv, TC358768_PLLCTL1,
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 06/12] drm/bridge: tc358768: Use struct videomode
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
` (4 preceding siblings ...)
2023-08-22 16:19 ` [PATCH v3 05/12] drm/bridge: tc358768: Cleanup PLL calculations Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 07/12] drm/bridge: tc358768: Print logical values, not raw register values Tomi Valkeinen
` (6 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Tomi Valkeinen, linux-kernel, dri-devel, Aradhya Bhatia
The TC358768 documentation uses HFP, HBP, etc. values to deal with the
video mode, while the driver currently uses the DRM display mode
(htotal, hsync_start, etc).
Change the driver to convert the DRM display mode to struct videomode,
which then allows us to use the same units the documentation uses. This
makes it much easier to work on the code when using the TC358768
documentation as a reference.
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/bridge/tc358768.c | 45 +++++++++++++++++++++------------------
1 file changed, 24 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index a465674f1e2e..b98c517c4726 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -650,6 +650,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
u32 dsiclk, dsibclk, video_start;
const u32 internal_delay = 40;
int ret, i;
+ struct videomode vm;
if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
dev_warn_once(priv->dev, "Non-continuous mode unimplemented, falling back to continuous\n");
@@ -673,6 +674,8 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
return;
}
+ drm_display_mode_to_videomode(mode, &vm);
+
dsiclk = priv->dsiclk;
dsibclk = dsiclk / 4;
@@ -681,28 +684,28 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
switch (dsi_dev->format) {
case MIPI_DSI_FMT_RGB888:
val |= (0x3 << 4);
- hact = mode->hdisplay * 3;
- video_start = (mode->htotal - mode->hsync_start) * 3;
+ hact = vm.hactive * 3;
+ video_start = (vm.hsync_len + vm.hback_porch) * 3;
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
break;
case MIPI_DSI_FMT_RGB666:
val |= (0x4 << 4);
- hact = mode->hdisplay * 3;
- video_start = (mode->htotal - mode->hsync_start) * 3;
+ hact = vm.hactive * 3;
+ video_start = (vm.hsync_len + vm.hback_porch) * 3;
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
break;
case MIPI_DSI_FMT_RGB666_PACKED:
val |= (0x4 << 4) | BIT(3);
- hact = mode->hdisplay * 18 / 8;
- video_start = (mode->htotal - mode->hsync_start) * 18 / 8;
+ hact = vm.hactive * 18 / 8;
+ video_start = (vm.hsync_len + vm.hback_porch) * 18 / 8;
data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
break;
case MIPI_DSI_FMT_RGB565:
val |= (0x5 << 4);
- hact = mode->hdisplay * 2;
- video_start = (mode->htotal - mode->hsync_start) * 2;
+ hact = vm.hactive * 2;
+ video_start = (vm.hsync_len + vm.hback_porch) * 2;
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
break;
default:
@@ -814,43 +817,43 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
tc358768_write(priv, TC358768_DSI_EVENT, 0);
/* vact */
- tc358768_write(priv, TC358768_DSI_VACT, mode->vdisplay);
+ tc358768_write(priv, TC358768_DSI_VACT, vm.vactive);
/* vsw */
- tc358768_write(priv, TC358768_DSI_VSW,
- mode->vsync_end - mode->vsync_start);
+ tc358768_write(priv, TC358768_DSI_VSW, vm.vsync_len);
+
/* vbp */
- tc358768_write(priv, TC358768_DSI_VBPR,
- mode->vtotal - mode->vsync_end);
+ tc358768_write(priv, TC358768_DSI_VBPR, vm.vback_porch);
/* hsw * byteclk * ndl / pclk */
- val = (u32)div_u64((mode->hsync_end - mode->hsync_start) *
+ val = (u32)div_u64(vm.hsync_len *
((u64)priv->dsiclk / 4) * priv->dsi_lanes,
- mode->clock * 1000);
+ vm.pixelclock);
tc358768_write(priv, TC358768_DSI_HSW, val);
/* hbp * byteclk * ndl / pclk */
- val = (u32)div_u64((mode->htotal - mode->hsync_end) *
+ val = (u32)div_u64(vm.hback_porch *
((u64)priv->dsiclk / 4) * priv->dsi_lanes,
- mode->clock * 1000);
+ vm.pixelclock);
tc358768_write(priv, TC358768_DSI_HBPR, val);
} else {
/* Set event mode */
tc358768_write(priv, TC358768_DSI_EVENT, 1);
/* vact */
- tc358768_write(priv, TC358768_DSI_VACT, mode->vdisplay);
+ tc358768_write(priv, TC358768_DSI_VACT, vm.vactive);
/* vsw (+ vbp) */
tc358768_write(priv, TC358768_DSI_VSW,
- mode->vtotal - mode->vsync_start);
+ vm.vsync_len + vm.vback_porch);
+
/* vbp (not used in event mode) */
tc358768_write(priv, TC358768_DSI_VBPR, 0);
/* (hsw + hbp) * byteclk * ndl / pclk */
- val = (u32)div_u64((mode->htotal - mode->hsync_start) *
+ val = (u32)div_u64((vm.hsync_len + vm.hback_porch) *
((u64)priv->dsiclk / 4) * priv->dsi_lanes,
- mode->clock * 1000);
+ vm.pixelclock);
tc358768_write(priv, TC358768_DSI_HSW, val);
/* hbp (not used in event mode) */
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 07/12] drm/bridge: tc358768: Print logical values, not raw register values
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
` (5 preceding siblings ...)
2023-08-22 16:19 ` [PATCH v3 06/12] drm/bridge: tc358768: Use struct videomode Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 08/12] drm/bridge: tc358768: Use dev for dbg prints, not priv->dev Tomi Valkeinen
` (5 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Tomi Valkeinen, linux-kernel, dri-devel, Aradhya Bhatia
The driver debug prints DSI related timings as raw register values in
hex. It is much more useful to see the "logical" value of the timing,
not the register value.
Change the prints to print the values separately, in case a single
register contains multiple values, and use %u to have it in a more human
consumable form.
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/bridge/tc358768.c | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index b98c517c4726..88060f961064 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -739,57 +739,59 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
/* LP11 > 100us for D-PHY Rx Init */
val = tc358768_ns_to_cnt(100 * 1000, dsibclk_nsk) - 1;
- dev_dbg(priv->dev, "LINEINITCNT: 0x%x\n", val);
+ dev_dbg(priv->dev, "LINEINITCNT: %u\n", val);
tc358768_write(priv, TC358768_LINEINITCNT, val);
/* LPTimeCnt > 50ns */
val = tc358768_ns_to_cnt(50, dsibclk_nsk) - 1;
lptxcnt = val;
- dev_dbg(priv->dev, "LPTXTIMECNT: 0x%x\n", val);
+ dev_dbg(priv->dev, "LPTXTIMECNT: %u\n", val);
tc358768_write(priv, TC358768_LPTXTIMECNT, val);
/* 38ns < TCLK_PREPARE < 95ns */
val = tc358768_ns_to_cnt(65, dsibclk_nsk) - 1;
+ dev_dbg(priv->dev, "TCLK_PREPARECNT %u\n", val);
/* TCLK_PREPARE + TCLK_ZERO > 300ns */
val2 = tc358768_ns_to_cnt(300 - tc358768_to_ns(2 * ui_nsk),
dsibclk_nsk) - 2;
+ dev_dbg(priv->dev, "TCLK_ZEROCNT %u\n", val2);
val |= val2 << 8;
- dev_dbg(priv->dev, "TCLK_HEADERCNT: 0x%x\n", val);
tc358768_write(priv, TC358768_TCLK_HEADERCNT, val);
/* TCLK_TRAIL > 60ns AND TEOT <= 105 ns + 12*UI */
raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(2 * ui_nsk), dsibclk_nsk) - 5;
val = clamp(raw_val, 0, 127);
- dev_dbg(priv->dev, "TCLK_TRAILCNT: 0x%x\n", val);
+ dev_dbg(priv->dev, "TCLK_TRAILCNT: %u\n", val);
tc358768_write(priv, TC358768_TCLK_TRAILCNT, val);
/* 40ns + 4*UI < THS_PREPARE < 85ns + 6*UI */
val = 50 + tc358768_to_ns(4 * ui_nsk);
val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 1;
+ dev_dbg(priv->dev, "THS_PREPARECNT %u\n", val);
/* THS_PREPARE + THS_ZERO > 145ns + 10*UI */
raw_val = tc358768_ns_to_cnt(145 - tc358768_to_ns(3 * ui_nsk), dsibclk_nsk) - 10;
val2 = clamp(raw_val, 0, 127);
+ dev_dbg(priv->dev, "THS_ZEROCNT %u\n", val2);
val |= val2 << 8;
- dev_dbg(priv->dev, "THS_HEADERCNT: 0x%x\n", val);
tc358768_write(priv, TC358768_THS_HEADERCNT, val);
/* TWAKEUP > 1ms in lptxcnt steps */
val = tc358768_ns_to_cnt(1020000, dsibclk_nsk);
val = val / (lptxcnt + 1) - 1;
- dev_dbg(priv->dev, "TWAKEUP: 0x%x\n", val);
+ dev_dbg(priv->dev, "TWAKEUP: %u\n", val);
tc358768_write(priv, TC358768_TWAKEUP, val);
/* TCLK_POSTCNT > 60ns + 52*UI */
val = tc358768_ns_to_cnt(60 + tc358768_to_ns(52 * ui_nsk),
dsibclk_nsk) - 3;
- dev_dbg(priv->dev, "TCLK_POSTCNT: 0x%x\n", val);
+ dev_dbg(priv->dev, "TCLK_POSTCNT: %u\n", val);
tc358768_write(priv, TC358768_TCLK_POSTCNT, val);
/* max(60ns + 4*UI, 8*UI) < THS_TRAILCNT < 105ns + 12*UI */
raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(18 * ui_nsk),
dsibclk_nsk) - 4;
val = clamp(raw_val, 0, 15);
- dev_dbg(priv->dev, "THS_TRAILCNT: 0x%x\n", val);
+ dev_dbg(priv->dev, "THS_TRAILCNT: %u\n", val);
tc358768_write(priv, TC358768_THS_TRAILCNT, val);
val = BIT(0);
@@ -803,10 +805,11 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
/* TXTAGOCNT[26:16] RXTASURECNT[10:0] */
val = tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk * 4);
val = tc358768_ns_to_cnt(val, dsibclk_nsk) / 4 - 1;
+ dev_dbg(priv->dev, "TXTAGOCNT: %u\n", val);
val2 = tc358768_ns_to_cnt(tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk),
dsibclk_nsk) - 2;
+ dev_dbg(priv->dev, "RXTASURECNT: %u\n", val2);
val = val << 16 | val2;
- dev_dbg(priv->dev, "BTACNTRL1: 0x%x\n", val);
tc358768_write(priv, TC358768_BTACNTRL1, val);
/* START[0] */
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 08/12] drm/bridge: tc358768: Use dev for dbg prints, not priv->dev
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
` (6 preceding siblings ...)
2023-08-22 16:19 ` [PATCH v3 07/12] drm/bridge: tc358768: Print logical values, not raw register values Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 09/12] drm/bridge: tc358768: Rename dsibclk to hsbyteclk Tomi Valkeinen
` (4 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Tomi Valkeinen, linux-kernel, dri-devel, Aradhya Bhatia
Simplify the code by capturing the priv->dev value to dev variable, and
use it.
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/bridge/tc358768.c | 41 ++++++++++++++++++++-------------------
1 file changed, 21 insertions(+), 20 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 88060f961064..6297d28250e9 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -651,9 +651,10 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
const u32 internal_delay = 40;
int ret, i;
struct videomode vm;
+ struct device *dev = priv->dev;
if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
- dev_warn_once(priv->dev, "Non-continuous mode unimplemented, falling back to continuous\n");
+ dev_warn_once(dev, "Non-continuous mode unimplemented, falling back to continuous\n");
mode_flags &= ~MIPI_DSI_CLOCK_NON_CONTINUOUS;
}
@@ -661,7 +662,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
ret = tc358768_sw_reset(priv);
if (ret) {
- dev_err(priv->dev, "Software reset failed: %d\n", ret);
+ dev_err(dev, "Software reset failed: %d\n", ret);
tc358768_hw_disable(priv);
return;
}
@@ -669,7 +670,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
mode = &bridge->encoder->crtc->state->adjusted_mode;
ret = tc358768_setup_pll(priv, mode);
if (ret) {
- dev_err(priv->dev, "PLL setup failed: %d\n", ret);
+ dev_err(dev, "PLL setup failed: %d\n", ret);
tc358768_hw_disable(priv);
return;
}
@@ -709,7 +710,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
break;
default:
- dev_err(priv->dev, "Invalid data format (%u)\n",
+ dev_err(dev, "Invalid data format (%u)\n",
dsi_dev->format);
tc358768_hw_disable(priv);
return;
@@ -733,65 +734,65 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
dsibclk);
dsiclk_nsk = (u32)div_u64((u64)1000000000 * TC358768_PRECISION, dsiclk);
ui_nsk = dsiclk_nsk / 2;
- dev_dbg(priv->dev, "dsiclk_nsk: %u\n", dsiclk_nsk);
- dev_dbg(priv->dev, "ui_nsk: %u\n", ui_nsk);
- dev_dbg(priv->dev, "dsibclk_nsk: %u\n", dsibclk_nsk);
+ dev_dbg(dev, "dsiclk_nsk: %u\n", dsiclk_nsk);
+ dev_dbg(dev, "ui_nsk: %u\n", ui_nsk);
+ dev_dbg(dev, "dsibclk_nsk: %u\n", dsibclk_nsk);
/* LP11 > 100us for D-PHY Rx Init */
val = tc358768_ns_to_cnt(100 * 1000, dsibclk_nsk) - 1;
- dev_dbg(priv->dev, "LINEINITCNT: %u\n", val);
+ dev_dbg(dev, "LINEINITCNT: %u\n", val);
tc358768_write(priv, TC358768_LINEINITCNT, val);
/* LPTimeCnt > 50ns */
val = tc358768_ns_to_cnt(50, dsibclk_nsk) - 1;
lptxcnt = val;
- dev_dbg(priv->dev, "LPTXTIMECNT: %u\n", val);
+ dev_dbg(dev, "LPTXTIMECNT: %u\n", val);
tc358768_write(priv, TC358768_LPTXTIMECNT, val);
/* 38ns < TCLK_PREPARE < 95ns */
val = tc358768_ns_to_cnt(65, dsibclk_nsk) - 1;
- dev_dbg(priv->dev, "TCLK_PREPARECNT %u\n", val);
+ dev_dbg(dev, "TCLK_PREPARECNT %u\n", val);
/* TCLK_PREPARE + TCLK_ZERO > 300ns */
val2 = tc358768_ns_to_cnt(300 - tc358768_to_ns(2 * ui_nsk),
dsibclk_nsk) - 2;
- dev_dbg(priv->dev, "TCLK_ZEROCNT %u\n", val2);
+ dev_dbg(dev, "TCLK_ZEROCNT %u\n", val2);
val |= val2 << 8;
tc358768_write(priv, TC358768_TCLK_HEADERCNT, val);
/* TCLK_TRAIL > 60ns AND TEOT <= 105 ns + 12*UI */
raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(2 * ui_nsk), dsibclk_nsk) - 5;
val = clamp(raw_val, 0, 127);
- dev_dbg(priv->dev, "TCLK_TRAILCNT: %u\n", val);
+ dev_dbg(dev, "TCLK_TRAILCNT: %u\n", val);
tc358768_write(priv, TC358768_TCLK_TRAILCNT, val);
/* 40ns + 4*UI < THS_PREPARE < 85ns + 6*UI */
val = 50 + tc358768_to_ns(4 * ui_nsk);
val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 1;
- dev_dbg(priv->dev, "THS_PREPARECNT %u\n", val);
+ dev_dbg(dev, "THS_PREPARECNT %u\n", val);
/* THS_PREPARE + THS_ZERO > 145ns + 10*UI */
raw_val = tc358768_ns_to_cnt(145 - tc358768_to_ns(3 * ui_nsk), dsibclk_nsk) - 10;
val2 = clamp(raw_val, 0, 127);
- dev_dbg(priv->dev, "THS_ZEROCNT %u\n", val2);
+ dev_dbg(dev, "THS_ZEROCNT %u\n", val2);
val |= val2 << 8;
tc358768_write(priv, TC358768_THS_HEADERCNT, val);
/* TWAKEUP > 1ms in lptxcnt steps */
val = tc358768_ns_to_cnt(1020000, dsibclk_nsk);
val = val / (lptxcnt + 1) - 1;
- dev_dbg(priv->dev, "TWAKEUP: %u\n", val);
+ dev_dbg(dev, "TWAKEUP: %u\n", val);
tc358768_write(priv, TC358768_TWAKEUP, val);
/* TCLK_POSTCNT > 60ns + 52*UI */
val = tc358768_ns_to_cnt(60 + tc358768_to_ns(52 * ui_nsk),
dsibclk_nsk) - 3;
- dev_dbg(priv->dev, "TCLK_POSTCNT: %u\n", val);
+ dev_dbg(dev, "TCLK_POSTCNT: %u\n", val);
tc358768_write(priv, TC358768_TCLK_POSTCNT, val);
/* max(60ns + 4*UI, 8*UI) < THS_TRAILCNT < 105ns + 12*UI */
raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(18 * ui_nsk),
dsibclk_nsk) - 4;
val = clamp(raw_val, 0, 15);
- dev_dbg(priv->dev, "THS_TRAILCNT: %u\n", val);
+ dev_dbg(dev, "THS_TRAILCNT: %u\n", val);
tc358768_write(priv, TC358768_THS_TRAILCNT, val);
val = BIT(0);
@@ -805,10 +806,10 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
/* TXTAGOCNT[26:16] RXTASURECNT[10:0] */
val = tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk * 4);
val = tc358768_ns_to_cnt(val, dsibclk_nsk) / 4 - 1;
- dev_dbg(priv->dev, "TXTAGOCNT: %u\n", val);
+ dev_dbg(dev, "TXTAGOCNT: %u\n", val);
val2 = tc358768_ns_to_cnt(tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk),
dsibclk_nsk) - 2;
- dev_dbg(priv->dev, "RXTASURECNT: %u\n", val2);
+ dev_dbg(dev, "RXTASURECNT: %u\n", val2);
val = val << 16 | val2;
tc358768_write(priv, TC358768_BTACNTRL1, val);
@@ -902,7 +903,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
ret = tc358768_clear_error(priv);
if (ret) {
- dev_err(priv->dev, "Bridge pre_enable failed: %d\n", ret);
+ dev_err(dev, "Bridge pre_enable failed: %d\n", ret);
tc358768_bridge_disable(bridge);
tc358768_bridge_post_disable(bridge);
}
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 09/12] drm/bridge: tc358768: Rename dsibclk to hsbyteclk
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
` (7 preceding siblings ...)
2023-08-22 16:19 ` [PATCH v3 08/12] drm/bridge: tc358768: Use dev for dbg prints, not priv->dev Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 10/12] drm/bridge: tc358768: Clean up clock period code Tomi Valkeinen
` (3 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Tomi Valkeinen, linux-kernel, dri-devel, Aradhya Bhatia
The Toshiba documentation talks about HSByteClk when referring to the
DSI HS byte clock, whereas the driver uses 'dsibclk' name. Also, in a
few places the driver calculates the byte clock from the DSI clock, even
if the byte clock is already available in a variable.
To align the driver with the documentation, change the 'dsibclk'
variable to 'hsbyteclk'. This also make it easier to visually separate
'dsibclk' and 'dsiclk' variables.
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/bridge/tc358768.c | 48 +++++++++++++++++++--------------------
1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 6297d28250e9..0f117d673b14 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -604,7 +604,7 @@ static int tc358768_setup_pll(struct tc358768_priv *priv,
dev_dbg(priv->dev, "PLL: refclk %lu, fbd %u, prd %u, frs %u\n",
clk_get_rate(priv->refclk), fbd, prd, frs);
- dev_dbg(priv->dev, "PLL: pll_clk: %u, DSIClk %u, DSIByteClk %u\n",
+ dev_dbg(priv->dev, "PLL: pll_clk: %u, DSIClk %u, HSByteClk %u\n",
priv->dsiclk * 2, priv->dsiclk, priv->dsiclk / 4);
dev_dbg(priv->dev, "PLL: pclk %u (panel: %u)\n",
tc358768_pll_to_pclk(priv, priv->dsiclk * 2),
@@ -646,8 +646,8 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
u32 val, val2, lptxcnt, hact, data_type;
s32 raw_val;
const struct drm_display_mode *mode;
- u32 dsibclk_nsk, dsiclk_nsk, ui_nsk;
- u32 dsiclk, dsibclk, video_start;
+ u32 hsbyteclk_nsk, dsiclk_nsk, ui_nsk;
+ u32 dsiclk, hsbyteclk, video_start;
const u32 internal_delay = 40;
int ret, i;
struct videomode vm;
@@ -678,7 +678,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
drm_display_mode_to_videomode(mode, &vm);
dsiclk = priv->dsiclk;
- dsibclk = dsiclk / 4;
+ hsbyteclk = dsiclk / 4;
/* Data Format Control Register */
val = BIT(2) | BIT(1) | BIT(0); /* rdswap_en | dsitx_en | txdt_en */
@@ -730,67 +730,67 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
tc358768_write(priv, TC358768_D0W_CNTRL + i * 4, 0x0000);
/* DSI Timings */
- dsibclk_nsk = (u32)div_u64((u64)1000000000 * TC358768_PRECISION,
- dsibclk);
+ hsbyteclk_nsk = (u32)div_u64((u64)1000000000 * TC358768_PRECISION,
+ hsbyteclk);
dsiclk_nsk = (u32)div_u64((u64)1000000000 * TC358768_PRECISION, dsiclk);
ui_nsk = dsiclk_nsk / 2;
dev_dbg(dev, "dsiclk_nsk: %u\n", dsiclk_nsk);
dev_dbg(dev, "ui_nsk: %u\n", ui_nsk);
- dev_dbg(dev, "dsibclk_nsk: %u\n", dsibclk_nsk);
+ dev_dbg(dev, "hsbyteclk_nsk: %u\n", hsbyteclk_nsk);
/* LP11 > 100us for D-PHY Rx Init */
- val = tc358768_ns_to_cnt(100 * 1000, dsibclk_nsk) - 1;
+ val = tc358768_ns_to_cnt(100 * 1000, hsbyteclk_nsk) - 1;
dev_dbg(dev, "LINEINITCNT: %u\n", val);
tc358768_write(priv, TC358768_LINEINITCNT, val);
/* LPTimeCnt > 50ns */
- val = tc358768_ns_to_cnt(50, dsibclk_nsk) - 1;
+ val = tc358768_ns_to_cnt(50, hsbyteclk_nsk) - 1;
lptxcnt = val;
dev_dbg(dev, "LPTXTIMECNT: %u\n", val);
tc358768_write(priv, TC358768_LPTXTIMECNT, val);
/* 38ns < TCLK_PREPARE < 95ns */
- val = tc358768_ns_to_cnt(65, dsibclk_nsk) - 1;
+ val = tc358768_ns_to_cnt(65, hsbyteclk_nsk) - 1;
dev_dbg(dev, "TCLK_PREPARECNT %u\n", val);
/* TCLK_PREPARE + TCLK_ZERO > 300ns */
val2 = tc358768_ns_to_cnt(300 - tc358768_to_ns(2 * ui_nsk),
- dsibclk_nsk) - 2;
+ hsbyteclk_nsk) - 2;
dev_dbg(dev, "TCLK_ZEROCNT %u\n", val2);
val |= val2 << 8;
tc358768_write(priv, TC358768_TCLK_HEADERCNT, val);
/* TCLK_TRAIL > 60ns AND TEOT <= 105 ns + 12*UI */
- raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(2 * ui_nsk), dsibclk_nsk) - 5;
+ raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(2 * ui_nsk), hsbyteclk_nsk) - 5;
val = clamp(raw_val, 0, 127);
dev_dbg(dev, "TCLK_TRAILCNT: %u\n", val);
tc358768_write(priv, TC358768_TCLK_TRAILCNT, val);
/* 40ns + 4*UI < THS_PREPARE < 85ns + 6*UI */
val = 50 + tc358768_to_ns(4 * ui_nsk);
- val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 1;
+ val = tc358768_ns_to_cnt(val, hsbyteclk_nsk) - 1;
dev_dbg(dev, "THS_PREPARECNT %u\n", val);
/* THS_PREPARE + THS_ZERO > 145ns + 10*UI */
- raw_val = tc358768_ns_to_cnt(145 - tc358768_to_ns(3 * ui_nsk), dsibclk_nsk) - 10;
+ raw_val = tc358768_ns_to_cnt(145 - tc358768_to_ns(3 * ui_nsk), hsbyteclk_nsk) - 10;
val2 = clamp(raw_val, 0, 127);
dev_dbg(dev, "THS_ZEROCNT %u\n", val2);
val |= val2 << 8;
tc358768_write(priv, TC358768_THS_HEADERCNT, val);
/* TWAKEUP > 1ms in lptxcnt steps */
- val = tc358768_ns_to_cnt(1020000, dsibclk_nsk);
+ val = tc358768_ns_to_cnt(1020000, hsbyteclk_nsk);
val = val / (lptxcnt + 1) - 1;
dev_dbg(dev, "TWAKEUP: %u\n", val);
tc358768_write(priv, TC358768_TWAKEUP, val);
/* TCLK_POSTCNT > 60ns + 52*UI */
val = tc358768_ns_to_cnt(60 + tc358768_to_ns(52 * ui_nsk),
- dsibclk_nsk) - 3;
+ hsbyteclk_nsk) - 3;
dev_dbg(dev, "TCLK_POSTCNT: %u\n", val);
tc358768_write(priv, TC358768_TCLK_POSTCNT, val);
/* max(60ns + 4*UI, 8*UI) < THS_TRAILCNT < 105ns + 12*UI */
raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(18 * ui_nsk),
- dsibclk_nsk) - 4;
+ hsbyteclk_nsk) - 4;
val = clamp(raw_val, 0, 15);
dev_dbg(dev, "THS_TRAILCNT: %u\n", val);
tc358768_write(priv, TC358768_THS_TRAILCNT, val);
@@ -804,11 +804,11 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0 : BIT(0));
/* TXTAGOCNT[26:16] RXTASURECNT[10:0] */
- val = tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk * 4);
- val = tc358768_ns_to_cnt(val, dsibclk_nsk) / 4 - 1;
+ val = tc358768_to_ns((lptxcnt + 1) * hsbyteclk_nsk * 4);
+ val = tc358768_ns_to_cnt(val, hsbyteclk_nsk) / 4 - 1;
dev_dbg(dev, "TXTAGOCNT: %u\n", val);
- val2 = tc358768_ns_to_cnt(tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk),
- dsibclk_nsk) - 2;
+ val2 = tc358768_ns_to_cnt(tc358768_to_ns((lptxcnt + 1) * hsbyteclk_nsk),
+ hsbyteclk_nsk) - 2;
dev_dbg(dev, "RXTASURECNT: %u\n", val2);
val = val << 16 | val2;
tc358768_write(priv, TC358768_BTACNTRL1, val);
@@ -831,13 +831,13 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
/* hsw * byteclk * ndl / pclk */
val = (u32)div_u64(vm.hsync_len *
- ((u64)priv->dsiclk / 4) * priv->dsi_lanes,
+ (u64)hsbyteclk * priv->dsi_lanes,
vm.pixelclock);
tc358768_write(priv, TC358768_DSI_HSW, val);
/* hbp * byteclk * ndl / pclk */
val = (u32)div_u64(vm.hback_porch *
- ((u64)priv->dsiclk / 4) * priv->dsi_lanes,
+ (u64)hsbyteclk * priv->dsi_lanes,
vm.pixelclock);
tc358768_write(priv, TC358768_DSI_HBPR, val);
} else {
@@ -856,7 +856,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
/* (hsw + hbp) * byteclk * ndl / pclk */
val = (u32)div_u64((vm.hsync_len + vm.hback_porch) *
- ((u64)priv->dsiclk / 4) * priv->dsi_lanes,
+ (u64)hsbyteclk * priv->dsi_lanes,
vm.pixelclock);
tc358768_write(priv, TC358768_DSI_HSW, val);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 10/12] drm/bridge: tc358768: Clean up clock period code
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
` (8 preceding siblings ...)
2023-08-22 16:19 ` [PATCH v3 09/12] drm/bridge: tc358768: Rename dsibclk to hsbyteclk Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 11/12] drm/bridge: tc358768: Fix tc358768_ns_to_cnt() Tomi Valkeinen
` (2 subsequent siblings)
12 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Tomi Valkeinen, linux-kernel, dri-devel, Aradhya Bhatia
The driver defines TC358768_PRECISION as 1000, and uses "nsk" to refer
to clock periods. The original author does not remember where all this
came from. Effectively the driver is using picoseconds as the unit for
clock periods, yet referring to them by "nsk".
Clean this up by just saying the periods are in picoseconds.
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/bridge/tc358768.c | 60 +++++++++++++++++++--------------------
1 file changed, 29 insertions(+), 31 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 0f117d673b14..9ce8d120b50c 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -15,6 +15,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/units.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
@@ -627,15 +628,14 @@ static int tc358768_setup_pll(struct tc358768_priv *priv,
return tc358768_clear_error(priv);
}
-#define TC358768_PRECISION 1000
-static u32 tc358768_ns_to_cnt(u32 ns, u32 period_nsk)
+static u32 tc358768_ns_to_cnt(u32 ns, u32 period_ps)
{
- return (ns * TC358768_PRECISION + period_nsk) / period_nsk;
+ return (ns * 1000 + period_ps) / period_ps;
}
-static u32 tc358768_to_ns(u32 nsk)
+static u32 tc358768_ps_to_ns(u32 ps)
{
- return (nsk / TC358768_PRECISION);
+ return ps / 1000;
}
static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
@@ -646,7 +646,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
u32 val, val2, lptxcnt, hact, data_type;
s32 raw_val;
const struct drm_display_mode *mode;
- u32 hsbyteclk_nsk, dsiclk_nsk, ui_nsk;
+ u32 hsbyteclk_ps, dsiclk_ps, ui_ps;
u32 dsiclk, hsbyteclk, video_start;
const u32 internal_delay = 40;
int ret, i;
@@ -730,67 +730,65 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
tc358768_write(priv, TC358768_D0W_CNTRL + i * 4, 0x0000);
/* DSI Timings */
- hsbyteclk_nsk = (u32)div_u64((u64)1000000000 * TC358768_PRECISION,
- hsbyteclk);
- dsiclk_nsk = (u32)div_u64((u64)1000000000 * TC358768_PRECISION, dsiclk);
- ui_nsk = dsiclk_nsk / 2;
- dev_dbg(dev, "dsiclk_nsk: %u\n", dsiclk_nsk);
- dev_dbg(dev, "ui_nsk: %u\n", ui_nsk);
- dev_dbg(dev, "hsbyteclk_nsk: %u\n", hsbyteclk_nsk);
+ hsbyteclk_ps = (u32)div_u64(PICO, hsbyteclk);
+ dsiclk_ps = (u32)div_u64(PICO, dsiclk);
+ ui_ps = dsiclk_ps / 2;
+ dev_dbg(dev, "dsiclk: %u ps, ui %u ps, hsbyteclk %u ps\n", dsiclk_ps,
+ ui_ps, hsbyteclk_ps);
/* LP11 > 100us for D-PHY Rx Init */
- val = tc358768_ns_to_cnt(100 * 1000, hsbyteclk_nsk) - 1;
+ val = tc358768_ns_to_cnt(100 * 1000, hsbyteclk_ps) - 1;
dev_dbg(dev, "LINEINITCNT: %u\n", val);
tc358768_write(priv, TC358768_LINEINITCNT, val);
/* LPTimeCnt > 50ns */
- val = tc358768_ns_to_cnt(50, hsbyteclk_nsk) - 1;
+ val = tc358768_ns_to_cnt(50, hsbyteclk_ps) - 1;
lptxcnt = val;
dev_dbg(dev, "LPTXTIMECNT: %u\n", val);
tc358768_write(priv, TC358768_LPTXTIMECNT, val);
/* 38ns < TCLK_PREPARE < 95ns */
- val = tc358768_ns_to_cnt(65, hsbyteclk_nsk) - 1;
+ val = tc358768_ns_to_cnt(65, hsbyteclk_ps) - 1;
dev_dbg(dev, "TCLK_PREPARECNT %u\n", val);
/* TCLK_PREPARE + TCLK_ZERO > 300ns */
- val2 = tc358768_ns_to_cnt(300 - tc358768_to_ns(2 * ui_nsk),
- hsbyteclk_nsk) - 2;
+ val2 = tc358768_ns_to_cnt(300 - tc358768_ps_to_ns(2 * ui_ps),
+ hsbyteclk_ps) - 2;
dev_dbg(dev, "TCLK_ZEROCNT %u\n", val2);
val |= val2 << 8;
tc358768_write(priv, TC358768_TCLK_HEADERCNT, val);
/* TCLK_TRAIL > 60ns AND TEOT <= 105 ns + 12*UI */
- raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(2 * ui_nsk), hsbyteclk_nsk) - 5;
+ raw_val = tc358768_ns_to_cnt(60 + tc358768_ps_to_ns(2 * ui_ps), hsbyteclk_ps) - 5;
val = clamp(raw_val, 0, 127);
dev_dbg(dev, "TCLK_TRAILCNT: %u\n", val);
tc358768_write(priv, TC358768_TCLK_TRAILCNT, val);
/* 40ns + 4*UI < THS_PREPARE < 85ns + 6*UI */
- val = 50 + tc358768_to_ns(4 * ui_nsk);
- val = tc358768_ns_to_cnt(val, hsbyteclk_nsk) - 1;
+ val = 50 + tc358768_ps_to_ns(4 * ui_ps);
+ val = tc358768_ns_to_cnt(val, hsbyteclk_ps) - 1;
dev_dbg(dev, "THS_PREPARECNT %u\n", val);
/* THS_PREPARE + THS_ZERO > 145ns + 10*UI */
- raw_val = tc358768_ns_to_cnt(145 - tc358768_to_ns(3 * ui_nsk), hsbyteclk_nsk) - 10;
+ raw_val = tc358768_ns_to_cnt(145 - tc358768_ps_to_ns(3 * ui_ps), hsbyteclk_ps) - 10;
val2 = clamp(raw_val, 0, 127);
dev_dbg(dev, "THS_ZEROCNT %u\n", val2);
val |= val2 << 8;
tc358768_write(priv, TC358768_THS_HEADERCNT, val);
/* TWAKEUP > 1ms in lptxcnt steps */
- val = tc358768_ns_to_cnt(1020000, hsbyteclk_nsk);
+ val = tc358768_ns_to_cnt(1020000, hsbyteclk_ps);
val = val / (lptxcnt + 1) - 1;
dev_dbg(dev, "TWAKEUP: %u\n", val);
tc358768_write(priv, TC358768_TWAKEUP, val);
/* TCLK_POSTCNT > 60ns + 52*UI */
- val = tc358768_ns_to_cnt(60 + tc358768_to_ns(52 * ui_nsk),
- hsbyteclk_nsk) - 3;
+ val = tc358768_ns_to_cnt(60 + tc358768_ps_to_ns(52 * ui_ps),
+ hsbyteclk_ps) - 3;
dev_dbg(dev, "TCLK_POSTCNT: %u\n", val);
tc358768_write(priv, TC358768_TCLK_POSTCNT, val);
/* max(60ns + 4*UI, 8*UI) < THS_TRAILCNT < 105ns + 12*UI */
- raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(18 * ui_nsk),
- hsbyteclk_nsk) - 4;
+ raw_val = tc358768_ns_to_cnt(60 + tc358768_ps_to_ns(18 * ui_ps),
+ hsbyteclk_ps) - 4;
val = clamp(raw_val, 0, 15);
dev_dbg(dev, "THS_TRAILCNT: %u\n", val);
tc358768_write(priv, TC358768_THS_TRAILCNT, val);
@@ -804,11 +802,11 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0 : BIT(0));
/* TXTAGOCNT[26:16] RXTASURECNT[10:0] */
- val = tc358768_to_ns((lptxcnt + 1) * hsbyteclk_nsk * 4);
- val = tc358768_ns_to_cnt(val, hsbyteclk_nsk) / 4 - 1;
+ val = tc358768_ps_to_ns((lptxcnt + 1) * hsbyteclk_ps * 4);
+ val = tc358768_ns_to_cnt(val, hsbyteclk_ps) / 4 - 1;
dev_dbg(dev, "TXTAGOCNT: %u\n", val);
- val2 = tc358768_ns_to_cnt(tc358768_to_ns((lptxcnt + 1) * hsbyteclk_nsk),
- hsbyteclk_nsk) - 2;
+ val2 = tc358768_ns_to_cnt(tc358768_ps_to_ns((lptxcnt + 1) * hsbyteclk_ps),
+ hsbyteclk_ps) - 2;
dev_dbg(dev, "RXTASURECNT: %u\n", val2);
val = val << 16 | val2;
tc358768_write(priv, TC358768_BTACNTRL1, val);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 11/12] drm/bridge: tc358768: Fix tc358768_ns_to_cnt()
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
` (9 preceding siblings ...)
2023-08-22 16:19 ` [PATCH v3 10/12] drm/bridge: tc358768: Clean up clock period code Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 12/12] drm/bridge: tc358768: Attempt to fix DSI horizontal timings Tomi Valkeinen
2023-08-29 6:27 ` [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
12 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Tomi Valkeinen, linux-kernel, dri-devel, Aradhya Bhatia
The tc358768_ns_to_cnt() is, most likely, supposed to do a div-round-up
operation, but it misses subtracting one from the dividend.
Fix this by just using DIV_ROUND_UP().
Fixes: ff1ca6397b1d ("drm/bridge: Add tc358768 driver")
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/bridge/tc358768.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index 9ce8d120b50c..f41bf56b7d6b 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -630,7 +630,7 @@ static int tc358768_setup_pll(struct tc358768_priv *priv,
static u32 tc358768_ns_to_cnt(u32 ns, u32 period_ps)
{
- return (ns * 1000 + period_ps) / period_ps;
+ return DIV_ROUND_UP(ns * 1000, period_ps);
}
static u32 tc358768_ps_to_ns(u32 ps)
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v3 12/12] drm/bridge: tc358768: Attempt to fix DSI horizontal timings
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
` (10 preceding siblings ...)
2023-08-22 16:19 ` [PATCH v3 11/12] drm/bridge: tc358768: Fix tc358768_ns_to_cnt() Tomi Valkeinen
@ 2023-08-22 16:19 ` Tomi Valkeinen
2023-09-04 18:46 ` Marcel Ziswiler
2023-08-29 6:27 ` [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
12 siblings, 1 reply; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-22 16:19 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, David Airlie, Daniel Vetter,
Péter Ujfalusi, Maxim Schwalm, Francesco Dolcini
Cc: Tomi Valkeinen, linux-kernel, dri-devel, Aradhya Bhatia
The DSI horizontal timing calculations done by the driver seem to often
lead to underflows or overflows, depending on the videomode.
There are two main things the current driver doesn't seem to get right:
DSI HSW and HFP, and VSDly. However, even following Toshiba's
documentation it seems we don't always get a working display.
This patch attempts to fix the horizontal timings for DSI event mode, and
on a system with a DSI->HDMI encoder, a lot of standard HDMI modes now
seem to work. The work relies on Toshiba's documentation, but also quite
a bit on empirical testing.
This also adds timing related debug prints to make it easier to improve
on this later.
The DSI pulse mode has only been tested with a fixed-resolution panel,
which limits the testing of different modes on DSI pulse mode. However,
as the VSDly calculation also affects pulse mode, so this might cause a
regression.
Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
---
drivers/gpu/drm/bridge/tc358768.c | 211 +++++++++++++++++++++++++++++++++-----
1 file changed, 183 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
index f41bf56b7d6b..b465e0a31d09 100644
--- a/drivers/gpu/drm/bridge/tc358768.c
+++ b/drivers/gpu/drm/bridge/tc358768.c
@@ -9,6 +9,7 @@
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
+#include <linux/math64.h>
#include <linux/media-bus-format.h>
#include <linux/minmax.h>
#include <linux/module.h>
@@ -157,6 +158,7 @@ struct tc358768_priv {
u32 frs; /* PLL Freqency range for HSCK (post divider) */
u32 dsiclk; /* pll_clk / 2 */
+ u32 pclk; /* incoming pclk rate */
};
static inline struct tc358768_priv *dsi_host_to_tc358768(struct mipi_dsi_host
@@ -380,6 +382,7 @@ static int tc358768_calc_pll(struct tc358768_priv *priv,
priv->prd = best_prd;
priv->frs = frs;
priv->dsiclk = best_pll / 2;
+ priv->pclk = mode->clock * 1000;
return 0;
}
@@ -638,6 +641,28 @@ static u32 tc358768_ps_to_ns(u32 ps)
return ps / 1000;
}
+static u32 tc358768_dpi_to_ns(u32 val, u32 pclk)
+{
+ return (u32)div_u64((u64)val * NANO, pclk);
+}
+
+/* Convert value in DPI pixel clock units to DSI byte count */
+static u32 tc358768_dpi_to_dsi_bytes(struct tc358768_priv *priv, u32 val)
+{
+ u64 m = (u64)val * priv->dsiclk / 4 * priv->dsi_lanes;
+ u64 n = priv->pclk;
+
+ return (u32)div_u64(m + n - 1, n);
+}
+
+static u32 tc358768_dsi_bytes_to_ns(struct tc358768_priv *priv, u32 val)
+{
+ u64 m = (u64)val * NANO;
+ u64 n = priv->dsiclk / 4 * priv->dsi_lanes;
+
+ return (u32)div_u64(m, n);
+}
+
static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
{
struct tc358768_priv *priv = bridge_to_tc358768(bridge);
@@ -647,11 +672,19 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
s32 raw_val;
const struct drm_display_mode *mode;
u32 hsbyteclk_ps, dsiclk_ps, ui_ps;
- u32 dsiclk, hsbyteclk, video_start;
- const u32 internal_delay = 40;
+ u32 dsiclk, hsbyteclk;
int ret, i;
struct videomode vm;
struct device *dev = priv->dev;
+ /* In pixelclock units */
+ u32 dpi_htot, dpi_data_start;
+ /* In byte units */
+ u32 dsi_dpi_htot, dsi_dpi_data_start;
+ u32 dsi_hsw, dsi_hbp, dsi_hact, dsi_hfp;
+ const u32 dsi_hss = 4; /* HSS is a short packet (4 bytes) */
+ /* In hsbyteclk units */
+ u32 dsi_vsdly;
+ const u32 internal_dly = 40;
if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
dev_warn_once(dev, "Non-continuous mode unimplemented, falling back to continuous\n");
@@ -686,27 +719,23 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
case MIPI_DSI_FMT_RGB888:
val |= (0x3 << 4);
hact = vm.hactive * 3;
- video_start = (vm.hsync_len + vm.hback_porch) * 3;
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
break;
case MIPI_DSI_FMT_RGB666:
val |= (0x4 << 4);
hact = vm.hactive * 3;
- video_start = (vm.hsync_len + vm.hback_porch) * 3;
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
break;
case MIPI_DSI_FMT_RGB666_PACKED:
val |= (0x4 << 4) | BIT(3);
hact = vm.hactive * 18 / 8;
- video_start = (vm.hsync_len + vm.hback_porch) * 18 / 8;
data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
break;
case MIPI_DSI_FMT_RGB565:
val |= (0x5 << 4);
hact = vm.hactive * 2;
- video_start = (vm.hsync_len + vm.hback_porch) * 2;
data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
break;
default:
@@ -716,9 +745,150 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
return;
}
+ /*
+ * There are three important things to make TC358768 work correctly,
+ * which are not trivial to manage:
+ *
+ * 1. Keep the DPI line-time and the DSI line-time as close to each
+ * other as possible.
+ * 2. TC358768 goes to LP mode after each line's active area. The DSI
+ * HFP period has to be long enough for entering and exiting LP mode.
+ * But it is not clear how to calculate this.
+ * 3. VSDly (video start delay) has to be long enough to ensure that the
+ * DSI TX does not start transmitting util we have started receiving
+ * pixel data from the DPI input. It is not clear how to calculate
+ * this either.
+ */
+
+ dpi_htot = vm.hactive + vm.hfront_porch + vm.hsync_len + vm.hback_porch;
+ dpi_data_start = vm.hsync_len + vm.hback_porch;
+
+ dev_dbg(dev, "dpi horiz timing (pclk): %u + %u + %u + %u = %u\n",
+ vm.hsync_len, vm.hback_porch, vm.hactive, vm.hfront_porch,
+ dpi_htot);
+
+ dev_dbg(dev, "dpi horiz timing (ns): %u + %u + %u + %u = %u\n",
+ tc358768_dpi_to_ns(vm.hsync_len, vm.pixelclock),
+ tc358768_dpi_to_ns(vm.hback_porch, vm.pixelclock),
+ tc358768_dpi_to_ns(vm.hactive, vm.pixelclock),
+ tc358768_dpi_to_ns(vm.hfront_porch, vm.pixelclock),
+ tc358768_dpi_to_ns(dpi_htot, vm.pixelclock));
+
+ dev_dbg(dev, "dpi data start (ns): %u + %u = %u\n",
+ tc358768_dpi_to_ns(vm.hsync_len, vm.pixelclock),
+ tc358768_dpi_to_ns(vm.hback_porch, vm.pixelclock),
+ tc358768_dpi_to_ns(dpi_data_start, vm.pixelclock));
+
+ dsi_dpi_htot = tc358768_dpi_to_dsi_bytes(priv, dpi_htot);
+ dsi_dpi_data_start = tc358768_dpi_to_dsi_bytes(priv, dpi_data_start);
+
+ if (dsi_dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
+ dsi_hsw = tc358768_dpi_to_dsi_bytes(priv, vm.hsync_len);
+ dsi_hbp = tc358768_dpi_to_dsi_bytes(priv, vm.hback_porch);
+ } else {
+ /* HBP is included in HSW in event mode */
+ dsi_hbp = 0;
+ dsi_hsw = tc358768_dpi_to_dsi_bytes(priv,
+ vm.hsync_len + vm.hback_porch);
+
+ /*
+ * The pixel packet includes the actual pixel data, and:
+ * DSI packet header = 4 bytes
+ * DCS code = 1 byte
+ * DSI packet footer = 2 bytes
+ */
+ dsi_hact = hact + 4 + 1 + 2;
+
+ dsi_hfp = dsi_dpi_htot - dsi_hact - dsi_hsw - dsi_hss;
+
+ /*
+ * Here we should check if HFP is long enough for entering LP
+ * and exiting LP, but it's not clear how to calculate that.
+ * Instead, this is a naive algorithm that just adjusts the HFP
+ * and HSW so that HFP is (at least) roughly 2/3 of the total
+ * blanking time.
+ */
+ if (dsi_hfp < (dsi_hfp + dsi_hsw + dsi_hss) * 2 / 3) {
+ u32 old_hfp = dsi_hfp;
+ u32 old_hsw = dsi_hsw;
+ u32 tot = dsi_hfp + dsi_hsw + dsi_hss;
+
+ dsi_hsw = tot / 3;
+
+ /*
+ * Seems like sometimes HSW has to be divisible by num-lanes, but
+ * not always...
+ */
+ dsi_hsw = roundup(dsi_hsw, priv->dsi_lanes);
+
+ dsi_hfp = dsi_dpi_htot - dsi_hact - dsi_hsw - dsi_hss;
+
+ dev_dbg(dev,
+ "hfp too short, adjusting dsi hfp and dsi hsw from %u, %u to %u, %u\n",
+ old_hfp, old_hsw, dsi_hfp, dsi_hsw);
+ }
+
+ dev_dbg(dev,
+ "dsi horiz timing (bytes): %u, %u + %u + %u + %u = %u\n",
+ dsi_hss, dsi_hsw, dsi_hbp, dsi_hact, dsi_hfp,
+ dsi_hss + dsi_hsw + dsi_hbp + dsi_hact + dsi_hfp);
+
+ dev_dbg(dev, "dsi horiz timing (ns): %u + %u + %u + %u + %u = %u\n",
+ tc358768_dsi_bytes_to_ns(priv, dsi_hss),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hsw),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hbp),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hact),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hfp),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hss + dsi_hsw + dsi_hbp + dsi_hact + dsi_hfp));
+ }
+
+ /* VSDly calculation */
+
+ /* Start with the HW internal delay */
+ dsi_vsdly = internal_dly;
+
+ /* Convert to byte units as the other variables are in byte units */
+ dsi_vsdly *= priv->dsi_lanes;
+
+ /* Do we need more delay, in addition to the internal? */
+ if (dsi_dpi_data_start > dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp) {
+ dsi_vsdly = dsi_dpi_data_start - dsi_hss - dsi_hsw - dsi_hbp;
+ dsi_vsdly = roundup(dsi_vsdly, priv->dsi_lanes);
+ }
+
+ dev_dbg(dev, "dsi data start (bytes) %u + %u + %u + %u = %u\n",
+ dsi_vsdly, dsi_hss, dsi_hsw, dsi_hbp,
+ dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp);
+
+ dev_dbg(dev, "dsi data start (ns) %u + %u + %u + %u = %u\n",
+ tc358768_dsi_bytes_to_ns(priv, dsi_vsdly),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hss),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hsw),
+ tc358768_dsi_bytes_to_ns(priv, dsi_hbp),
+ tc358768_dsi_bytes_to_ns(priv, dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp));
+
+ /* Convert back to hsbyteclk */
+ dsi_vsdly /= priv->dsi_lanes;
+
+ /*
+ * The docs say that there is an internal delay of 40 cycles.
+ * However, we get underflows if we follow that rule. If we
+ * instead ignore the internal delay, things work. So either
+ * the docs are wrong or the calculations are wrong.
+ *
+ * As a temporary fix, add the internal delay here, to counter
+ * the subtraction when writing the register.
+ */
+ dsi_vsdly += internal_dly;
+
+ /* Clamp to the register max */
+ if (dsi_vsdly - internal_dly > 0x3ff) {
+ dev_warn(dev, "VSDly too high, underflows likely\n");
+ dsi_vsdly = 0x3ff + internal_dly;
+ }
+
/* VSDly[9:0] */
- video_start = max(video_start, internal_delay + 1) - internal_delay;
- tc358768_write(priv, TC358768_VSDLY, video_start);
+ tc358768_write(priv, TC358768_VSDLY, dsi_vsdly - internal_dly);
tc358768_write(priv, TC358768_DATAFMT, val);
tc358768_write(priv, TC358768_DSITX_DT, data_type);
@@ -826,18 +996,6 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
/* vbp */
tc358768_write(priv, TC358768_DSI_VBPR, vm.vback_porch);
-
- /* hsw * byteclk * ndl / pclk */
- val = (u32)div_u64(vm.hsync_len *
- (u64)hsbyteclk * priv->dsi_lanes,
- vm.pixelclock);
- tc358768_write(priv, TC358768_DSI_HSW, val);
-
- /* hbp * byteclk * ndl / pclk */
- val = (u32)div_u64(vm.hback_porch *
- (u64)hsbyteclk * priv->dsi_lanes,
- vm.pixelclock);
- tc358768_write(priv, TC358768_DSI_HBPR, val);
} else {
/* Set event mode */
tc358768_write(priv, TC358768_DSI_EVENT, 1);
@@ -851,16 +1009,13 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
/* vbp (not used in event mode) */
tc358768_write(priv, TC358768_DSI_VBPR, 0);
+ }
- /* (hsw + hbp) * byteclk * ndl / pclk */
- val = (u32)div_u64((vm.hsync_len + vm.hback_porch) *
- (u64)hsbyteclk * priv->dsi_lanes,
- vm.pixelclock);
- tc358768_write(priv, TC358768_DSI_HSW, val);
+ /* hsw (bytes) */
+ tc358768_write(priv, TC358768_DSI_HSW, dsi_hsw);
- /* hbp (not used in event mode) */
- tc358768_write(priv, TC358768_DSI_HBPR, 0);
- }
+ /* hbp (bytes) */
+ tc358768_write(priv, TC358768_DSI_HBPR, dsi_hbp);
/* hact (bytes) */
tc358768_write(priv, TC358768_DSI_HACT, hact);
--
2.34.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
` (11 preceding siblings ...)
2023-08-22 16:19 ` [PATCH v3 12/12] drm/bridge: tc358768: Attempt to fix DSI horizontal timings Tomi Valkeinen
@ 2023-08-29 6:27 ` Tomi Valkeinen
2023-08-30 20:41 ` Maxim Schwalm
12 siblings, 1 reply; 17+ messages in thread
From: Tomi Valkeinen @ 2023-08-29 6:27 UTC (permalink / raw)
To: Maxim Schwalm
Cc: Neil Armstrong, Jernej Skrabec, Robert Foss, Aradhya Bhatia,
Jonas Karlman, Péter Ujfalusi, linux-kernel, dri-devel,
Thierry Reding, Laurent Pinchart, Andrzej Hajda, Thierry Reding,
Francesco Dolcini
Hi Maxim,
On 22/08/2023 19:19, Tomi Valkeinen wrote:
> This series contains various fixes and cleanups for TC358768. The target
> of this work is to get TC358768 working on Toradex's AM62 based board,
> which has the following display pipeline:
>
> AM62 DPI -> TC358768 -> LT8912B -> HDMI connector
>
> The main thing the series does is to improve the DSI HSW, HFP and VSDly
> calculations.
>
> Tomi
Does this version work for you? Can I add your tested-by?
Tomi
>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> ---
> Changes in v3:
> - Add Peter's reviewed-bys
> - Move "Default to positive h/v syncs" earlier in the series to avoid
> regression in the middle of the series
> - Link to v2: https://lore.kernel.org/r/20230816-tc358768-v2-0-242b9d5f703a@ideasonboard.com
>
> Changes in v2:
> - Add "drm/tegra: rgb: Parameterize V- and H-sync polarities" so that
> Tegra can configure the polarities correctly.
> - Add "drm/bridge: tc358768: Default to positive h/v syncs" as we don't
> (necessarily) have the polarities set in the mode.
> - Drop "drm/bridge: tc358768: Add DRM_BRIDGE_ATTACH_NO_CONNECTOR
> support" as it's not needed for DRM_BRIDGE_ATTACH_NO_CONNECTOR
> support.
> - Link to v1: https://lore.kernel.org/r/20230804-tc358768-v1-0-1afd44b7826b@ideasonboard.com
>
> ---
> Thierry Reding (1):
> drm/tegra: rgb: Parameterize V- and H-sync polarities
>
> Tomi Valkeinen (11):
> drm/bridge: tc358768: Fix use of uninitialized variable
> drm/bridge: tc358768: Default to positive h/v syncs
> drm/bridge: tc358768: Fix bit updates
> drm/bridge: tc358768: Cleanup PLL calculations
> drm/bridge: tc358768: Use struct videomode
> drm/bridge: tc358768: Print logical values, not raw register values
> drm/bridge: tc358768: Use dev for dbg prints, not priv->dev
> drm/bridge: tc358768: Rename dsibclk to hsbyteclk
> drm/bridge: tc358768: Clean up clock period code
> drm/bridge: tc358768: Fix tc358768_ns_to_cnt()
> drm/bridge: tc358768: Attempt to fix DSI horizontal timings
>
> drivers/gpu/drm/bridge/tc358768.c | 381 ++++++++++++++++++++++++++++----------
> drivers/gpu/drm/tegra/rgb.c | 16 +-
> 2 files changed, 295 insertions(+), 102 deletions(-)
> ---
> base-commit: 25205087df1ffe06ccea9302944ed1f77dc68c6f
> change-id: 20230804-tc358768-1b6949ef2e3d
>
> Best regards,
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements
2023-08-29 6:27 ` [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
@ 2023-08-30 20:41 ` Maxim Schwalm
0 siblings, 0 replies; 17+ messages in thread
From: Maxim Schwalm @ 2023-08-30 20:41 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: Neil Armstrong, Jernej Skrabec, Robert Foss, Aradhya Bhatia,
Jonas Karlman, Péter Ujfalusi, linux-kernel, dri-devel,
Thierry Reding, Laurent Pinchart, Andrzej Hajda, Thierry Reding,
Francesco Dolcini
Hi Tomi,
On 29.08.23 08:27, Tomi Valkeinen wrote:
> Hi Maxim,
>
> On 22/08/2023 19:19, Tomi Valkeinen wrote:
>> This series contains various fixes and cleanups for TC358768. The target
>> of this work is to get TC358768 working on Toradex's AM62 based board,
>> which has the following display pipeline:
>>
>> AM62 DPI -> TC358768 -> LT8912B -> HDMI connector
>>
>> The main thing the series does is to improve the DSI HSW, HFP and VSDly
>> calculations.
>>
>> Tomi
>
> Does this version work for you? Can I add your tested-by?
Yes, this series does work fine on the Asus TF700T, so:
Tested-by: Maxim Schwalm <maxim.schwalm@gmail.com> # Asus TF700T
Aside from the TF700T, I have a different kind of tablet, which also has
a TC358768/TC358778 bridge, but it's not running a mainline kernel yet.
So, unfortunately, I can't tell how this patchset fares on it at this
time.
Best regards,
Maxim
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 12/12] drm/bridge: tc358768: Attempt to fix DSI horizontal timings
2023-08-22 16:19 ` [PATCH v3 12/12] drm/bridge: tc358768: Attempt to fix DSI horizontal timings Tomi Valkeinen
@ 2023-09-04 18:46 ` Marcel Ziswiler
2023-09-05 7:28 ` Tomi Valkeinen
0 siblings, 1 reply; 17+ messages in thread
From: Marcel Ziswiler @ 2023-09-04 18:46 UTC (permalink / raw)
To: Laurent.pinchart@ideasonboard.com, andrzej.hajda@intel.com,
neil.armstrong@linaro.org, tomi.valkeinen@ideasonboard.com,
peter.ujfalusi@gmail.com, rfoss@kernel.org, airlied@gmail.com,
francesco@dolcini.it, jonas@kwiboo.se, jernej.skrabec@gmail.com,
daniel@ffwll.ch, maxim.schwalm@gmail.com
Cc: linux-kernel@vger.kernel.org, dri-devel@lists.freedesktop.org,
a-bhatia1@ti.com
Hi Tomi
Looks good. Thanks! Tested both on Verdin AM62 as well as on Verdin iMX8M Mini.
Just a minor nit-pick in your code comment further below.
On Tue, 2023-08-22 at 19:19 +0300, Tomi Valkeinen wrote:
> The DSI horizontal timing calculations done by the driver seem to often
> lead to underflows or overflows, depending on the videomode.
>
> There are two main things the current driver doesn't seem to get right:
> DSI HSW and HFP, and VSDly. However, even following Toshiba's
> documentation it seems we don't always get a working display.
>
> This patch attempts to fix the horizontal timings for DSI event mode, and
> on a system with a DSI->HDMI encoder, a lot of standard HDMI modes now
> seem to work. The work relies on Toshiba's documentation, but also quite
> a bit on empirical testing.
>
> This also adds timing related debug prints to make it easier to improve
> on this later.
>
> The DSI pulse mode has only been tested with a fixed-resolution panel,
> which limits the testing of different modes on DSI pulse mode. However,
> as the VSDly calculation also affects pulse mode, so this might cause a
> regression.
>
> Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
For the whole series:
Tested-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
> ---
> drivers/gpu/drm/bridge/tc358768.c | 211 +++++++++++++++++++++++++++++++++-----
> 1 file changed, 183 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c
> index f41bf56b7d6b..b465e0a31d09 100644
> --- a/drivers/gpu/drm/bridge/tc358768.c
> +++ b/drivers/gpu/drm/bridge/tc358768.c
> @@ -9,6 +9,7 @@
> #include <linux/gpio/consumer.h>
> #include <linux/i2c.h>
> #include <linux/kernel.h>
> +#include <linux/math64.h>
> #include <linux/media-bus-format.h>
> #include <linux/minmax.h>
> #include <linux/module.h>
> @@ -157,6 +158,7 @@ struct tc358768_priv {
> u32 frs; /* PLL Freqency range for HSCK (post divider) */
>
> u32 dsiclk; /* pll_clk / 2 */
> + u32 pclk; /* incoming pclk rate */
> };
>
> static inline struct tc358768_priv *dsi_host_to_tc358768(struct mipi_dsi_host
> @@ -380,6 +382,7 @@ static int tc358768_calc_pll(struct tc358768_priv *priv,
> priv->prd = best_prd;
> priv->frs = frs;
> priv->dsiclk = best_pll / 2;
> + priv->pclk = mode->clock * 1000;
>
> return 0;
> }
> @@ -638,6 +641,28 @@ static u32 tc358768_ps_to_ns(u32 ps)
> return ps / 1000;
> }
>
> +static u32 tc358768_dpi_to_ns(u32 val, u32 pclk)
> +{
> + return (u32)div_u64((u64)val * NANO, pclk);
> +}
> +
> +/* Convert value in DPI pixel clock units to DSI byte count */
> +static u32 tc358768_dpi_to_dsi_bytes(struct tc358768_priv *priv, u32 val)
> +{
> + u64 m = (u64)val * priv->dsiclk / 4 * priv->dsi_lanes;
> + u64 n = priv->pclk;
> +
> + return (u32)div_u64(m + n - 1, n);
> +}
> +
> +static u32 tc358768_dsi_bytes_to_ns(struct tc358768_priv *priv, u32 val)
> +{
> + u64 m = (u64)val * NANO;
> + u64 n = priv->dsiclk / 4 * priv->dsi_lanes;
> +
> + return (u32)div_u64(m, n);
> +}
> +
> static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
> {
> struct tc358768_priv *priv = bridge_to_tc358768(bridge);
> @@ -647,11 +672,19 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
> s32 raw_val;
> const struct drm_display_mode *mode;
> u32 hsbyteclk_ps, dsiclk_ps, ui_ps;
> - u32 dsiclk, hsbyteclk, video_start;
> - const u32 internal_delay = 40;
> + u32 dsiclk, hsbyteclk;
> int ret, i;
> struct videomode vm;
> struct device *dev = priv->dev;
> + /* In pixelclock units */
> + u32 dpi_htot, dpi_data_start;
> + /* In byte units */
> + u32 dsi_dpi_htot, dsi_dpi_data_start;
> + u32 dsi_hsw, dsi_hbp, dsi_hact, dsi_hfp;
> + const u32 dsi_hss = 4; /* HSS is a short packet (4 bytes) */
> + /* In hsbyteclk units */
> + u32 dsi_vsdly;
> + const u32 internal_dly = 40;
>
> if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
> dev_warn_once(dev, "Non-continuous mode unimplemented, falling back to continuous\n");
> @@ -686,27 +719,23 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
> case MIPI_DSI_FMT_RGB888:
> val |= (0x3 << 4);
> hact = vm.hactive * 3;
> - video_start = (vm.hsync_len + vm.hback_porch) * 3;
> data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24;
> break;
> case MIPI_DSI_FMT_RGB666:
> val |= (0x4 << 4);
> hact = vm.hactive * 3;
> - video_start = (vm.hsync_len + vm.hback_porch) * 3;
> data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18;
> break;
>
> case MIPI_DSI_FMT_RGB666_PACKED:
> val |= (0x4 << 4) | BIT(3);
> hact = vm.hactive * 18 / 8;
> - video_start = (vm.hsync_len + vm.hback_porch) * 18 / 8;
> data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18;
> break;
>
> case MIPI_DSI_FMT_RGB565:
> val |= (0x5 << 4);
> hact = vm.hactive * 2;
> - video_start = (vm.hsync_len + vm.hback_porch) * 2;
> data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16;
> break;
> default:
> @@ -716,9 +745,150 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
> return;
> }
>
> + /*
> + * There are three important things to make TC358768 work correctly,
> + * which are not trivial to manage:
> + *
> + * 1. Keep the DPI line-time and the DSI line-time as close to each
> + * other as possible.
> + * 2. TC358768 goes to LP mode after each line's active area. The DSI
> + * HFP period has to be long enough for entering and exiting LP mode.
> + * But it is not clear how to calculate this.
> + * 3. VSDly (video start delay) has to be long enough to ensure that the
> + * DSI TX does not start transmitting util we have started receiving
until we have started receiving
> + * pixel data from the DPI input. It is not clear how to calculate
> + * this either.
> + */
> +
> + dpi_htot = vm.hactive + vm.hfront_porch + vm.hsync_len + vm.hback_porch;
> + dpi_data_start = vm.hsync_len + vm.hback_porch;
> +
> + dev_dbg(dev, "dpi horiz timing (pclk): %u + %u + %u + %u = %u\n",
> + vm.hsync_len, vm.hback_porch, vm.hactive, vm.hfront_porch,
> + dpi_htot);
> +
> + dev_dbg(dev, "dpi horiz timing (ns): %u + %u + %u + %u = %u\n",
> + tc358768_dpi_to_ns(vm.hsync_len, vm.pixelclock),
> + tc358768_dpi_to_ns(vm.hback_porch, vm.pixelclock),
> + tc358768_dpi_to_ns(vm.hactive, vm.pixelclock),
> + tc358768_dpi_to_ns(vm.hfront_porch, vm.pixelclock),
> + tc358768_dpi_to_ns(dpi_htot, vm.pixelclock));
> +
> + dev_dbg(dev, "dpi data start (ns): %u + %u = %u\n",
> + tc358768_dpi_to_ns(vm.hsync_len, vm.pixelclock),
> + tc358768_dpi_to_ns(vm.hback_porch, vm.pixelclock),
> + tc358768_dpi_to_ns(dpi_data_start, vm.pixelclock));
> +
> + dsi_dpi_htot = tc358768_dpi_to_dsi_bytes(priv, dpi_htot);
> + dsi_dpi_data_start = tc358768_dpi_to_dsi_bytes(priv, dpi_data_start);
> +
> + if (dsi_dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
> + dsi_hsw = tc358768_dpi_to_dsi_bytes(priv, vm.hsync_len);
> + dsi_hbp = tc358768_dpi_to_dsi_bytes(priv, vm.hback_porch);
> + } else {
> + /* HBP is included in HSW in event mode */
> + dsi_hbp = 0;
> + dsi_hsw = tc358768_dpi_to_dsi_bytes(priv,
> + vm.hsync_len + vm.hback_porch);
> +
> + /*
> + * The pixel packet includes the actual pixel data, and:
> + * DSI packet header = 4 bytes
> + * DCS code = 1 byte
> + * DSI packet footer = 2 bytes
> + */
> + dsi_hact = hact + 4 + 1 + 2;
> +
> + dsi_hfp = dsi_dpi_htot - dsi_hact - dsi_hsw - dsi_hss;
> +
> + /*
> + * Here we should check if HFP is long enough for entering LP
> + * and exiting LP, but it's not clear how to calculate that.
> + * Instead, this is a naive algorithm that just adjusts the HFP
> + * and HSW so that HFP is (at least) roughly 2/3 of the total
> + * blanking time.
> + */
> + if (dsi_hfp < (dsi_hfp + dsi_hsw + dsi_hss) * 2 / 3) {
> + u32 old_hfp = dsi_hfp;
> + u32 old_hsw = dsi_hsw;
> + u32 tot = dsi_hfp + dsi_hsw + dsi_hss;
> +
> + dsi_hsw = tot / 3;
> +
> + /*
> + * Seems like sometimes HSW has to be divisible by num-lanes, but
> + * not always...
> + */
> + dsi_hsw = roundup(dsi_hsw, priv->dsi_lanes);
> +
> + dsi_hfp = dsi_dpi_htot - dsi_hact - dsi_hsw - dsi_hss;
> +
> + dev_dbg(dev,
> + "hfp too short, adjusting dsi hfp and dsi hsw from %u, %u to %u, %u\n",
> + old_hfp, old_hsw, dsi_hfp, dsi_hsw);
> + }
> +
> + dev_dbg(dev,
> + "dsi horiz timing (bytes): %u, %u + %u + %u + %u = %u\n",
> + dsi_hss, dsi_hsw, dsi_hbp, dsi_hact, dsi_hfp,
> + dsi_hss + dsi_hsw + dsi_hbp + dsi_hact + dsi_hfp);
> +
> + dev_dbg(dev, "dsi horiz timing (ns): %u + %u + %u + %u + %u = %u\n",
> + tc358768_dsi_bytes_to_ns(priv, dsi_hss),
> + tc358768_dsi_bytes_to_ns(priv, dsi_hsw),
> + tc358768_dsi_bytes_to_ns(priv, dsi_hbp),
> + tc358768_dsi_bytes_to_ns(priv, dsi_hact),
> + tc358768_dsi_bytes_to_ns(priv, dsi_hfp),
> + tc358768_dsi_bytes_to_ns(priv, dsi_hss + dsi_hsw + dsi_hbp + dsi_hact + dsi_hfp));
> + }
> +
> + /* VSDly calculation */
> +
> + /* Start with the HW internal delay */
> + dsi_vsdly = internal_dly;
> +
> + /* Convert to byte units as the other variables are in byte units */
> + dsi_vsdly *= priv->dsi_lanes;
> +
> + /* Do we need more delay, in addition to the internal? */
> + if (dsi_dpi_data_start > dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp) {
> + dsi_vsdly = dsi_dpi_data_start - dsi_hss - dsi_hsw - dsi_hbp;
> + dsi_vsdly = roundup(dsi_vsdly, priv->dsi_lanes);
> + }
> +
> + dev_dbg(dev, "dsi data start (bytes) %u + %u + %u + %u = %u\n",
> + dsi_vsdly, dsi_hss, dsi_hsw, dsi_hbp,
> + dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp);
> +
> + dev_dbg(dev, "dsi data start (ns) %u + %u + %u + %u = %u\n",
> + tc358768_dsi_bytes_to_ns(priv, dsi_vsdly),
> + tc358768_dsi_bytes_to_ns(priv, dsi_hss),
> + tc358768_dsi_bytes_to_ns(priv, dsi_hsw),
> + tc358768_dsi_bytes_to_ns(priv, dsi_hbp),
> + tc358768_dsi_bytes_to_ns(priv, dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp));
> +
> + /* Convert back to hsbyteclk */
> + dsi_vsdly /= priv->dsi_lanes;
> +
> + /*
> + * The docs say that there is an internal delay of 40 cycles.
> + * However, we get underflows if we follow that rule. If we
> + * instead ignore the internal delay, things work. So either
> + * the docs are wrong or the calculations are wrong.
> + *
> + * As a temporary fix, add the internal delay here, to counter
> + * the subtraction when writing the register.
> + */
> + dsi_vsdly += internal_dly;
> +
> + /* Clamp to the register max */
> + if (dsi_vsdly - internal_dly > 0x3ff) {
> + dev_warn(dev, "VSDly too high, underflows likely\n");
> + dsi_vsdly = 0x3ff + internal_dly;
> + }
> +
> /* VSDly[9:0] */
> - video_start = max(video_start, internal_delay + 1) - internal_delay;
> - tc358768_write(priv, TC358768_VSDLY, video_start);
> + tc358768_write(priv, TC358768_VSDLY, dsi_vsdly - internal_dly);
>
> tc358768_write(priv, TC358768_DATAFMT, val);
> tc358768_write(priv, TC358768_DSITX_DT, data_type);
> @@ -826,18 +996,6 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
>
> /* vbp */
> tc358768_write(priv, TC358768_DSI_VBPR, vm.vback_porch);
> -
> - /* hsw * byteclk * ndl / pclk */
> - val = (u32)div_u64(vm.hsync_len *
> - (u64)hsbyteclk * priv->dsi_lanes,
> - vm.pixelclock);
> - tc358768_write(priv, TC358768_DSI_HSW, val);
> -
> - /* hbp * byteclk * ndl / pclk */
> - val = (u32)div_u64(vm.hback_porch *
> - (u64)hsbyteclk * priv->dsi_lanes,
> - vm.pixelclock);
> - tc358768_write(priv, TC358768_DSI_HBPR, val);
> } else {
> /* Set event mode */
> tc358768_write(priv, TC358768_DSI_EVENT, 1);
> @@ -851,16 +1009,13 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge)
>
> /* vbp (not used in event mode) */
> tc358768_write(priv, TC358768_DSI_VBPR, 0);
> + }
>
> - /* (hsw + hbp) * byteclk * ndl / pclk */
> - val = (u32)div_u64((vm.hsync_len + vm.hback_porch) *
> - (u64)hsbyteclk * priv->dsi_lanes,
> - vm.pixelclock);
> - tc358768_write(priv, TC358768_DSI_HSW, val);
> + /* hsw (bytes) */
> + tc358768_write(priv, TC358768_DSI_HSW, dsi_hsw);
>
> - /* hbp (not used in event mode) */
> - tc358768_write(priv, TC358768_DSI_HBPR, 0);
> - }
> + /* hbp (bytes) */
> + tc358768_write(priv, TC358768_DSI_HBPR, dsi_hbp);
>
> /* hact (bytes) */
> tc358768_write(priv, TC358768_DSI_HACT, hact);
Cheers
Marcel
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v3 12/12] drm/bridge: tc358768: Attempt to fix DSI horizontal timings
2023-09-04 18:46 ` Marcel Ziswiler
@ 2023-09-05 7:28 ` Tomi Valkeinen
0 siblings, 0 replies; 17+ messages in thread
From: Tomi Valkeinen @ 2023-09-05 7:28 UTC (permalink / raw)
To: Marcel Ziswiler
Cc: neil.armstrong@linaro.org, jernej.skrabec@gmail.com,
rfoss@kernel.org, francesco@dolcini.it, a-bhatia1@ti.com,
jonas@kwiboo.se, linux-kernel@vger.kernel.org,
dri-devel@lists.freedesktop.org,
Laurent.pinchart@ideasonboard.com, andrzej.hajda@intel.com,
maxim.schwalm@gmail.com, peter.ujfalusi@gmail.com
On 04/09/2023 21:46, Marcel Ziswiler wrote:
> Hi Tomi
>
> Looks good. Thanks! Tested both on Verdin AM62 as well as on Verdin iMX8M Mini.
>
> Just a minor nit-pick in your code comment further below.
>
> On Tue, 2023-08-22 at 19:19 +0300, Tomi Valkeinen wrote:
>> The DSI horizontal timing calculations done by the driver seem to often
>> lead to underflows or overflows, depending on the videomode.
>>
>> There are two main things the current driver doesn't seem to get right:
>> DSI HSW and HFP, and VSDly. However, even following Toshiba's
>> documentation it seems we don't always get a working display.
>>
>> This patch attempts to fix the horizontal timings for DSI event mode, and
>> on a system with a DSI->HDMI encoder, a lot of standard HDMI modes now
>> seem to work. The work relies on Toshiba's documentation, but also quite
>> a bit on empirical testing.
>>
>> This also adds timing related debug prints to make it easier to improve
>> on this later.
>>
>> The DSI pulse mode has only been tested with a fixed-resolution panel,
>> which limits the testing of different modes on DSI pulse mode. However,
>> as the VSDly calculation also affects pulse mode, so this might cause a
>> regression.
>>
>> Reviewed-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
>> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
>
> For the whole series:
>
> Tested-by: Marcel Ziswiler <marcel.ziswiler@toradex.com>
Thanks! I have fixed the typo in the comment.
Tomi
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2023-09-05 7:29 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-08-22 16:19 [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 01/12] drm/tegra: rgb: Parameterize V- and H-sync polarities Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 02/12] drm/bridge: tc358768: Fix use of uninitialized variable Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 03/12] drm/bridge: tc358768: Default to positive h/v syncs Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 04/12] drm/bridge: tc358768: Fix bit updates Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 05/12] drm/bridge: tc358768: Cleanup PLL calculations Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 06/12] drm/bridge: tc358768: Use struct videomode Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 07/12] drm/bridge: tc358768: Print logical values, not raw register values Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 08/12] drm/bridge: tc358768: Use dev for dbg prints, not priv->dev Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 09/12] drm/bridge: tc358768: Rename dsibclk to hsbyteclk Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 10/12] drm/bridge: tc358768: Clean up clock period code Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 11/12] drm/bridge: tc358768: Fix tc358768_ns_to_cnt() Tomi Valkeinen
2023-08-22 16:19 ` [PATCH v3 12/12] drm/bridge: tc358768: Attempt to fix DSI horizontal timings Tomi Valkeinen
2023-09-04 18:46 ` Marcel Ziswiler
2023-09-05 7:28 ` Tomi Valkeinen
2023-08-29 6:27 ` [PATCH v3 00/12] drm/bridge: tc358768: Fixes and timings improvements Tomi Valkeinen
2023-08-30 20:41 ` Maxim Schwalm
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.