The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v8 0/2] Decouple max/min_pclk_khz check from constant display
@ 2025-11-04 15:14 Swamil Jain
  2025-11-04 15:14 ` [PATCH v8 1/2] drm/tidss: Remove max_pclk_khz and min_pclk_khz from tidss display features Swamil Jain
  2025-11-04 15:14 ` [PATCH v8 2/2] drm/tidss: Move OLDI mode validation to OLDI bridge mode_valid hook Swamil Jain
  0 siblings, 2 replies; 7+ messages in thread
From: Swamil Jain @ 2025-11-04 15:14 UTC (permalink / raw)
  To: aradhya.bhatia, devarsht, mripard, tomi.valkeinen, jyri.sarha,
	maarten.lankhorst, simona, airlied, tzimmermann, h-shenoy
  Cc: praneeth, u-kumar1, vigneshr, dri-devel, linux-kernel, s-jain1

In an effort to make the existing compatibles more usable, we are
removing the max/min_pclk_khz form dispc_features structure and doing
the supported pixel clock checks using clk_round_rate().

Changes are fully backwards compatible.

- Remove max/min_pclk_khz and use clk_round_rate to validate modes
- Skip OLDI mode validate through dispc_vp_mode_valid and add
  mode_valid hook to validate modes for OLDI bridge in tidss_oldi.c

---
Changelog v7->v8
- Add commit message in PATCH 2/2 to explain the need of skipping
  check_pixel_clock() for VP's having external clocking
- Make check_pixel_clock() inline
- Add newline for code readability
- Pick up R-by tag

v7 patch link:
https://lore.kernel.org/all/20251028033958.369100-1-s-jain1@ti.com/

Changelog v6->v7
- Remove both max/min_pclk_khz as suggested by Tomi
- Use mode_valid() instead of atomic_check()
- Squash patches v6 PATCH 1/3 and PATCH 3/3 which is a fix to OLDI
  series[1]
- Minor fixes in commit message
- Update commit message to include more reasoning to add Fixes tag to
  Patch v7 2/2

[1]: https://lore.kernel.org/all/20250528122544.817829-1-aradhya.bhatia@linux.dev/

v6 patch link:
https://lore.kernel.org/all/20250911110715.2873596-1-s-jain1@ti.com/

Changelog v5->v6
- Drop caching logic
- Add comments for tolerance value
  (We are targeting another series for reducing tolerance value)
- Minor fixes in commit message

v5 patch link:
https://lore.kernel.org/all/20250819192113.2420396-1-s-jain1@ti.com/

Changelog v4->v5
- Rename is_oldi_vp[] to is_ext_vp_clk[]
- Store both pixel clock round_rate and attempted_rate to reduce
  clk_round_rate() calls while validating modes
- Code changes suggested by Tomi[2]
- Minor fixes in comments and commit message

[2]: https://lore.kernel.org/all/8cd9d1c4-2e9f-4766-b224-21925c4f991d@ideasonboard.com/

v4 patch link:
https://lore.kernel.org/all/20250704094851.182131-1-j-choudhary@ti.com/

Changelog v3->v4:
- Minor cosmetic fixes in code, comments and commit message
- Pick up R-by and add Fixes tag

v3 patch link:
https://lore.kernel.org/all/20250701095541.190422-1-j-choudhary@ti.com/

Changelog v2->v3:
- Add changes for OLDI
- Rename max_pclk as it is misleading
- Change commit message to make it more appropriate
- Drop unnecessary zero initialization

v2 patch link:
https://lore.kernel.org/all/20250618100509.20386-1-j-choudhary@ti.com/

Changelog v1->v2:
- Rebase it on linux-next after OLDI support series as all of its
  patches are reviewed and tested and it touches one of the functions
  used.
  
v1 patch link:
https://lore.kernel.org/all/20250618075804.139844-1-j-choudhary@ti.com/

---

Jayesh Choudhary (2):
  drm/tidss: Remove max_pclk_khz and min_pclk_khz from tidss display
    features
  drm/tidss: Move OLDI mode validation to OLDI bridge mode_valid hook

 drivers/gpu/drm/tidss/tidss_dispc.c | 93 ++++++++++++-----------------
 drivers/gpu/drm/tidss/tidss_dispc.h |  3 -
 drivers/gpu/drm/tidss/tidss_drv.h   |  2 +
 drivers/gpu/drm/tidss/tidss_oldi.c  | 22 +++++++
 4 files changed, 62 insertions(+), 58 deletions(-)


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v8 1/2] drm/tidss: Remove max_pclk_khz and min_pclk_khz from tidss display features
  2025-11-04 15:14 [PATCH v8 0/2] Decouple max/min_pclk_khz check from constant display Swamil Jain
@ 2025-11-04 15:14 ` Swamil Jain
  2025-11-12  9:40   ` Tomi Valkeinen
  2025-11-04 15:14 ` [PATCH v8 2/2] drm/tidss: Move OLDI mode validation to OLDI bridge mode_valid hook Swamil Jain
  1 sibling, 1 reply; 7+ messages in thread
From: Swamil Jain @ 2025-11-04 15:14 UTC (permalink / raw)
  To: aradhya.bhatia, devarsht, mripard, tomi.valkeinen, jyri.sarha,
	maarten.lankhorst, simona, airlied, tzimmermann, h-shenoy
  Cc: praneeth, u-kumar1, vigneshr, dri-devel, linux-kernel, s-jain1

From: Jayesh Choudhary <j-choudhary@ti.com>

The TIDSS hardware does not have independent maximum or minimum pixel
clock limits for each video port. Instead, these limits are determined
by the SoC's clock architecture. Previously, this constraint was
modeled using the 'max_pclk_khz' and 'min_pclk_khz' fields in
'dispc_features', but this approach is static and does not account for
the dynamic behavior of PLLs.

This patch removes the 'max_pclk_khz' and 'min_pclk_khz' fields from
'dispc_features'. The correct way to check if a requested mode's pixel
clock is supported is by using 'clk_round_rate()' in the 'mode_valid()'
hook. If the best frequency match for the mode clock falls within the
supported tolerance, it is approved. TIDSS supports a 5% pixel clock
tolerance, which is now reflected in the validation logic.

This change allows existing DSS-compatible drivers to be reused across
SoCs that only differ in their pixel clock characteristics. The
validation uses 'clk_round_rate()' for each mode, which may introduce
additional delay (about 3.5 ms for 30 modes), but this is generally
negligible. Users desiring faster validation may bypass these calls
selectively, for example, checking only the highest resolution mode,
as shown here[1].

[1]: https://lore.kernel.org/all/20250704094851.182131-3-j-choudhary@ti.com/

Tested-by: Michael Walle <mwalle@kernel.org>
Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
Signed-off-by: Swamil Jain <s-jain1@ti.com>
---
 drivers/gpu/drm/tidss/tidss_dispc.c | 86 +++++++++++------------------
 drivers/gpu/drm/tidss/tidss_dispc.h |  3 -
 2 files changed, 31 insertions(+), 58 deletions(-)

diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
index d0b191c470ca..b11880178cba 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.c
+++ b/drivers/gpu/drm/tidss/tidss_dispc.c
@@ -57,12 +57,6 @@ static const u16 tidss_k2g_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
 };
 
 const struct dispc_features dispc_k2g_feats = {
-	.min_pclk_khz = 4375,
-
-	.max_pclk_khz = {
-		[DISPC_VP_DPI] = 150000,
-	},
-
 	/*
 	 * XXX According TRM the RGB input buffer width up to 2560 should
 	 *     work on 3 taps, but in practice it only works up to 1280.
@@ -145,11 +139,6 @@ static const u16 tidss_am65x_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
 };
 
 const struct dispc_features dispc_am65x_feats = {
-	.max_pclk_khz = {
-		[DISPC_VP_DPI] = 165000,
-		[DISPC_VP_OLDI_AM65X] = 165000,
-	},
-
 	.scaling = {
 		.in_width_max_5tap_rgb = 1280,
 		.in_width_max_3tap_rgb = 2560,
@@ -245,11 +234,6 @@ static const u16 tidss_j721e_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
 };
 
 const struct dispc_features dispc_j721e_feats = {
-	.max_pclk_khz = {
-		[DISPC_VP_DPI] = 170000,
-		[DISPC_VP_INTERNAL] = 600000,
-	},
-
 	.scaling = {
 		.in_width_max_5tap_rgb = 2048,
 		.in_width_max_3tap_rgb = 4096,
@@ -316,11 +300,6 @@ const struct dispc_features dispc_j721e_feats = {
 };
 
 const struct dispc_features dispc_am625_feats = {
-	.max_pclk_khz = {
-		[DISPC_VP_DPI] = 165000,
-		[DISPC_VP_INTERNAL] = 170000,
-	},
-
 	.scaling = {
 		.in_width_max_5tap_rgb = 1280,
 		.in_width_max_3tap_rgb = 2560,
@@ -377,15 +356,6 @@ const struct dispc_features dispc_am625_feats = {
 };
 
 const struct dispc_features dispc_am62a7_feats = {
-	/*
-	 * if the code reaches dispc_mode_valid with VP1,
-	 * it should return MODE_BAD.
-	 */
-	.max_pclk_khz = {
-		[DISPC_VP_TIED_OFF] = 0,
-		[DISPC_VP_DPI] = 165000,
-	},
-
 	.scaling = {
 		.in_width_max_5tap_rgb = 1280,
 		.in_width_max_3tap_rgb = 2560,
@@ -442,10 +412,6 @@ const struct dispc_features dispc_am62a7_feats = {
 };
 
 const struct dispc_features dispc_am62l_feats = {
-	.max_pclk_khz = {
-		[DISPC_VP_DPI] = 165000,
-	},
-
 	.subrev = DISPC_AM62L,
 
 	.common = "common",
@@ -1333,33 +1299,54 @@ static void dispc_vp_set_default_color(struct dispc_device *dispc,
 			DISPC_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff);
 }
 
+/*
+ * Calculate the percentage difference between the requested pixel clock rate
+ * and the effective rate resulting from calculating the clock divider value.
+ */
+unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
+{
+	int r = rate / 100, rr = real_rate / 100;
+
+	return (unsigned int)(abs(((rr - r) * 100) / r));
+}
+
+static inline int check_pixel_clock(struct dispc_device *dispc,
+			     u32 hw_videoport, unsigned long clock)
+{
+	unsigned long round_clock;
+
+	round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
+	/*
+	 * To keep the check consistent with dispc_vp_set_clk_rate(), we
+	 * use the same 5% check here.
+	 */
+	if (dispc_pclk_diff(clock, round_clock) > 5)
+		return -EINVAL;
+
+	return 0;
+}
+
 enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc,
 					 u32 hw_videoport,
 					 const struct drm_display_mode *mode)
 {
 	u32 hsw, hfp, hbp, vsw, vfp, vbp;
 	enum dispc_vp_bus_type bus_type;
-	int max_pclk;
 
 	bus_type = dispc->feat->vp_bus_type[hw_videoport];
 
-	max_pclk = dispc->feat->max_pclk_khz[bus_type];
-
-	if (WARN_ON(max_pclk == 0))
+	if (WARN_ON(bus_type == DISPC_VP_TIED_OFF))
 		return MODE_BAD;
 
-	if (mode->clock < dispc->feat->min_pclk_khz)
-		return MODE_CLOCK_LOW;
-
-	if (mode->clock > max_pclk)
-		return MODE_CLOCK_HIGH;
-
 	if (mode->hdisplay > 4096)
 		return MODE_BAD;
 
 	if (mode->vdisplay > 4096)
 		return MODE_BAD;
 
+	if (check_pixel_clock(dispc, hw_videoport, mode->clock * 1000))
+		return MODE_CLOCK_RANGE;
+
 	/* TODO: add interlace support */
 	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
 		return MODE_NO_INTERLACE;
@@ -1423,17 +1410,6 @@ void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport)
 	clk_disable_unprepare(dispc->vp_clk[hw_videoport]);
 }
 
-/*
- * Calculate the percentage difference between the requested pixel clock rate
- * and the effective rate resulting from calculating the clock divider value.
- */
-unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
-{
-	int r = rate / 100, rr = real_rate / 100;
-
-	return (unsigned int)(abs(((rr - r) * 100) / r));
-}
-
 int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
 			  unsigned long rate)
 {
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h
index 60c1b400eb89..42279312dcc1 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.h
+++ b/drivers/gpu/drm/tidss/tidss_dispc.h
@@ -77,9 +77,6 @@ enum dispc_dss_subrevision {
 };
 
 struct dispc_features {
-	int min_pclk_khz;
-	int max_pclk_khz[DISPC_VP_MAX_BUS_TYPE];
-
 	struct dispc_features_scaling scaling;
 
 	enum dispc_dss_subrevision subrev;

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v8 2/2] drm/tidss: Move OLDI mode validation to OLDI bridge mode_valid hook
  2025-11-04 15:14 [PATCH v8 0/2] Decouple max/min_pclk_khz check from constant display Swamil Jain
  2025-11-04 15:14 ` [PATCH v8 1/2] drm/tidss: Remove max_pclk_khz and min_pclk_khz from tidss display features Swamil Jain
@ 2025-11-04 15:14 ` Swamil Jain
  1 sibling, 0 replies; 7+ messages in thread
From: Swamil Jain @ 2025-11-04 15:14 UTC (permalink / raw)
  To: aradhya.bhatia, devarsht, mripard, tomi.valkeinen, jyri.sarha,
	maarten.lankhorst, simona, airlied, tzimmermann, h-shenoy
  Cc: praneeth, u-kumar1, vigneshr, dri-devel, linux-kernel, s-jain1

From: Jayesh Choudhary <j-choudhary@ti.com>

After integrating OLDI support[0], it is necessary to identify which VP
instances use OLDI, since the OLDI driver owns the video port clock
(as a serial clock). Clock operations on these VPs must be delegated to
the OLDI driver, not handled by the TIDSS driver. This issue also
emerged in upstream discussions when DSI-related clock management was
attempted in the TIDSS driver[1].

To address this, add an 'is_ext_vp_clk' array to the 'tidss_device'
structure, marking a VP as 'true' during 'tidss_oldi_init()' and as
'false' during 'tidss_oldi_deinit()'. TIDSS then uses 'is_ext_vp_clk'
to skip clock validation checks in 'dispc_vp_mode_valid()' for VPs
under OLDI control.

Since OLDI uses the DSS VP clock directly as a serial interface and
manages its own rate, mode validation should be implemented in the OLDI
bridge's 'mode_valid' hook. This patch adds that logic, ensuring proper
delegation and avoiding spurious clock handling in the TIDSS driver.

[0]: https://lore.kernel.org/all/20250528122544.817829-1-aradhya.bhatia@linux.dev/
[1]: https://lore.kernel.org/all/DA6TT575Z82D.3MPK8HG5GRL8U@kernel.org/

Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
Tested-by: Michael Walle <mwalle@kernel.org>
Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
Signed-off-by: Swamil Jain <s-jain1@ti.com>
---
 drivers/gpu/drm/tidss/tidss_dispc.c |  7 +++++++
 drivers/gpu/drm/tidss/tidss_drv.h   |  2 ++
 drivers/gpu/drm/tidss/tidss_oldi.c  | 22 ++++++++++++++++++++++
 3 files changed, 31 insertions(+)

diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
index b11880178cba..ea5001311f1a 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.c
+++ b/drivers/gpu/drm/tidss/tidss_dispc.c
@@ -1315,6 +1315,13 @@ static inline int check_pixel_clock(struct dispc_device *dispc,
 {
 	unsigned long round_clock;
 
+	/*
+	 * For VP's with external clocking, clock operations must be
+	 * delegated to respective driver, so we skip the check here.
+	 */
+	if (dispc->tidss->is_ext_vp_clk[hw_videoport])
+		return 0;
+
 	round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
 	/*
 	 * To keep the check consistent with dispc_vp_set_clk_rate(), we
diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h
index 84454a4855d1..e1c1f41d8b4b 100644
--- a/drivers/gpu/drm/tidss/tidss_drv.h
+++ b/drivers/gpu/drm/tidss/tidss_drv.h
@@ -24,6 +24,8 @@ struct tidss_device {
 
 	const struct dispc_features *feat;
 	struct dispc_device *dispc;
+	bool is_ext_vp_clk[TIDSS_MAX_PORTS];
+
 
 	unsigned int num_crtcs;
 	struct drm_crtc *crtcs[TIDSS_MAX_PORTS];
diff --git a/drivers/gpu/drm/tidss/tidss_oldi.c b/drivers/gpu/drm/tidss/tidss_oldi.c
index 7688251beba2..17c535bfa057 100644
--- a/drivers/gpu/drm/tidss/tidss_oldi.c
+++ b/drivers/gpu/drm/tidss/tidss_oldi.c
@@ -309,6 +309,25 @@ static u32 *tidss_oldi_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
 	return input_fmts;
 }
 
+static enum drm_mode_status
+tidss_oldi_mode_valid(struct drm_bridge *bridge,
+		      const struct drm_display_info *info,
+		      const struct drm_display_mode *mode)
+{
+	struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
+	unsigned long round_clock;
+
+	round_clock = clk_round_rate(oldi->serial, mode->clock * 7 * 1000);
+	/*
+	 * To keep the check consistent with dispc_vp_set_clk_rate(),
+	 * we use the same 5% check here.
+	 */
+	if (dispc_pclk_diff(mode->clock * 7 * 1000, round_clock) > 5)
+		return -EINVAL;
+
+	return 0;
+}
+
 static const struct drm_bridge_funcs tidss_oldi_bridge_funcs = {
 	.attach	= tidss_oldi_bridge_attach,
 	.atomic_pre_enable = tidss_oldi_atomic_pre_enable,
@@ -317,6 +336,7 @@ static const struct drm_bridge_funcs tidss_oldi_bridge_funcs = {
 	.atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
 	.atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
 	.atomic_reset = drm_atomic_helper_bridge_reset,
+	.mode_valid = tidss_oldi_mode_valid,
 };
 
 static int get_oldi_mode(struct device_node *oldi_tx, int *companion_instance)
@@ -430,6 +450,7 @@ void tidss_oldi_deinit(struct tidss_device *tidss)
 	for (int i = 0; i < tidss->num_oldis; i++) {
 		if (tidss->oldis[i]) {
 			drm_bridge_remove(&tidss->oldis[i]->bridge);
+			tidss->is_ext_vp_clk[tidss->oldis[i]->parent_vp] = false;
 			tidss->oldis[i] = NULL;
 		}
 	}
@@ -580,6 +601,7 @@ int tidss_oldi_init(struct tidss_device *tidss)
 		oldi->bridge.timings = &default_tidss_oldi_timings;
 
 		tidss->oldis[tidss->num_oldis++] = oldi;
+		tidss->is_ext_vp_clk[oldi->parent_vp] = true;
 		oldi->tidss = tidss;
 
 		drm_bridge_add(&oldi->bridge);

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v8 1/2] drm/tidss: Remove max_pclk_khz and min_pclk_khz from tidss display features
  2025-11-04 15:14 ` [PATCH v8 1/2] drm/tidss: Remove max_pclk_khz and min_pclk_khz from tidss display features Swamil Jain
@ 2025-11-12  9:40   ` Tomi Valkeinen
  2025-11-12  9:43     ` Swamil Jain
  0 siblings, 1 reply; 7+ messages in thread
From: Tomi Valkeinen @ 2025-11-12  9:40 UTC (permalink / raw)
  To: Swamil Jain, aradhya.bhatia, devarsht, mripard, jyri.sarha,
	maarten.lankhorst, simona, airlied, tzimmermann, h-shenoy
  Cc: praneeth, u-kumar1, vigneshr, dri-devel, linux-kernel

Hi,

On 04/11/2025 17:14, Swamil Jain wrote:
> From: Jayesh Choudhary <j-choudhary@ti.com>
> 
> The TIDSS hardware does not have independent maximum or minimum pixel
> clock limits for each video port. Instead, these limits are determined
> by the SoC's clock architecture. Previously, this constraint was
> modeled using the 'max_pclk_khz' and 'min_pclk_khz' fields in
> 'dispc_features', but this approach is static and does not account for
> the dynamic behavior of PLLs.
> 
> This patch removes the 'max_pclk_khz' and 'min_pclk_khz' fields from
> 'dispc_features'. The correct way to check if a requested mode's pixel
> clock is supported is by using 'clk_round_rate()' in the 'mode_valid()'
> hook. If the best frequency match for the mode clock falls within the
> supported tolerance, it is approved. TIDSS supports a 5% pixel clock
> tolerance, which is now reflected in the validation logic.
> 
> This change allows existing DSS-compatible drivers to be reused across
> SoCs that only differ in their pixel clock characteristics. The
> validation uses 'clk_round_rate()' for each mode, which may introduce
> additional delay (about 3.5 ms for 30 modes), but this is generally
> negligible. Users desiring faster validation may bypass these calls
> selectively, for example, checking only the highest resolution mode,
> as shown here[1].
> 
> [1]: https://lore.kernel.org/all/20250704094851.182131-3-j-choudhary@ti.com/
> 
> Tested-by: Michael Walle <mwalle@kernel.org>
> Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
> Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
> Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
> Signed-off-by: Swamil Jain <s-jain1@ti.com>
> ---
>  drivers/gpu/drm/tidss/tidss_dispc.c | 86 +++++++++++------------------
>  drivers/gpu/drm/tidss/tidss_dispc.h |  3 -
>  2 files changed, 31 insertions(+), 58 deletions(-)
> 
> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
> index d0b191c470ca..b11880178cba 100644
> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
> @@ -57,12 +57,6 @@ static const u16 tidss_k2g_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>  };
>  
>  const struct dispc_features dispc_k2g_feats = {
> -	.min_pclk_khz = 4375,
> -
> -	.max_pclk_khz = {
> -		[DISPC_VP_DPI] = 150000,
> -	},
> -
>  	/*
>  	 * XXX According TRM the RGB input buffer width up to 2560 should
>  	 *     work on 3 taps, but in practice it only works up to 1280.
> @@ -145,11 +139,6 @@ static const u16 tidss_am65x_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>  };
>  
>  const struct dispc_features dispc_am65x_feats = {
> -	.max_pclk_khz = {
> -		[DISPC_VP_DPI] = 165000,
> -		[DISPC_VP_OLDI_AM65X] = 165000,
> -	},
> -
>  	.scaling = {
>  		.in_width_max_5tap_rgb = 1280,
>  		.in_width_max_3tap_rgb = 2560,
> @@ -245,11 +234,6 @@ static const u16 tidss_j721e_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>  };
>  
>  const struct dispc_features dispc_j721e_feats = {
> -	.max_pclk_khz = {
> -		[DISPC_VP_DPI] = 170000,
> -		[DISPC_VP_INTERNAL] = 600000,
> -	},
> -
>  	.scaling = {
>  		.in_width_max_5tap_rgb = 2048,
>  		.in_width_max_3tap_rgb = 4096,
> @@ -316,11 +300,6 @@ const struct dispc_features dispc_j721e_feats = {
>  };
>  
>  const struct dispc_features dispc_am625_feats = {
> -	.max_pclk_khz = {
> -		[DISPC_VP_DPI] = 165000,
> -		[DISPC_VP_INTERNAL] = 170000,
> -	},
> -
>  	.scaling = {
>  		.in_width_max_5tap_rgb = 1280,
>  		.in_width_max_3tap_rgb = 2560,
> @@ -377,15 +356,6 @@ const struct dispc_features dispc_am625_feats = {
>  };
>  
>  const struct dispc_features dispc_am62a7_feats = {
> -	/*
> -	 * if the code reaches dispc_mode_valid with VP1,
> -	 * it should return MODE_BAD.
> -	 */
> -	.max_pclk_khz = {
> -		[DISPC_VP_TIED_OFF] = 0,
> -		[DISPC_VP_DPI] = 165000,
> -	},
> -
>  	.scaling = {
>  		.in_width_max_5tap_rgb = 1280,
>  		.in_width_max_3tap_rgb = 2560,
> @@ -442,10 +412,6 @@ const struct dispc_features dispc_am62a7_feats = {
>  };
>  
>  const struct dispc_features dispc_am62l_feats = {
> -	.max_pclk_khz = {
> -		[DISPC_VP_DPI] = 165000,
> -	},
> -
>  	.subrev = DISPC_AM62L,
>  
>  	.common = "common",
> @@ -1333,33 +1299,54 @@ static void dispc_vp_set_default_color(struct dispc_device *dispc,
>  			DISPC_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff);
>  }
>  
> +/*
> + * Calculate the percentage difference between the requested pixel clock rate
> + * and the effective rate resulting from calculating the clock divider value.
> + */
> +unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
> +{
> +	int r = rate / 100, rr = real_rate / 100;
> +
> +	return (unsigned int)(abs(((rr - r) * 100) / r));
> +}
> +
> +static inline int check_pixel_clock(struct dispc_device *dispc,
> +			     u32 hw_videoport, unsigned long clock)
> +{

Ah... Sorry, I was quite unclear in my comment to v7. I did not mean
mark it as inline. I meant "move it inline", i.e. move this code into
the dispc_vp_mode_valid function. This is just a few lines, and having
it in a separate function makes it a bit more difficult to understand
what are all the checks done in dispc_vp_mode_valid().

I can do that change when applying the patches, if that's ok for you.

 Tomi

> +	unsigned long round_clock;
> +
> +	round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
> +	/*
> +	 * To keep the check consistent with dispc_vp_set_clk_rate(), we
> +	 * use the same 5% check here.
> +	 */
> +	if (dispc_pclk_diff(clock, round_clock) > 5)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
>  enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc,
>  					 u32 hw_videoport,
>  					 const struct drm_display_mode *mode)
>  {
>  	u32 hsw, hfp, hbp, vsw, vfp, vbp;
>  	enum dispc_vp_bus_type bus_type;
> -	int max_pclk;
>  
>  	bus_type = dispc->feat->vp_bus_type[hw_videoport];
>  
> -	max_pclk = dispc->feat->max_pclk_khz[bus_type];
> -
> -	if (WARN_ON(max_pclk == 0))
> +	if (WARN_ON(bus_type == DISPC_VP_TIED_OFF))
>  		return MODE_BAD;
>  
> -	if (mode->clock < dispc->feat->min_pclk_khz)
> -		return MODE_CLOCK_LOW;
> -
> -	if (mode->clock > max_pclk)
> -		return MODE_CLOCK_HIGH;
> -
>  	if (mode->hdisplay > 4096)
>  		return MODE_BAD;
>  
>  	if (mode->vdisplay > 4096)
>  		return MODE_BAD;
>  
> +	if (check_pixel_clock(dispc, hw_videoport, mode->clock * 1000))
> +		return MODE_CLOCK_RANGE;
> +
>  	/* TODO: add interlace support */
>  	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
>  		return MODE_NO_INTERLACE;
> @@ -1423,17 +1410,6 @@ void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport)
>  	clk_disable_unprepare(dispc->vp_clk[hw_videoport]);
>  }
>  
> -/*
> - * Calculate the percentage difference between the requested pixel clock rate
> - * and the effective rate resulting from calculating the clock divider value.
> - */
> -unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
> -{
> -	int r = rate / 100, rr = real_rate / 100;
> -
> -	return (unsigned int)(abs(((rr - r) * 100) / r));
> -}
> -
>  int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
>  			  unsigned long rate)
>  {
> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h
> index 60c1b400eb89..42279312dcc1 100644
> --- a/drivers/gpu/drm/tidss/tidss_dispc.h
> +++ b/drivers/gpu/drm/tidss/tidss_dispc.h
> @@ -77,9 +77,6 @@ enum dispc_dss_subrevision {
>  };
>  
>  struct dispc_features {
> -	int min_pclk_khz;
> -	int max_pclk_khz[DISPC_VP_MAX_BUS_TYPE];
> -
>  	struct dispc_features_scaling scaling;
>  
>  	enum dispc_dss_subrevision subrev;


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v8 1/2] drm/tidss: Remove max_pclk_khz and min_pclk_khz from tidss display features
  2025-11-12  9:40   ` Tomi Valkeinen
@ 2025-11-12  9:43     ` Swamil Jain
  2025-11-12  9:51       ` Tomi Valkeinen
  0 siblings, 1 reply; 7+ messages in thread
From: Swamil Jain @ 2025-11-12  9:43 UTC (permalink / raw)
  To: Tomi Valkeinen, aradhya.bhatia, devarsht, mripard, jyri.sarha,
	maarten.lankhorst, simona, airlied, tzimmermann, h-shenoy
  Cc: praneeth, u-kumar1, vigneshr, dri-devel, linux-kernel

Hi Tomi,

On 11/12/25 15:10, Tomi Valkeinen wrote:
> Hi,
> 
> On 04/11/2025 17:14, Swamil Jain wrote:
>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>
>> The TIDSS hardware does not have independent maximum or minimum pixel
>> clock limits for each video port. Instead, these limits are determined
>> by the SoC's clock architecture. Previously, this constraint was
>> modeled using the 'max_pclk_khz' and 'min_pclk_khz' fields in
>> 'dispc_features', but this approach is static and does not account for
>> the dynamic behavior of PLLs.
>>
>> This patch removes the 'max_pclk_khz' and 'min_pclk_khz' fields from
>> 'dispc_features'. The correct way to check if a requested mode's pixel
>> clock is supported is by using 'clk_round_rate()' in the 'mode_valid()'
>> hook. If the best frequency match for the mode clock falls within the
>> supported tolerance, it is approved. TIDSS supports a 5% pixel clock
>> tolerance, which is now reflected in the validation logic.
>>
>> This change allows existing DSS-compatible drivers to be reused across
>> SoCs that only differ in their pixel clock characteristics. The
>> validation uses 'clk_round_rate()' for each mode, which may introduce
>> additional delay (about 3.5 ms for 30 modes), but this is generally
>> negligible. Users desiring faster validation may bypass these calls
>> selectively, for example, checking only the highest resolution mode,
>> as shown here[1].
>>
>> [1]: https://lore.kernel.org/all/20250704094851.182131-3-j-choudhary@ti.com/
>>
>> Tested-by: Michael Walle <mwalle@kernel.org>
>> Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
>> Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
>> Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
>> Signed-off-by: Swamil Jain <s-jain1@ti.com>
>> ---
>>   drivers/gpu/drm/tidss/tidss_dispc.c | 86 +++++++++++------------------
>>   drivers/gpu/drm/tidss/tidss_dispc.h |  3 -
>>   2 files changed, 31 insertions(+), 58 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
>> index d0b191c470ca..b11880178cba 100644
>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>> @@ -57,12 +57,6 @@ static const u16 tidss_k2g_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>>   };
>>   
>>   const struct dispc_features dispc_k2g_feats = {
>> -	.min_pclk_khz = 4375,
>> -
>> -	.max_pclk_khz = {
>> -		[DISPC_VP_DPI] = 150000,
>> -	},
>> -
>>   	/*
>>   	 * XXX According TRM the RGB input buffer width up to 2560 should
>>   	 *     work on 3 taps, but in practice it only works up to 1280.
>> @@ -145,11 +139,6 @@ static const u16 tidss_am65x_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>>   };
>>   
>>   const struct dispc_features dispc_am65x_feats = {
>> -	.max_pclk_khz = {
>> -		[DISPC_VP_DPI] = 165000,
>> -		[DISPC_VP_OLDI_AM65X] = 165000,
>> -	},
>> -
>>   	.scaling = {
>>   		.in_width_max_5tap_rgb = 1280,
>>   		.in_width_max_3tap_rgb = 2560,
>> @@ -245,11 +234,6 @@ static const u16 tidss_j721e_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>>   };
>>   
>>   const struct dispc_features dispc_j721e_feats = {
>> -	.max_pclk_khz = {
>> -		[DISPC_VP_DPI] = 170000,
>> -		[DISPC_VP_INTERNAL] = 600000,
>> -	},
>> -
>>   	.scaling = {
>>   		.in_width_max_5tap_rgb = 2048,
>>   		.in_width_max_3tap_rgb = 4096,
>> @@ -316,11 +300,6 @@ const struct dispc_features dispc_j721e_feats = {
>>   };
>>   
>>   const struct dispc_features dispc_am625_feats = {
>> -	.max_pclk_khz = {
>> -		[DISPC_VP_DPI] = 165000,
>> -		[DISPC_VP_INTERNAL] = 170000,
>> -	},
>> -
>>   	.scaling = {
>>   		.in_width_max_5tap_rgb = 1280,
>>   		.in_width_max_3tap_rgb = 2560,
>> @@ -377,15 +356,6 @@ const struct dispc_features dispc_am625_feats = {
>>   };
>>   
>>   const struct dispc_features dispc_am62a7_feats = {
>> -	/*
>> -	 * if the code reaches dispc_mode_valid with VP1,
>> -	 * it should return MODE_BAD.
>> -	 */
>> -	.max_pclk_khz = {
>> -		[DISPC_VP_TIED_OFF] = 0,
>> -		[DISPC_VP_DPI] = 165000,
>> -	},
>> -
>>   	.scaling = {
>>   		.in_width_max_5tap_rgb = 1280,
>>   		.in_width_max_3tap_rgb = 2560,
>> @@ -442,10 +412,6 @@ const struct dispc_features dispc_am62a7_feats = {
>>   };
>>   
>>   const struct dispc_features dispc_am62l_feats = {
>> -	.max_pclk_khz = {
>> -		[DISPC_VP_DPI] = 165000,
>> -	},
>> -
>>   	.subrev = DISPC_AM62L,
>>   
>>   	.common = "common",
>> @@ -1333,33 +1299,54 @@ static void dispc_vp_set_default_color(struct dispc_device *dispc,
>>   			DISPC_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff);
>>   }
>>   
>> +/*
>> + * Calculate the percentage difference between the requested pixel clock rate
>> + * and the effective rate resulting from calculating the clock divider value.
>> + */
>> +unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
>> +{
>> +	int r = rate / 100, rr = real_rate / 100;
>> +
>> +	return (unsigned int)(abs(((rr - r) * 100) / r));
>> +}
>> +
>> +static inline int check_pixel_clock(struct dispc_device *dispc,
>> +			     u32 hw_videoport, unsigned long clock)
>> +{
> 
> Ah... Sorry, I was quite unclear in my comment to v7. I did not mean
> mark it as inline. I meant "move it inline", i.e. move this code into
> the dispc_vp_mode_valid function. This is just a few lines, and having
> it in a separate function makes it a bit more difficult to understand
> what are all the checks done in dispc_vp_mode_valid().
> 
> I can do that change when applying the patches, if that's ok for you.
> 

Thanks Tomi, please do the required changes.

Regards,
Swamil

>   Tomi
> 
>> +	unsigned long round_clock;
>> +
>> +	round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
>> +	/*
>> +	 * To keep the check consistent with dispc_vp_set_clk_rate(), we
>> +	 * use the same 5% check here.
>> +	 */
>> +	if (dispc_pclk_diff(clock, round_clock) > 5)
>> +		return -EINVAL;
>> +
>> +	return 0;
>> +}
>> +
>>   enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc,
>>   					 u32 hw_videoport,
>>   					 const struct drm_display_mode *mode)
>>   {
>>   	u32 hsw, hfp, hbp, vsw, vfp, vbp;
>>   	enum dispc_vp_bus_type bus_type;
>> -	int max_pclk;
>>   
>>   	bus_type = dispc->feat->vp_bus_type[hw_videoport];
>>   
>> -	max_pclk = dispc->feat->max_pclk_khz[bus_type];
>> -
>> -	if (WARN_ON(max_pclk == 0))
>> +	if (WARN_ON(bus_type == DISPC_VP_TIED_OFF))
>>   		return MODE_BAD;
>>   
>> -	if (mode->clock < dispc->feat->min_pclk_khz)
>> -		return MODE_CLOCK_LOW;
>> -
>> -	if (mode->clock > max_pclk)
>> -		return MODE_CLOCK_HIGH;
>> -
>>   	if (mode->hdisplay > 4096)
>>   		return MODE_BAD;
>>   
>>   	if (mode->vdisplay > 4096)
>>   		return MODE_BAD;
>>   
>> +	if (check_pixel_clock(dispc, hw_videoport, mode->clock * 1000))
>> +		return MODE_CLOCK_RANGE;
>> +
>>   	/* TODO: add interlace support */
>>   	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
>>   		return MODE_NO_INTERLACE;
>> @@ -1423,17 +1410,6 @@ void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport)
>>   	clk_disable_unprepare(dispc->vp_clk[hw_videoport]);
>>   }
>>   
>> -/*
>> - * Calculate the percentage difference between the requested pixel clock rate
>> - * and the effective rate resulting from calculating the clock divider value.
>> - */
>> -unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate)
>> -{
>> -	int r = rate / 100, rr = real_rate / 100;
>> -
>> -	return (unsigned int)(abs(((rr - r) * 100) / r));
>> -}
>> -
>>   int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport,
>>   			  unsigned long rate)
>>   {
>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/tidss/tidss_dispc.h
>> index 60c1b400eb89..42279312dcc1 100644
>> --- a/drivers/gpu/drm/tidss/tidss_dispc.h
>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.h
>> @@ -77,9 +77,6 @@ enum dispc_dss_subrevision {
>>   };
>>   
>>   struct dispc_features {
>> -	int min_pclk_khz;
>> -	int max_pclk_khz[DISPC_VP_MAX_BUS_TYPE];
>> -
>>   	struct dispc_features_scaling scaling;
>>   
>>   	enum dispc_dss_subrevision subrev;
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v8 1/2] drm/tidss: Remove max_pclk_khz and min_pclk_khz from tidss display features
  2025-11-12  9:43     ` Swamil Jain
@ 2025-11-12  9:51       ` Tomi Valkeinen
  2025-11-12 10:00         ` Swamil Jain
  0 siblings, 1 reply; 7+ messages in thread
From: Tomi Valkeinen @ 2025-11-12  9:51 UTC (permalink / raw)
  To: Swamil Jain, aradhya.bhatia, devarsht, mripard, jyri.sarha,
	maarten.lankhorst, simona, airlied, tzimmermann, h-shenoy
  Cc: praneeth, u-kumar1, vigneshr, dri-devel, linux-kernel

Hi,

On 12/11/2025 11:43, Swamil Jain wrote:
> Hi Tomi,
> 
> On 11/12/25 15:10, Tomi Valkeinen wrote:
>> Hi,
>>
>> On 04/11/2025 17:14, Swamil Jain wrote:
>>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>>
>>> The TIDSS hardware does not have independent maximum or minimum pixel
>>> clock limits for each video port. Instead, these limits are determined
>>> by the SoC's clock architecture. Previously, this constraint was
>>> modeled using the 'max_pclk_khz' and 'min_pclk_khz' fields in
>>> 'dispc_features', but this approach is static and does not account for
>>> the dynamic behavior of PLLs.
>>>
>>> This patch removes the 'max_pclk_khz' and 'min_pclk_khz' fields from
>>> 'dispc_features'. The correct way to check if a requested mode's pixel
>>> clock is supported is by using 'clk_round_rate()' in the 'mode_valid()'
>>> hook. If the best frequency match for the mode clock falls within the
>>> supported tolerance, it is approved. TIDSS supports a 5% pixel clock
>>> tolerance, which is now reflected in the validation logic.
>>>
>>> This change allows existing DSS-compatible drivers to be reused across
>>> SoCs that only differ in their pixel clock characteristics. The
>>> validation uses 'clk_round_rate()' for each mode, which may introduce
>>> additional delay (about 3.5 ms for 30 modes), but this is generally
>>> negligible. Users desiring faster validation may bypass these calls
>>> selectively, for example, checking only the highest resolution mode,
>>> as shown here[1].
>>>
>>> [1]: https://lore.kernel.org/all/20250704094851.182131-3-j-
>>> choudhary@ti.com/
>>>
>>> Tested-by: Michael Walle <mwalle@kernel.org>
>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
>>> Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
>>> Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
>>> Signed-off-by: Swamil Jain <s-jain1@ti.com>
>>> ---
>>>   drivers/gpu/drm/tidss/tidss_dispc.c | 86 +++++++++++------------------
>>>   drivers/gpu/drm/tidss/tidss_dispc.h |  3 -
>>>   2 files changed, 31 insertions(+), 58 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/
>>> tidss/tidss_dispc.c
>>> index d0b191c470ca..b11880178cba 100644
>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>>> @@ -57,12 +57,6 @@ static const u16
>>> tidss_k2g_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>>>   };
>>>     const struct dispc_features dispc_k2g_feats = {
>>> -    .min_pclk_khz = 4375,
>>> -
>>> -    .max_pclk_khz = {
>>> -        [DISPC_VP_DPI] = 150000,
>>> -    },
>>> -
>>>       /*
>>>        * XXX According TRM the RGB input buffer width up to 2560 should
>>>        *     work on 3 taps, but in practice it only works up to 1280.
>>> @@ -145,11 +139,6 @@ static const u16
>>> tidss_am65x_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>>>   };
>>>     const struct dispc_features dispc_am65x_feats = {
>>> -    .max_pclk_khz = {
>>> -        [DISPC_VP_DPI] = 165000,
>>> -        [DISPC_VP_OLDI_AM65X] = 165000,
>>> -    },
>>> -
>>>       .scaling = {
>>>           .in_width_max_5tap_rgb = 1280,
>>>           .in_width_max_3tap_rgb = 2560,
>>> @@ -245,11 +234,6 @@ static const u16
>>> tidss_j721e_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>>>   };
>>>     const struct dispc_features dispc_j721e_feats = {
>>> -    .max_pclk_khz = {
>>> -        [DISPC_VP_DPI] = 170000,
>>> -        [DISPC_VP_INTERNAL] = 600000,
>>> -    },
>>> -
>>>       .scaling = {
>>>           .in_width_max_5tap_rgb = 2048,
>>>           .in_width_max_3tap_rgb = 4096,
>>> @@ -316,11 +300,6 @@ const struct dispc_features dispc_j721e_feats = {
>>>   };
>>>     const struct dispc_features dispc_am625_feats = {
>>> -    .max_pclk_khz = {
>>> -        [DISPC_VP_DPI] = 165000,
>>> -        [DISPC_VP_INTERNAL] = 170000,
>>> -    },
>>> -
>>>       .scaling = {
>>>           .in_width_max_5tap_rgb = 1280,
>>>           .in_width_max_3tap_rgb = 2560,
>>> @@ -377,15 +356,6 @@ const struct dispc_features dispc_am625_feats = {
>>>   };
>>>     const struct dispc_features dispc_am62a7_feats = {
>>> -    /*
>>> -     * if the code reaches dispc_mode_valid with VP1,
>>> -     * it should return MODE_BAD.
>>> -     */
>>> -    .max_pclk_khz = {
>>> -        [DISPC_VP_TIED_OFF] = 0,
>>> -        [DISPC_VP_DPI] = 165000,
>>> -    },
>>> -
>>>       .scaling = {
>>>           .in_width_max_5tap_rgb = 1280,
>>>           .in_width_max_3tap_rgb = 2560,
>>> @@ -442,10 +412,6 @@ const struct dispc_features dispc_am62a7_feats = {
>>>   };
>>>     const struct dispc_features dispc_am62l_feats = {
>>> -    .max_pclk_khz = {
>>> -        [DISPC_VP_DPI] = 165000,
>>> -    },
>>> -
>>>       .subrev = DISPC_AM62L,
>>>         .common = "common",
>>> @@ -1333,33 +1299,54 @@ static void dispc_vp_set_default_color(struct
>>> dispc_device *dispc,
>>>               DISPC_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff);
>>>   }
>>>   +/*
>>> + * Calculate the percentage difference between the requested pixel
>>> clock rate
>>> + * and the effective rate resulting from calculating the clock
>>> divider value.
>>> + */
>>> +unsigned int dispc_pclk_diff(unsigned long rate, unsigned long
>>> real_rate)
>>> +{
>>> +    int r = rate / 100, rr = real_rate / 100;
>>> +
>>> +    return (unsigned int)(abs(((rr - r) * 100) / r));
>>> +}
>>> +
>>> +static inline int check_pixel_clock(struct dispc_device *dispc,
>>> +                 u32 hw_videoport, unsigned long clock)
>>> +{
>>
>> Ah... Sorry, I was quite unclear in my comment to v7. I did not mean
>> mark it as inline. I meant "move it inline", i.e. move this code into
>> the dispc_vp_mode_valid function. This is just a few lines, and having
>> it in a separate function makes it a bit more difficult to understand
>> what are all the checks done in dispc_vp_mode_valid().
>>
>> I can do that change when applying the patches, if that's ok for you.
>>
> 
> Thanks Tomi, please do the required changes.

Actually, now that I was about to do the change, I realized the next
patch adds an if check here. So maybe it's better to keep it as a
separate function. I'll just drop the "inline".

 Tomi

> 
> Regards,
> Swamil
> 
>>   Tomi
>>
>>> +    unsigned long round_clock;
>>> +
>>> +    round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
>>> +    /*
>>> +     * To keep the check consistent with dispc_vp_set_clk_rate(), we
>>> +     * use the same 5% check here.
>>> +     */
>>> +    if (dispc_pclk_diff(clock, round_clock) > 5)
>>> +        return -EINVAL;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>   enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc,
>>>                        u32 hw_videoport,
>>>                        const struct drm_display_mode *mode)
>>>   {
>>>       u32 hsw, hfp, hbp, vsw, vfp, vbp;
>>>       enum dispc_vp_bus_type bus_type;
>>> -    int max_pclk;
>>>         bus_type = dispc->feat->vp_bus_type[hw_videoport];
>>>   -    max_pclk = dispc->feat->max_pclk_khz[bus_type];
>>> -
>>> -    if (WARN_ON(max_pclk == 0))
>>> +    if (WARN_ON(bus_type == DISPC_VP_TIED_OFF))
>>>           return MODE_BAD;
>>>   -    if (mode->clock < dispc->feat->min_pclk_khz)
>>> -        return MODE_CLOCK_LOW;
>>> -
>>> -    if (mode->clock > max_pclk)
>>> -        return MODE_CLOCK_HIGH;
>>> -
>>>       if (mode->hdisplay > 4096)
>>>           return MODE_BAD;
>>>         if (mode->vdisplay > 4096)
>>>           return MODE_BAD;
>>>   +    if (check_pixel_clock(dispc, hw_videoport, mode->clock * 1000))
>>> +        return MODE_CLOCK_RANGE;
>>> +
>>>       /* TODO: add interlace support */
>>>       if (mode->flags & DRM_MODE_FLAG_INTERLACE)
>>>           return MODE_NO_INTERLACE;
>>> @@ -1423,17 +1410,6 @@ void dispc_vp_disable_clk(struct dispc_device
>>> *dispc, u32 hw_videoport)
>>>       clk_disable_unprepare(dispc->vp_clk[hw_videoport]);
>>>   }
>>>   -/*
>>> - * Calculate the percentage difference between the requested pixel
>>> clock rate
>>> - * and the effective rate resulting from calculating the clock
>>> divider value.
>>> - */
>>> -unsigned int dispc_pclk_diff(unsigned long rate, unsigned long
>>> real_rate)
>>> -{
>>> -    int r = rate / 100, rr = real_rate / 100;
>>> -
>>> -    return (unsigned int)(abs(((rr - r) * 100) / r));
>>> -}
>>> -
>>>   int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32
>>> hw_videoport,
>>>                 unsigned long rate)
>>>   {
>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/
>>> tidss/tidss_dispc.h
>>> index 60c1b400eb89..42279312dcc1 100644
>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.h
>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.h
>>> @@ -77,9 +77,6 @@ enum dispc_dss_subrevision {
>>>   };
>>>     struct dispc_features {
>>> -    int min_pclk_khz;
>>> -    int max_pclk_khz[DISPC_VP_MAX_BUS_TYPE];
>>> -
>>>       struct dispc_features_scaling scaling;
>>>         enum dispc_dss_subrevision subrev;
>>
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v8 1/2] drm/tidss: Remove max_pclk_khz and min_pclk_khz from tidss display features
  2025-11-12  9:51       ` Tomi Valkeinen
@ 2025-11-12 10:00         ` Swamil Jain
  0 siblings, 0 replies; 7+ messages in thread
From: Swamil Jain @ 2025-11-12 10:00 UTC (permalink / raw)
  To: Tomi Valkeinen, aradhya.bhatia, devarsht, mripard, jyri.sarha,
	maarten.lankhorst, simona, airlied, tzimmermann, h-shenoy
  Cc: praneeth, u-kumar1, vigneshr, dri-devel, linux-kernel

Hi Tomi,

On 11/12/25 15:21, Tomi Valkeinen wrote:
> Hi,
> 
> On 12/11/2025 11:43, Swamil Jain wrote:
>> Hi Tomi,
>>
>> On 11/12/25 15:10, Tomi Valkeinen wrote:
>>> Hi,
>>>
>>> On 04/11/2025 17:14, Swamil Jain wrote:
>>>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>>>
>>>> The TIDSS hardware does not have independent maximum or minimum pixel
>>>> clock limits for each video port. Instead, these limits are determined
>>>> by the SoC's clock architecture. Previously, this constraint was
>>>> modeled using the 'max_pclk_khz' and 'min_pclk_khz' fields in
>>>> 'dispc_features', but this approach is static and does not account for
>>>> the dynamic behavior of PLLs.
>>>>
>>>> This patch removes the 'max_pclk_khz' and 'min_pclk_khz' fields from
>>>> 'dispc_features'. The correct way to check if a requested mode's pixel
>>>> clock is supported is by using 'clk_round_rate()' in the 'mode_valid()'
>>>> hook. If the best frequency match for the mode clock falls within the
>>>> supported tolerance, it is approved. TIDSS supports a 5% pixel clock
>>>> tolerance, which is now reflected in the validation logic.
>>>>
>>>> This change allows existing DSS-compatible drivers to be reused across
>>>> SoCs that only differ in their pixel clock characteristics. The
>>>> validation uses 'clk_round_rate()' for each mode, which may introduce
>>>> additional delay (about 3.5 ms for 30 modes), but this is generally
>>>> negligible. Users desiring faster validation may bypass these calls
>>>> selectively, for example, checking only the highest resolution mode,
>>>> as shown here[1].
>>>>
>>>> [1]: https://lore.kernel.org/all/20250704094851.182131-3-j-
>>>> choudhary@ti.com/
>>>>
>>>> Tested-by: Michael Walle <mwalle@kernel.org>
>>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
>>>> Reviewed-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
>>>> Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
>>>> Signed-off-by: Swamil Jain <s-jain1@ti.com>
>>>> ---
>>>>    drivers/gpu/drm/tidss/tidss_dispc.c | 86 +++++++++++------------------
>>>>    drivers/gpu/drm/tidss/tidss_dispc.h |  3 -
>>>>    2 files changed, 31 insertions(+), 58 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/
>>>> tidss/tidss_dispc.c
>>>> index d0b191c470ca..b11880178cba 100644
>>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>>>> @@ -57,12 +57,6 @@ static const u16
>>>> tidss_k2g_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>>>>    };
>>>>      const struct dispc_features dispc_k2g_feats = {
>>>> -    .min_pclk_khz = 4375,
>>>> -
>>>> -    .max_pclk_khz = {
>>>> -        [DISPC_VP_DPI] = 150000,
>>>> -    },
>>>> -
>>>>        /*
>>>>         * XXX According TRM the RGB input buffer width up to 2560 should
>>>>         *     work on 3 taps, but in practice it only works up to 1280.
>>>> @@ -145,11 +139,6 @@ static const u16
>>>> tidss_am65x_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>>>>    };
>>>>      const struct dispc_features dispc_am65x_feats = {
>>>> -    .max_pclk_khz = {
>>>> -        [DISPC_VP_DPI] = 165000,
>>>> -        [DISPC_VP_OLDI_AM65X] = 165000,
>>>> -    },
>>>> -
>>>>        .scaling = {
>>>>            .in_width_max_5tap_rgb = 1280,
>>>>            .in_width_max_3tap_rgb = 2560,
>>>> @@ -245,11 +234,6 @@ static const u16
>>>> tidss_j721e_common_regs[DISPC_COMMON_REG_TABLE_LEN] = {
>>>>    };
>>>>      const struct dispc_features dispc_j721e_feats = {
>>>> -    .max_pclk_khz = {
>>>> -        [DISPC_VP_DPI] = 170000,
>>>> -        [DISPC_VP_INTERNAL] = 600000,
>>>> -    },
>>>> -
>>>>        .scaling = {
>>>>            .in_width_max_5tap_rgb = 2048,
>>>>            .in_width_max_3tap_rgb = 4096,
>>>> @@ -316,11 +300,6 @@ const struct dispc_features dispc_j721e_feats = {
>>>>    };
>>>>      const struct dispc_features dispc_am625_feats = {
>>>> -    .max_pclk_khz = {
>>>> -        [DISPC_VP_DPI] = 165000,
>>>> -        [DISPC_VP_INTERNAL] = 170000,
>>>> -    },
>>>> -
>>>>        .scaling = {
>>>>            .in_width_max_5tap_rgb = 1280,
>>>>            .in_width_max_3tap_rgb = 2560,
>>>> @@ -377,15 +356,6 @@ const struct dispc_features dispc_am625_feats = {
>>>>    };
>>>>      const struct dispc_features dispc_am62a7_feats = {
>>>> -    /*
>>>> -     * if the code reaches dispc_mode_valid with VP1,
>>>> -     * it should return MODE_BAD.
>>>> -     */
>>>> -    .max_pclk_khz = {
>>>> -        [DISPC_VP_TIED_OFF] = 0,
>>>> -        [DISPC_VP_DPI] = 165000,
>>>> -    },
>>>> -
>>>>        .scaling = {
>>>>            .in_width_max_5tap_rgb = 1280,
>>>>            .in_width_max_3tap_rgb = 2560,
>>>> @@ -442,10 +412,6 @@ const struct dispc_features dispc_am62a7_feats = {
>>>>    };
>>>>      const struct dispc_features dispc_am62l_feats = {
>>>> -    .max_pclk_khz = {
>>>> -        [DISPC_VP_DPI] = 165000,
>>>> -    },
>>>> -
>>>>        .subrev = DISPC_AM62L,
>>>>          .common = "common",
>>>> @@ -1333,33 +1299,54 @@ static void dispc_vp_set_default_color(struct
>>>> dispc_device *dispc,
>>>>                DISPC_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff);
>>>>    }
>>>>    +/*
>>>> + * Calculate the percentage difference between the requested pixel
>>>> clock rate
>>>> + * and the effective rate resulting from calculating the clock
>>>> divider value.
>>>> + */
>>>> +unsigned int dispc_pclk_diff(unsigned long rate, unsigned long
>>>> real_rate)
>>>> +{
>>>> +    int r = rate / 100, rr = real_rate / 100;
>>>> +
>>>> +    return (unsigned int)(abs(((rr - r) * 100) / r));
>>>> +}
>>>> +
>>>> +static inline int check_pixel_clock(struct dispc_device *dispc,
>>>> +                 u32 hw_videoport, unsigned long clock)
>>>> +{
>>>
>>> Ah... Sorry, I was quite unclear in my comment to v7. I did not mean
>>> mark it as inline. I meant "move it inline", i.e. move this code into
>>> the dispc_vp_mode_valid function. This is just a few lines, and having
>>> it in a separate function makes it a bit more difficult to understand
>>> what are all the checks done in dispc_vp_mode_valid().
>>>
>>> I can do that change when applying the patches, if that's ok for you.
>>>
>>
>> Thanks Tomi, please do the required changes.
> 
> Actually, now that I was about to do the change, I realized the next
> patch adds an if check here. So maybe it's better to keep it as a
> separate function. I'll just drop the "inline".

Yeah, I think we can move it inline and accordingly modify the next 
patch also. But I think having a separate function makes it clear that 
we are skipping check for VPs with external clocking and we are having a 
similar check within the bridge driver. Please drop the "inline". Thanks 
for the re-work.

Regards,
Swamil

> 
>   Tomi
> 
>>
>> Regards,
>> Swamil
>>
>>>    Tomi
>>>
>>>> +    unsigned long round_clock;
>>>> +
>>>> +    round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
>>>> +    /*
>>>> +     * To keep the check consistent with dispc_vp_set_clk_rate(), we
>>>> +     * use the same 5% check here.
>>>> +     */
>>>> +    if (dispc_pclk_diff(clock, round_clock) > 5)
>>>> +        return -EINVAL;
>>>> +
>>>> +    return 0;
>>>> +}
>>>> +
>>>>    enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc,
>>>>                         u32 hw_videoport,
>>>>                         const struct drm_display_mode *mode)
>>>>    {
>>>>        u32 hsw, hfp, hbp, vsw, vfp, vbp;
>>>>        enum dispc_vp_bus_type bus_type;
>>>> -    int max_pclk;
>>>>          bus_type = dispc->feat->vp_bus_type[hw_videoport];
>>>>    -    max_pclk = dispc->feat->max_pclk_khz[bus_type];
>>>> -
>>>> -    if (WARN_ON(max_pclk == 0))
>>>> +    if (WARN_ON(bus_type == DISPC_VP_TIED_OFF))
>>>>            return MODE_BAD;
>>>>    -    if (mode->clock < dispc->feat->min_pclk_khz)
>>>> -        return MODE_CLOCK_LOW;
>>>> -
>>>> -    if (mode->clock > max_pclk)
>>>> -        return MODE_CLOCK_HIGH;
>>>> -
>>>>        if (mode->hdisplay > 4096)
>>>>            return MODE_BAD;
>>>>          if (mode->vdisplay > 4096)
>>>>            return MODE_BAD;
>>>>    +    if (check_pixel_clock(dispc, hw_videoport, mode->clock * 1000))
>>>> +        return MODE_CLOCK_RANGE;
>>>> +
>>>>        /* TODO: add interlace support */
>>>>        if (mode->flags & DRM_MODE_FLAG_INTERLACE)
>>>>            return MODE_NO_INTERLACE;
>>>> @@ -1423,17 +1410,6 @@ void dispc_vp_disable_clk(struct dispc_device
>>>> *dispc, u32 hw_videoport)
>>>>        clk_disable_unprepare(dispc->vp_clk[hw_videoport]);
>>>>    }
>>>>    -/*
>>>> - * Calculate the percentage difference between the requested pixel
>>>> clock rate
>>>> - * and the effective rate resulting from calculating the clock
>>>> divider value.
>>>> - */
>>>> -unsigned int dispc_pclk_diff(unsigned long rate, unsigned long
>>>> real_rate)
>>>> -{
>>>> -    int r = rate / 100, rr = real_rate / 100;
>>>> -
>>>> -    return (unsigned int)(abs(((rr - r) * 100) / r));
>>>> -}
>>>> -
>>>>    int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32
>>>> hw_videoport,
>>>>                  unsigned long rate)
>>>>    {
>>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.h b/drivers/gpu/drm/
>>>> tidss/tidss_dispc.h
>>>> index 60c1b400eb89..42279312dcc1 100644
>>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.h
>>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.h
>>>> @@ -77,9 +77,6 @@ enum dispc_dss_subrevision {
>>>>    };
>>>>      struct dispc_features {
>>>> -    int min_pclk_khz;
>>>> -    int max_pclk_khz[DISPC_VP_MAX_BUS_TYPE];
>>>> -
>>>>        struct dispc_features_scaling scaling;
>>>>          enum dispc_dss_subrevision subrev;
>>>
>>
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2025-11-12 10:00 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-04 15:14 [PATCH v8 0/2] Decouple max/min_pclk_khz check from constant display Swamil Jain
2025-11-04 15:14 ` [PATCH v8 1/2] drm/tidss: Remove max_pclk_khz and min_pclk_khz from tidss display features Swamil Jain
2025-11-12  9:40   ` Tomi Valkeinen
2025-11-12  9:43     ` Swamil Jain
2025-11-12  9:51       ` Tomi Valkeinen
2025-11-12 10:00         ` Swamil Jain
2025-11-04 15:14 ` [PATCH v8 2/2] drm/tidss: Move OLDI mode validation to OLDI bridge mode_valid hook Swamil Jain

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox