* [PATCH v5 0/3] Decouple max_pclk check from constant display feats
@ 2025-08-19 19:21 Swamil Jain
2025-08-19 19:21 ` [PATCH v5 1/3] drm/tidss: oldi: Add property to identify OLDI supported VP Swamil Jain
` (3 more replies)
0 siblings, 4 replies; 22+ messages in thread
From: Swamil Jain @ 2025-08-19 19:21 UTC (permalink / raw)
To: jyri.sarha, tomi.valkeinen, maarten.lankhorst, mripard,
tzimmermann, airlied, simona, aradhya.bhatia
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, s-jain1
In an effort to make the existing compatibles more usable, we are
removing the max_pclk_khz form dispc_features structure and doing the
supported pixel clock checks using "max_successful_rate[]" and
"max_attempted_rate[]".
Changes are fully backwards compatible.
After integration of OLDI support[0], we need additional patches in
OLDI to identify the VP that has OLDI. We have to do this since
OLDI driver owns the VP clock (its serial clock) and we cannot perform
clock operations on those VP clock from tidss driver. This issue was
also reported upstream when DSI fixes[1] had some clock related calls
in tidss driver. When "clk_round_rate()" is called, ideally it should
have gone to "sci_clk_determine_rate()" to query DM but it doesn't since
clock is owned by OLDI not tidss.
So add a member is_ext_vp_clk[] in tidss_device structure to identify
this and avoid performing clock operations for VP if it has OLDI panel.
For the same checks in OLDI driver, atomic_check() hook is added to its
bridge_funcs.
In the atomic_check() chain, first the bridge_atomic_check() is called
and then crtc_atomic_check() is called. So mode clock is first checked
in oldi driver and then skipped in tidss driver.
Had the tidss_oldi structure been exposed to tidss_dispc.c, we could
have directly checked VP type in dispc but since the structure is defined
in tidss_oldi.c , we have to add additional member to tidss_device
structure.
[0]: https://lore.kernel.org/all/20250528122544.817829-1-aradhya.bhatia@linux.dev/
[1]: https://lore.kernel.org/all/DA6TT575Z82D.3MPK8HG5GRL8U@kernel.org/
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 (3):
drm/tidss: oldi: Add property to identify OLDI supported VP
drm/tidss: Remove max_pclk_khz from tidss display features
drm/tidss: oldi: Add atomic_check hook for oldi bridge
drivers/gpu/drm/tidss/tidss_dispc.c | 85 +++++++++++++----------------
drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
drivers/gpu/drm/tidss/tidss_drv.h | 11 ++++
drivers/gpu/drm/tidss/tidss_oldi.c | 27 +++++++++
4 files changed, 75 insertions(+), 49 deletions(-)
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v5 1/3] drm/tidss: oldi: Add property to identify OLDI supported VP
2025-08-19 19:21 [PATCH v5 0/3] Decouple max_pclk check from constant display feats Swamil Jain
@ 2025-08-19 19:21 ` Swamil Jain
2025-08-19 19:21 ` [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features Swamil Jain
` (2 subsequent siblings)
3 siblings, 0 replies; 22+ messages in thread
From: Swamil Jain @ 2025-08-19 19:21 UTC (permalink / raw)
To: jyri.sarha, tomi.valkeinen, maarten.lankhorst, mripard,
tzimmermann, airlied, simona, aradhya.bhatia
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, s-jain1
From: Jayesh Choudhary <j-choudhary@ti.com>
TIDSS should know which VP has OLDI output to avoid calling clock
functions for that VP as those are controlled by OLDI driver. Add a
property "is_ext_vp_clk" to "tidss_device" structure for that. Mark it
'true' in tidss_oldi_init() and 'false' in tidss_oldi_deinit().
Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
Signed-off-by: Swamil Jain <s-jain1@ti.com>
---
drivers/gpu/drm/tidss/tidss_drv.h | 2 ++
drivers/gpu/drm/tidss/tidss_oldi.c | 2 ++
2 files changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h
index d14d5d28f0a3..4e38cfa99e84 100644
--- a/drivers/gpu/drm/tidss/tidss_drv.h
+++ b/drivers/gpu/drm/tidss/tidss_drv.h
@@ -22,6 +22,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 8f25159d0666..ef01ecc17a12 100644
--- a/drivers/gpu/drm/tidss/tidss_oldi.c
+++ b/drivers/gpu/drm/tidss/tidss_oldi.c
@@ -430,6 +430,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;
}
}
@@ -581,6 +582,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] 22+ messages in thread
* [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-08-19 19:21 [PATCH v5 0/3] Decouple max_pclk check from constant display feats Swamil Jain
2025-08-19 19:21 ` [PATCH v5 1/3] drm/tidss: oldi: Add property to identify OLDI supported VP Swamil Jain
@ 2025-08-19 19:21 ` Swamil Jain
2025-08-27 8:49 ` Tomi Valkeinen
2025-08-19 19:21 ` [PATCH v5 3/3] drm/tidss: oldi: Add atomic_check hook for oldi bridge Swamil Jain
2025-08-21 12:09 ` [PATCH v5 0/3] Decouple max_pclk check from constant display feats Michael Walle
3 siblings, 1 reply; 22+ messages in thread
From: Swamil Jain @ 2025-08-19 19:21 UTC (permalink / raw)
To: jyri.sarha, tomi.valkeinen, maarten.lankhorst, mripard,
tzimmermann, airlied, simona, aradhya.bhatia
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, s-jain1
From: Jayesh Choudhary <j-choudhary@ti.com>
TIDSS hardware by itself does not have variable max_pclk for each VP.
The maximum pixel clock is determined by the limiting factor between
the functional clock and the PLL (parent to the VP/pixel clock).
The limitation that has been modeled till now comes from the clock
(PLL can only be programmed to a particular max value). Instead of
putting it as a constant field in dispc_features, we can query the
DM to see if requested clock can be set or not and use it in
mode_valid().
Replace constant "max_pclk_khz" in dispc_features with
max_successful_rate and max_attempted_rate, both of these in
tidss_device structure would be modified in runtime. In mode_valid()
call, check if a best frequency match for mode clock can be found or
not using "clk_round_rate()". Based on that, propagate
max_successful_rate and max_attempted_rate and query DM again only if
the requested mode clock is greater than max_attempted_rate. (As the
preferred display mode is usually the max resolution, driver ends up
checking the highest clock the first time itself which is used in
subsequent checks).
Since TIDSS display controller provides clock tolerance of 5%, we use
this while checking the max_successful_rate. Also, move up
"dispc_pclk_diff()" before it is called.
This will make the existing compatibles reusable if DSS features are
same across two SoCs with the only difference being the pixel clock.
Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 +++++++++++++----------------
drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
3 files changed, 47 insertions(+), 50 deletions(-)
diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
index c0277fa36425..c2c0fe0d4a0f 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.c
+++ b/drivers/gpu/drm/tidss/tidss_dispc.c
@@ -58,10 +58,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.
@@ -144,11 +140,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,
@@ -244,11 +235,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,
@@ -315,11 +301,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,
@@ -376,15 +357,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,
@@ -441,10 +413,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",
@@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
+ u32 hw_videoport, unsigned long clock)
+{
+ unsigned long round_clock;
+
+ if (dispc->tidss->is_ext_vp_clk[hw_videoport])
+ return 0;
+
+ if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
+ return 0;
+
+ if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
+ return -EINVAL;
+
+ round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
+
+ if (dispc_pclk_diff(clock, round_clock) > 5)
+ return -EINVAL;
+
+ dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
+ dispc->tidss->max_attempted_rate[hw_videoport] = clock;
+ 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)
+ if (check_pixel_clock(dispc, hw_videoport, mode->clock * 1000))
return MODE_CLOCK_HIGH;
if (mode->hdisplay > 4096)
@@ -1437,17 +1437,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 b8614f62186c..45b1a8aa9089 100644
--- a/drivers/gpu/drm/tidss/tidss_dispc.h
+++ b/drivers/gpu/drm/tidss/tidss_dispc.h
@@ -75,7 +75,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;
diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h
index 4e38cfa99e84..667c0d772519 100644
--- a/drivers/gpu/drm/tidss/tidss_drv.h
+++ b/drivers/gpu/drm/tidss/tidss_drv.h
@@ -23,7 +23,16 @@ struct tidss_device {
const struct dispc_features *feat;
struct dispc_device *dispc;
bool is_ext_vp_clk[TIDSS_MAX_PORTS];
-
+ /*
+ * Stores highest pixel clock value found to be valid while checking
+ * supported modes for connected display
+ */
+ unsigned long max_successful_rate[TIDSS_MAX_PORTS];
+ /*
+ * Stores the highest attempted pixel clock rate whose validated
+ * clock is within the tolerance range
+ */
+ unsigned long max_attempted_rate[TIDSS_MAX_PORTS];
unsigned int num_crtcs;
struct drm_crtc *crtcs[TIDSS_MAX_PORTS];
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH v5 3/3] drm/tidss: oldi: Add atomic_check hook for oldi bridge
2025-08-19 19:21 [PATCH v5 0/3] Decouple max_pclk check from constant display feats Swamil Jain
2025-08-19 19:21 ` [PATCH v5 1/3] drm/tidss: oldi: Add property to identify OLDI supported VP Swamil Jain
2025-08-19 19:21 ` [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features Swamil Jain
@ 2025-08-19 19:21 ` Swamil Jain
2025-08-27 9:05 ` Tomi Valkeinen
2025-08-21 12:09 ` [PATCH v5 0/3] Decouple max_pclk check from constant display feats Michael Walle
3 siblings, 1 reply; 22+ messages in thread
From: Swamil Jain @ 2025-08-19 19:21 UTC (permalink / raw)
To: jyri.sarha, tomi.valkeinen, maarten.lankhorst, mripard,
tzimmermann, airlied, simona, aradhya.bhatia
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, s-jain1
From: Jayesh Choudhary <j-choudhary@ti.com>
Since OLDI consumes DSS VP clock directly as serial clock, certain
checks cannot be performed in tidss driver which should be checked
in OLDI driver. Add check for mode clock and set max_successful_rate
and max_attempted_rate field for tidss in case the VP is OLDI.
Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
Signed-off-by: Swamil Jain <s-jain1@ti.com>
---
drivers/gpu/drm/tidss/tidss_oldi.c | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/drivers/gpu/drm/tidss/tidss_oldi.c b/drivers/gpu/drm/tidss/tidss_oldi.c
index ef01ecc17a12..2ed2d0666ccb 100644
--- a/drivers/gpu/drm/tidss/tidss_oldi.c
+++ b/drivers/gpu/drm/tidss/tidss_oldi.c
@@ -309,6 +309,30 @@ static u32 *tidss_oldi_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
return input_fmts;
}
+static int tidss_oldi_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
+ struct drm_display_mode *adjusted_mode;
+ unsigned long round_clock;
+
+ adjusted_mode = &crtc_state->adjusted_mode;
+
+ if (adjusted_mode->clock > oldi->tidss->max_successful_rate[oldi->parent_vp]) {
+ round_clock = clk_round_rate(oldi->serial, adjusted_mode->clock * 7 * 1000);
+
+ if (dispc_pclk_diff(adjusted_mode->clock * 7 * 1000, round_clock) > 5)
+ return -EINVAL;
+
+ oldi->tidss->max_successful_rate[oldi->parent_vp] = round_clock;
+ oldi->tidss->max_attempted_rate[oldi->parent_vp] = adjusted_mode->clock * 7 * 1000;
+ }
+
+ 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 +341,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,
+ .atomic_check = tidss_oldi_atomic_check,
};
static int get_oldi_mode(struct device_node *oldi_tx, int *companion_instance)
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH v5 0/3] Decouple max_pclk check from constant display feats
2025-08-19 19:21 [PATCH v5 0/3] Decouple max_pclk check from constant display feats Swamil Jain
` (2 preceding siblings ...)
2025-08-19 19:21 ` [PATCH v5 3/3] drm/tidss: oldi: Add atomic_check hook for oldi bridge Swamil Jain
@ 2025-08-21 12:09 ` Michael Walle
2025-08-28 15:59 ` Swamil Jain
3 siblings, 1 reply; 22+ messages in thread
From: Michael Walle @ 2025-08-21 12:09 UTC (permalink / raw)
To: Swamil Jain, jyri.sarha, tomi.valkeinen, maarten.lankhorst,
mripard, tzimmermann, airlied, simona, aradhya.bhatia
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel
[-- Attachment #1: Type: text/plain, Size: 1826 bytes --]
On Tue Aug 19, 2025 at 9:21 PM CEST, Swamil Jain wrote:
> In an effort to make the existing compatibles more usable, we are
> removing the max_pclk_khz form dispc_features structure and doing the
> supported pixel clock checks using "max_successful_rate[]" and
> "max_attempted_rate[]".
>
> Changes are fully backwards compatible.
>
> After integration of OLDI support[0], we need additional patches in
> OLDI to identify the VP that has OLDI. We have to do this since
> OLDI driver owns the VP clock (its serial clock) and we cannot perform
> clock operations on those VP clock from tidss driver. This issue was
> also reported upstream when DSI fixes[1] had some clock related calls
> in tidss driver. When "clk_round_rate()" is called, ideally it should
> have gone to "sci_clk_determine_rate()" to query DM but it doesn't since
> clock is owned by OLDI not tidss.
>
> So add a member is_ext_vp_clk[] in tidss_device structure to identify
> this and avoid performing clock operations for VP if it has OLDI panel.
> For the same checks in OLDI driver, atomic_check() hook is added to its
> bridge_funcs.
> In the atomic_check() chain, first the bridge_atomic_check() is called
> and then crtc_atomic_check() is called. So mode clock is first checked
> in oldi driver and then skipped in tidss driver.
>
> Had the tidss_oldi structure been exposed to tidss_dispc.c, we could
> have directly checked VP type in dispc but since the structure is defined
> in tidss_oldi.c , we have to add additional member to tidss_device
> structure.
>
> [0]: https://lore.kernel.org/all/20250528122544.817829-1-aradhya.bhatia@linux.dev/
> [1]: https://lore.kernel.org/all/DA6TT575Z82D.3MPK8HG5GRL8U@kernel.org/
Since that wasn't picked up from v4:
Tested-by: Michael Walle <mwalle@kernel.org> # on am67a
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 297 bytes --]
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-08-19 19:21 ` [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features Swamil Jain
@ 2025-08-27 8:49 ` Tomi Valkeinen
2025-08-27 9:27 ` Maxime Ripard
2025-08-28 16:44 ` Swamil Jain
0 siblings, 2 replies; 22+ messages in thread
From: Tomi Valkeinen @ 2025-08-27 8:49 UTC (permalink / raw)
To: Swamil Jain
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, jyri.sarha, maarten.lankhorst, mripard, tzimmermann,
airlied, simona, aradhya.bhatia
Hi,
On 19/08/2025 22:21, Swamil Jain wrote:
> From: Jayesh Choudhary <j-choudhary@ti.com>
>
> TIDSS hardware by itself does not have variable max_pclk for each VP.
> The maximum pixel clock is determined by the limiting factor between
> the functional clock and the PLL (parent to the VP/pixel clock).
Hmm, this is actually not in the driver, is it? We're not limiting the
pclk based on the fclk.
> The limitation that has been modeled till now comes from the clock
> (PLL can only be programmed to a particular max value). Instead of
> putting it as a constant field in dispc_features, we can query the
> DM to see if requested clock can be set or not and use it in
> mode_valid().
>
> Replace constant "max_pclk_khz" in dispc_features with
> max_successful_rate and max_attempted_rate, both of these in
> tidss_device structure would be modified in runtime. In mode_valid()
> call, check if a best frequency match for mode clock can be found or
> not using "clk_round_rate()". Based on that, propagate
> max_successful_rate and max_attempted_rate and query DM again only if
> the requested mode clock is greater than max_attempted_rate. (As the
> preferred display mode is usually the max resolution, driver ends up
> checking the highest clock the first time itself which is used in
> subsequent checks).
>
> Since TIDSS display controller provides clock tolerance of 5%, we use
> this while checking the max_successful_rate. Also, move up
> "dispc_pclk_diff()" before it is called.
>
> This will make the existing compatibles reusable if DSS features are
> same across two SoCs with the only difference being the pixel clock.
>
> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
> Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 +++++++++++++----------------
> drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
> drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
> 3 files changed, 47 insertions(+), 50 deletions(-)
>
> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
> index c0277fa36425..c2c0fe0d4a0f 100644
> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
> @@ -58,10 +58,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.
> @@ -144,11 +140,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,
> @@ -244,11 +235,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,
> @@ -315,11 +301,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,
> @@ -376,15 +357,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,
> @@ -441,10 +413,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",
> @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
> + u32 hw_videoport, unsigned long clock)
> +{
> + unsigned long round_clock;
> +
> + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
> + return 0;
> +
> + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
> + return 0;
> +
> + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
> + return -EINVAL;
> +
> + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
> +
> + if (dispc_pclk_diff(clock, round_clock) > 5)
> + return -EINVAL;
> +
> + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
> + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
I still don't think this logic is sound. This is trying to find the
maximum clock rate, and optimize by avoiding the calls to
clk_round_rate() if possible. That makes sense.
But checking for the 5% tolerance breaks it, in my opinion. If we find
out that the PLL can do, say, 100M, but we need pclk of 90M, the current
maximum is still the 100M, isn't it?
Why can't we replace the "if (mode->clock > max_pclk)" check with a new
check that only looks for the max rate? If we want to add tolerance
checks to mode_valid (which are currently not there), let's add it in a
separate patch.
Tomi
> + 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)
> + if (check_pixel_clock(dispc, hw_videoport, mode->clock * 1000))
> return MODE_CLOCK_HIGH;
>
> if (mode->hdisplay > 4096)
> @@ -1437,17 +1437,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 b8614f62186c..45b1a8aa9089 100644
> --- a/drivers/gpu/drm/tidss/tidss_dispc.h
> +++ b/drivers/gpu/drm/tidss/tidss_dispc.h
> @@ -75,7 +75,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;
>
> diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h
> index 4e38cfa99e84..667c0d772519 100644
> --- a/drivers/gpu/drm/tidss/tidss_drv.h
> +++ b/drivers/gpu/drm/tidss/tidss_drv.h
> @@ -23,7 +23,16 @@ struct tidss_device {
> const struct dispc_features *feat;
> struct dispc_device *dispc;
> bool is_ext_vp_clk[TIDSS_MAX_PORTS];
> -
> + /*
> + * Stores highest pixel clock value found to be valid while checking
> + * supported modes for connected display
> + */
> + unsigned long max_successful_rate[TIDSS_MAX_PORTS];
> + /*
> + * Stores the highest attempted pixel clock rate whose validated
> + * clock is within the tolerance range
> + */
> + unsigned long max_attempted_rate[TIDSS_MAX_PORTS];
>
> unsigned int num_crtcs;
> struct drm_crtc *crtcs[TIDSS_MAX_PORTS];
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 3/3] drm/tidss: oldi: Add atomic_check hook for oldi bridge
2025-08-19 19:21 ` [PATCH v5 3/3] drm/tidss: oldi: Add atomic_check hook for oldi bridge Swamil Jain
@ 2025-08-27 9:05 ` Tomi Valkeinen
2025-08-29 3:50 ` Swamil Jain
0 siblings, 1 reply; 22+ messages in thread
From: Tomi Valkeinen @ 2025-08-27 9:05 UTC (permalink / raw)
To: Swamil Jain
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, jyri.sarha, maarten.lankhorst, mripard, tzimmermann,
airlied, simona, aradhya.bhatia
Hi,
On 19/08/2025 22:21, Swamil Jain wrote:
> From: Jayesh Choudhary <j-choudhary@ti.com>
>
> Since OLDI consumes DSS VP clock directly as serial clock, certain
> checks cannot be performed in tidss driver which should be checked
I think this is a bit misleading. The OLDI input clock doesn't come from
DSS, so I wouldn't call it "DSS VP clock". The point here is that the
clock from the PLL is used by both OLDI and DSS, and in the current
architecture the OLDI driver manages the clock, so the DSS driver can't
really do checks, it just has to accept the clock rate. All checks need
to be done in the OLDI driver.
> in OLDI driver. Add check for mode clock and set max_successful_rate
> and max_attempted_rate field for tidss in case the VP is OLDI.
>
> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
> Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
> Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
> Signed-off-by: Swamil Jain <s-jain1@ti.com>
> ---
> drivers/gpu/drm/tidss/tidss_oldi.c | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
>
> diff --git a/drivers/gpu/drm/tidss/tidss_oldi.c b/drivers/gpu/drm/tidss/tidss_oldi.c
> index ef01ecc17a12..2ed2d0666ccb 100644
> --- a/drivers/gpu/drm/tidss/tidss_oldi.c
> +++ b/drivers/gpu/drm/tidss/tidss_oldi.c
> @@ -309,6 +309,30 @@ static u32 *tidss_oldi_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
> return input_fmts;
> }
>
> +static int tidss_oldi_atomic_check(struct drm_bridge *bridge,
> + struct drm_bridge_state *bridge_state,
> + struct drm_crtc_state *crtc_state,
> + struct drm_connector_state *conn_state)
> +{
> + struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
> + struct drm_display_mode *adjusted_mode;
> + unsigned long round_clock;
> +
> + adjusted_mode = &crtc_state->adjusted_mode;
> +
> + if (adjusted_mode->clock > oldi->tidss->max_successful_rate[oldi->parent_vp]) {
You can change the above check to <=, and return 0 here early.
> + round_clock = clk_round_rate(oldi->serial, adjusted_mode->clock * 7 * 1000);
> +
> + if (dispc_pclk_diff(adjusted_mode->clock * 7 * 1000, round_clock) > 5)
> + return -EINVAL;
> +
> + oldi->tidss->max_successful_rate[oldi->parent_vp] = round_clock;
> + oldi->tidss->max_attempted_rate[oldi->parent_vp] = adjusted_mode->clock * 7 * 1000;
> + }
This is not very nice. We should have a function in tidss that we call
here, instead of poking into these tidss's variables directly.
Actually... Do we even need to use the tidss->max_* fields? The above
code is not checking the VP clock maximum, it's actually looking at the
serial clock maximum. Currently those two clocks are linked, though, but
would it make more sense to have the max_* fields here, in OLDI, for
OLDI's serial clock?
Tomi
> +
> + 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 +341,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,
> + .atomic_check = tidss_oldi_atomic_check,
> };
>
> static int get_oldi_mode(struct device_node *oldi_tx, int *companion_instance)
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-08-27 8:49 ` Tomi Valkeinen
@ 2025-08-27 9:27 ` Maxime Ripard
2025-08-27 9:49 ` Tomi Valkeinen
2025-08-29 3:35 ` Swamil Jain
2025-08-28 16:44 ` Swamil Jain
1 sibling, 2 replies; 22+ messages in thread
From: Maxime Ripard @ 2025-08-27 9:27 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: Swamil Jain, h-shenoy, devarsht, vigneshr, praneeth, u-kumar1,
dri-devel, linux-kernel, jyri.sarha, maarten.lankhorst,
tzimmermann, airlied, simona, aradhya.bhatia
[-- Attachment #1: Type: text/plain, Size: 7148 bytes --]
On Wed, Aug 27, 2025 at 11:49:22AM +0300, Tomi Valkeinen wrote:
> On 19/08/2025 22:21, Swamil Jain wrote:
> > From: Jayesh Choudhary <j-choudhary@ti.com>
> >
> > TIDSS hardware by itself does not have variable max_pclk for each VP.
> > The maximum pixel clock is determined by the limiting factor between
> > the functional clock and the PLL (parent to the VP/pixel clock).
>
> Hmm, this is actually not in the driver, is it? We're not limiting the
> pclk based on the fclk.
>
> > The limitation that has been modeled till now comes from the clock
> > (PLL can only be programmed to a particular max value). Instead of
> > putting it as a constant field in dispc_features, we can query the
> > DM to see if requested clock can be set or not and use it in
> > mode_valid().
> >
> > Replace constant "max_pclk_khz" in dispc_features with
> > max_successful_rate and max_attempted_rate, both of these in
> > tidss_device structure would be modified in runtime. In mode_valid()
> > call, check if a best frequency match for mode clock can be found or
> > not using "clk_round_rate()". Based on that, propagate
> > max_successful_rate and max_attempted_rate and query DM again only if
> > the requested mode clock is greater than max_attempted_rate. (As the
> > preferred display mode is usually the max resolution, driver ends up
> > checking the highest clock the first time itself which is used in
> > subsequent checks).
> >
> > Since TIDSS display controller provides clock tolerance of 5%, we use
> > this while checking the max_successful_rate. Also, move up
> > "dispc_pclk_diff()" before it is called.
> >
> > This will make the existing compatibles reusable if DSS features are
> > same across two SoCs with the only difference being the pixel clock.
> >
> > Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
> > Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 +++++++++++++----------------
> > drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
> > drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
> > 3 files changed, 47 insertions(+), 50 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
> > index c0277fa36425..c2c0fe0d4a0f 100644
> > --- a/drivers/gpu/drm/tidss/tidss_dispc.c
> > +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
> > @@ -58,10 +58,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.
> > @@ -144,11 +140,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,
> > @@ -244,11 +235,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,
> > @@ -315,11 +301,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,
> > @@ -376,15 +357,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,
> > @@ -441,10 +413,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",
> > @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
> > + u32 hw_videoport, unsigned long clock)
> > +{
> > + unsigned long round_clock;
> > +
> > + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
> > + return 0;
> > +
> > + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
> > + return 0;
> > +
> > + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
> > + return -EINVAL;
> > +
> > + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
> > +
> > + if (dispc_pclk_diff(clock, round_clock) > 5)
> > + return -EINVAL;
> > +
> > + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
> > + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
>
> I still don't think this logic is sound. This is trying to find the
> maximum clock rate, and optimize by avoiding the calls to
> clk_round_rate() if possible. That makes sense.
>
> But checking for the 5% tolerance breaks it, in my opinion. If we find
> out that the PLL can do, say, 100M, but we need pclk of 90M, the current
> maximum is still the 100M, isn't it?
5% is pretty large indeed. We've been using .5% in multiple drivers and
it proved to be pretty ok. I would advise you tu use it too.
It's not clear to me why avoiding a clk_round_rate() call is something
worth doing though?
Even caching the maximum rate you have been able to reach before is
pretty fragile: if the PLL changes its rate, or if a sibling clock has
set some limits on what the PLL can do, your maximum isn't relevant
anymore.
in other words, what's wrong with simply calling clk_round_rate() and
checking if it's within a .5% deviation?
At the very least, this should be explained in comments or the commit
message.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-08-27 9:27 ` Maxime Ripard
@ 2025-08-27 9:49 ` Tomi Valkeinen
2025-08-27 10:34 ` Maxime Ripard
` (2 more replies)
2025-08-29 3:35 ` Swamil Jain
1 sibling, 3 replies; 22+ messages in thread
From: Tomi Valkeinen @ 2025-08-27 9:49 UTC (permalink / raw)
To: Maxime Ripard
Cc: Swamil Jain, h-shenoy, devarsht, vigneshr, praneeth, u-kumar1,
dri-devel, linux-kernel, jyri.sarha, maarten.lankhorst,
tzimmermann, airlied, simona, aradhya.bhatia
Hi,
On 27/08/2025 12:27, Maxime Ripard wrote:
> On Wed, Aug 27, 2025 at 11:49:22AM +0300, Tomi Valkeinen wrote:
>> On 19/08/2025 22:21, Swamil Jain wrote:
>>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>>
>>> TIDSS hardware by itself does not have variable max_pclk for each VP.
>>> The maximum pixel clock is determined by the limiting factor between
>>> the functional clock and the PLL (parent to the VP/pixel clock).
>>
>> Hmm, this is actually not in the driver, is it? We're not limiting the
>> pclk based on the fclk.
>>
>>> The limitation that has been modeled till now comes from the clock
>>> (PLL can only be programmed to a particular max value). Instead of
>>> putting it as a constant field in dispc_features, we can query the
>>> DM to see if requested clock can be set or not and use it in
>>> mode_valid().
>>>
>>> Replace constant "max_pclk_khz" in dispc_features with
>>> max_successful_rate and max_attempted_rate, both of these in
>>> tidss_device structure would be modified in runtime. In mode_valid()
>>> call, check if a best frequency match for mode clock can be found or
>>> not using "clk_round_rate()". Based on that, propagate
>>> max_successful_rate and max_attempted_rate and query DM again only if
>>> the requested mode clock is greater than max_attempted_rate. (As the
>>> preferred display mode is usually the max resolution, driver ends up
>>> checking the highest clock the first time itself which is used in
>>> subsequent checks).
>>>
>>> Since TIDSS display controller provides clock tolerance of 5%, we use
>>> this while checking the max_successful_rate. Also, move up
>>> "dispc_pclk_diff()" before it is called.
>>>
>>> This will make the existing compatibles reusable if DSS features are
>>> same across two SoCs with the only difference being the pixel clock.
>>>
>>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 +++++++++++++----------------
>>> drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
>>> drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
>>> 3 files changed, 47 insertions(+), 50 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
>>> index c0277fa36425..c2c0fe0d4a0f 100644
>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>>> @@ -58,10 +58,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.
>>> @@ -144,11 +140,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,
>>> @@ -244,11 +235,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,
>>> @@ -315,11 +301,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,
>>> @@ -376,15 +357,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,
>>> @@ -441,10 +413,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",
>>> @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
>>> + u32 hw_videoport, unsigned long clock)
>>> +{
>>> + unsigned long round_clock;
>>> +
>>> + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
>>> + return 0;
>>> +
>>> + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
>>> + return 0;
>>> +
>>> + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
>>> + return -EINVAL;
>>> +
>>> + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
>>> +
>>> + if (dispc_pclk_diff(clock, round_clock) > 5)
>>> + return -EINVAL;
>>> +
>>> + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
>>> + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
>>
>> I still don't think this logic is sound. This is trying to find the
>> maximum clock rate, and optimize by avoiding the calls to
>> clk_round_rate() if possible. That makes sense.
>>
>> But checking for the 5% tolerance breaks it, in my opinion. If we find
>> out that the PLL can do, say, 100M, but we need pclk of 90M, the current
>> maximum is still the 100M, isn't it?
>
> 5% is pretty large indeed. We've been using .5% in multiple drivers and
> it proved to be pretty ok. I would advise you tu use it too.
The 5% comes from OMAP DSS, where we had to do pixel clock with a few
dividers and multipliers. The rates were quite coarse, and we ended up
having quite a large tolerance.
I think with tidss, we always have a PLL we control, so we should always
have very exact clocks. So I'm fine with dropping it to .5%. However,
this patch and series is about removing the a-bit-too-hardcoded VP clk
max rate code in the driver, so I would leave everything else to another
series.
> It's not clear to me why avoiding a clk_round_rate() call is something
> worth doing though?
Hard to say if it's worth doing, someone should make some perf tests.
However, afaik, the calls do go to the firmware, so it involves
inter-processor calls. On OMAP DSS checking the clock rates was slow, as
it involved lots of iterating with dividers and multipliers. Perhaps
it's much faster here.
> Even caching the maximum rate you have been able to reach before is
> pretty fragile: if the PLL changes its rate, or if a sibling clock has
> set some limits on what the PLL can do, your maximum isn't relevant
> anymore.
You're right, although afaik it should not happen with TI's SoCs. We
would be in trouble anyway if that were the case (e.g. someone starts
the camera, and suddenly we can't support 1080p anymore).
> in other words, what's wrong with simply calling clk_round_rate() and
> checking if it's within a .5% deviation?
This started with discussions how to replace the hardcoded max VP clock
rate (used to quickly weed out impossible rates), which in reality was
actually PLL max clock rate. We don't know the PLL max rate, and can't
query it, so this approach was taken.
> At the very least, this should be explained in comments or the commit
> message.
I agree.
Swamil, can you do some perf tests with clk_round_rate()? If it's fast
(enough), it will simplify the driver.
Tomi
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-08-27 9:49 ` Tomi Valkeinen
@ 2025-08-27 10:34 ` Maxime Ripard
2025-08-27 10:39 ` Tomi Valkeinen
2025-08-29 3:37 ` Swamil Jain
2025-09-03 8:38 ` Swamil Jain
2 siblings, 1 reply; 22+ messages in thread
From: Maxime Ripard @ 2025-08-27 10:34 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: Swamil Jain, h-shenoy, devarsht, vigneshr, praneeth, u-kumar1,
dri-devel, linux-kernel, jyri.sarha, maarten.lankhorst,
tzimmermann, airlied, simona, aradhya.bhatia
[-- Attachment #1: Type: text/plain, Size: 9685 bytes --]
On Wed, Aug 27, 2025 at 12:49:37PM +0300, Tomi Valkeinen wrote:
> On 27/08/2025 12:27, Maxime Ripard wrote:
> > On Wed, Aug 27, 2025 at 11:49:22AM +0300, Tomi Valkeinen wrote:
> >> On 19/08/2025 22:21, Swamil Jain wrote:
> >>> From: Jayesh Choudhary <j-choudhary@ti.com>
> >>>
> >>> TIDSS hardware by itself does not have variable max_pclk for each VP.
> >>> The maximum pixel clock is determined by the limiting factor between
> >>> the functional clock and the PLL (parent to the VP/pixel clock).
> >>
> >> Hmm, this is actually not in the driver, is it? We're not limiting the
> >> pclk based on the fclk.
> >>
> >>> The limitation that has been modeled till now comes from the clock
> >>> (PLL can only be programmed to a particular max value). Instead of
> >>> putting it as a constant field in dispc_features, we can query the
> >>> DM to see if requested clock can be set or not and use it in
> >>> mode_valid().
> >>>
> >>> Replace constant "max_pclk_khz" in dispc_features with
> >>> max_successful_rate and max_attempted_rate, both of these in
> >>> tidss_device structure would be modified in runtime. In mode_valid()
> >>> call, check if a best frequency match for mode clock can be found or
> >>> not using "clk_round_rate()". Based on that, propagate
> >>> max_successful_rate and max_attempted_rate and query DM again only if
> >>> the requested mode clock is greater than max_attempted_rate. (As the
> >>> preferred display mode is usually the max resolution, driver ends up
> >>> checking the highest clock the first time itself which is used in
> >>> subsequent checks).
> >>>
> >>> Since TIDSS display controller provides clock tolerance of 5%, we use
> >>> this while checking the max_successful_rate. Also, move up
> >>> "dispc_pclk_diff()" before it is called.
> >>>
> >>> This will make the existing compatibles reusable if DSS features are
> >>> same across two SoCs with the only difference being the pixel clock.
> >>>
> >>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
> >>> Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 +++++++++++++----------------
> >>> drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
> >>> drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
> >>> 3 files changed, 47 insertions(+), 50 deletions(-)
> >>>
> >>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
> >>> index c0277fa36425..c2c0fe0d4a0f 100644
> >>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
> >>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
> >>> @@ -58,10 +58,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.
> >>> @@ -144,11 +140,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,
> >>> @@ -244,11 +235,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,
> >>> @@ -315,11 +301,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,
> >>> @@ -376,15 +357,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,
> >>> @@ -441,10 +413,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",
> >>> @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
> >>> + u32 hw_videoport, unsigned long clock)
> >>> +{
> >>> + unsigned long round_clock;
> >>> +
> >>> + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
> >>> + return 0;
> >>> +
> >>> + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
> >>> + return 0;
> >>> +
> >>> + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
> >>> + return -EINVAL;
> >>> +
> >>> + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
> >>> +
> >>> + if (dispc_pclk_diff(clock, round_clock) > 5)
> >>> + return -EINVAL;
> >>> +
> >>> + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
> >>> + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
> >>
> >> I still don't think this logic is sound. This is trying to find the
> >> maximum clock rate, and optimize by avoiding the calls to
> >> clk_round_rate() if possible. That makes sense.
> >>
> >> But checking for the 5% tolerance breaks it, in my opinion. If we find
> >> out that the PLL can do, say, 100M, but we need pclk of 90M, the current
> >> maximum is still the 100M, isn't it?
> >
> > 5% is pretty large indeed. We've been using .5% in multiple drivers and
> > it proved to be pretty ok. I would advise you tu use it too.
>
> The 5% comes from OMAP DSS, where we had to do pixel clock with a few
> dividers and multipliers. The rates were quite coarse, and we ended up
> having quite a large tolerance.
>
> I think with tidss, we always have a PLL we control, so we should always
> have very exact clocks. So I'm fine with dropping it to .5%. However,
> this patch and series is about removing the a-bit-too-hardcoded VP clk
> max rate code in the driver, so I would leave everything else to another
> series.
Ack
> > It's not clear to me why avoiding a clk_round_rate() call is something
> > worth doing though?
>
> Hard to say if it's worth doing, someone should make some perf tests.
> However, afaik, the calls do go to the firmware, so it involves
> inter-processor calls. On OMAP DSS checking the clock rates was slow, as
> it involved lots of iterating with dividers and multipliers. Perhaps
> it's much faster here.
It's not like it's going to be called a lot anyway. It's called once for
each mode when EDID are read (using an I2C bus), and then once per
commit that change the mode.
Both operations are super slow already, so I'm pretty sure you wouldn't
be able to tell.
> > Even caching the maximum rate you have been able to reach before is
> > pretty fragile: if the PLL changes its rate, or if a sibling clock has
> > set some limits on what the PLL can do, your maximum isn't relevant
> > anymore.
>
> You're right, although afaik it should not happen with TI's SoCs. We
> would be in trouble anyway if that were the case (e.g. someone starts
> the camera, and suddenly we can't support 1080p anymore).
>
> > in other words, what's wrong with simply calling clk_round_rate() and
> > checking if it's within a .5% deviation?
>
> This started with discussions how to replace the hardcoded max VP clock
> rate (used to quickly weed out impossible rates), which in reality was
> actually PLL max clock rate. We don't know the PLL max rate, and can't
> query it, so this approach was taken.
If it's fixed by the platform, you have clk_get_max_rate(), but also,
does it really matter?
I mean, you don't really care about the max, you care whether you can
have a clock matching the expected pixel clock. Whether it's too low,
too high, or just that it doesn't want to doesn't matter if you can't:
you should just reject that mode.
It does matter if you try to optimize things and avoid extra
clk_round_rate() calls, but realistically speaking, for the OLDI that
drives panel afaik, you're only going to consider a handful of modes.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-08-27 10:34 ` Maxime Ripard
@ 2025-08-27 10:39 ` Tomi Valkeinen
2025-08-27 11:25 ` Maxime Ripard
0 siblings, 1 reply; 22+ messages in thread
From: Tomi Valkeinen @ 2025-08-27 10:39 UTC (permalink / raw)
To: Maxime Ripard
Cc: Swamil Jain, h-shenoy, devarsht, vigneshr, praneeth, u-kumar1,
dri-devel, linux-kernel, jyri.sarha, maarten.lankhorst,
tzimmermann, airlied, simona, aradhya.bhatia
Hi,
On 27/08/2025 13:34, Maxime Ripard wrote:
> On Wed, Aug 27, 2025 at 12:49:37PM +0300, Tomi Valkeinen wrote:
>> On 27/08/2025 12:27, Maxime Ripard wrote:
>>> On Wed, Aug 27, 2025 at 11:49:22AM +0300, Tomi Valkeinen wrote:
>>>> On 19/08/2025 22:21, Swamil Jain wrote:
>>>>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>>>>
>>>>> TIDSS hardware by itself does not have variable max_pclk for each VP.
>>>>> The maximum pixel clock is determined by the limiting factor between
>>>>> the functional clock and the PLL (parent to the VP/pixel clock).
>>>>
>>>> Hmm, this is actually not in the driver, is it? We're not limiting the
>>>> pclk based on the fclk.
>>>>
>>>>> The limitation that has been modeled till now comes from the clock
>>>>> (PLL can only be programmed to a particular max value). Instead of
>>>>> putting it as a constant field in dispc_features, we can query the
>>>>> DM to see if requested clock can be set or not and use it in
>>>>> mode_valid().
>>>>>
>>>>> Replace constant "max_pclk_khz" in dispc_features with
>>>>> max_successful_rate and max_attempted_rate, both of these in
>>>>> tidss_device structure would be modified in runtime. In mode_valid()
>>>>> call, check if a best frequency match for mode clock can be found or
>>>>> not using "clk_round_rate()". Based on that, propagate
>>>>> max_successful_rate and max_attempted_rate and query DM again only if
>>>>> the requested mode clock is greater than max_attempted_rate. (As the
>>>>> preferred display mode is usually the max resolution, driver ends up
>>>>> checking the highest clock the first time itself which is used in
>>>>> subsequent checks).
>>>>>
>>>>> Since TIDSS display controller provides clock tolerance of 5%, we use
>>>>> this while checking the max_successful_rate. Also, move up
>>>>> "dispc_pclk_diff()" before it is called.
>>>>>
>>>>> This will make the existing compatibles reusable if DSS features are
>>>>> same across two SoCs with the only difference being the pixel clock.
>>>>>
>>>>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
>>>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 +++++++++++++----------------
>>>>> drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
>>>>> drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
>>>>> 3 files changed, 47 insertions(+), 50 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
>>>>> index c0277fa36425..c2c0fe0d4a0f 100644
>>>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>>>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>>>>> @@ -58,10 +58,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.
>>>>> @@ -144,11 +140,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,
>>>>> @@ -244,11 +235,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,
>>>>> @@ -315,11 +301,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,
>>>>> @@ -376,15 +357,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,
>>>>> @@ -441,10 +413,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",
>>>>> @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
>>>>> + u32 hw_videoport, unsigned long clock)
>>>>> +{
>>>>> + unsigned long round_clock;
>>>>> +
>>>>> + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
>>>>> + return 0;
>>>>> +
>>>>> + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
>>>>> + return 0;
>>>>> +
>>>>> + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
>>>>> + return -EINVAL;
>>>>> +
>>>>> + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
>>>>> +
>>>>> + if (dispc_pclk_diff(clock, round_clock) > 5)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
>>>>> + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
>>>>
>>>> I still don't think this logic is sound. This is trying to find the
>>>> maximum clock rate, and optimize by avoiding the calls to
>>>> clk_round_rate() if possible. That makes sense.
>>>>
>>>> But checking for the 5% tolerance breaks it, in my opinion. If we find
>>>> out that the PLL can do, say, 100M, but we need pclk of 90M, the current
>>>> maximum is still the 100M, isn't it?
>>>
>>> 5% is pretty large indeed. We've been using .5% in multiple drivers and
>>> it proved to be pretty ok. I would advise you tu use it too.
>>
>> The 5% comes from OMAP DSS, where we had to do pixel clock with a few
>> dividers and multipliers. The rates were quite coarse, and we ended up
>> having quite a large tolerance.
>>
>> I think with tidss, we always have a PLL we control, so we should always
>> have very exact clocks. So I'm fine with dropping it to .5%. However,
>> this patch and series is about removing the a-bit-too-hardcoded VP clk
>> max rate code in the driver, so I would leave everything else to another
>> series.
>
> Ack
>
>>> It's not clear to me why avoiding a clk_round_rate() call is something
>>> worth doing though?
>>
>> Hard to say if it's worth doing, someone should make some perf tests.
>> However, afaik, the calls do go to the firmware, so it involves
>> inter-processor calls. On OMAP DSS checking the clock rates was slow, as
>> it involved lots of iterating with dividers and multipliers. Perhaps
>> it's much faster here.
>
> It's not like it's going to be called a lot anyway. It's called once for
> each mode when EDID are read (using an I2C bus), and then once per
> commit that change the mode.
>
> Both operations are super slow already, so I'm pretty sure you wouldn't
> be able to tell.
>
>>> Even caching the maximum rate you have been able to reach before is
>>> pretty fragile: if the PLL changes its rate, or if a sibling clock has
>>> set some limits on what the PLL can do, your maximum isn't relevant
>>> anymore.
>>
>> You're right, although afaik it should not happen with TI's SoCs. We
>> would be in trouble anyway if that were the case (e.g. someone starts
>> the camera, and suddenly we can't support 1080p anymore).
>>
>>> in other words, what's wrong with simply calling clk_round_rate() and
>>> checking if it's within a .5% deviation?
>>
>> This started with discussions how to replace the hardcoded max VP clock
>> rate (used to quickly weed out impossible rates), which in reality was
>> actually PLL max clock rate. We don't know the PLL max rate, and can't
>> query it, so this approach was taken.
>
> If it's fixed by the platform, you have clk_get_max_rate(), but also,
We have what, where? I don't see clk_get_max_rate(), and when I looked,
I didn't see any means to find out the limits of a clock.
> does it really matter?
>
> I mean, you don't really care about the max, you care whether you can
> have a clock matching the expected pixel clock. Whether it's too low,
> too high, or just that it doesn't want to doesn't matter if you can't:
> you should just reject that mode.
>
> It does matter if you try to optimize things and avoid extra
> clk_round_rate() calls, but realistically speaking, for the OLDI that
> drives panel afaik, you're only going to consider a handful of modes.
I agree. In the minimum we have to see if clk_round_rate() just works,
because it well might. If it's absolutely too slow, maybe we can have a
LRU cache for it.
Tomi
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-08-27 10:39 ` Tomi Valkeinen
@ 2025-08-27 11:25 ` Maxime Ripard
0 siblings, 0 replies; 22+ messages in thread
From: Maxime Ripard @ 2025-08-27 11:25 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: Swamil Jain, h-shenoy, devarsht, vigneshr, praneeth, u-kumar1,
dri-devel, linux-kernel, jyri.sarha, maarten.lankhorst,
tzimmermann, airlied, simona, aradhya.bhatia
[-- Attachment #1: Type: text/plain, Size: 10233 bytes --]
On Wed, Aug 27, 2025 at 01:39:06PM +0300, Tomi Valkeinen wrote:
> Hi,
>
> On 27/08/2025 13:34, Maxime Ripard wrote:
> > On Wed, Aug 27, 2025 at 12:49:37PM +0300, Tomi Valkeinen wrote:
> >> On 27/08/2025 12:27, Maxime Ripard wrote:
> >>> On Wed, Aug 27, 2025 at 11:49:22AM +0300, Tomi Valkeinen wrote:
> >>>> On 19/08/2025 22:21, Swamil Jain wrote:
> >>>>> From: Jayesh Choudhary <j-choudhary@ti.com>
> >>>>>
> >>>>> TIDSS hardware by itself does not have variable max_pclk for each VP.
> >>>>> The maximum pixel clock is determined by the limiting factor between
> >>>>> the functional clock and the PLL (parent to the VP/pixel clock).
> >>>>
> >>>> Hmm, this is actually not in the driver, is it? We're not limiting the
> >>>> pclk based on the fclk.
> >>>>
> >>>>> The limitation that has been modeled till now comes from the clock
> >>>>> (PLL can only be programmed to a particular max value). Instead of
> >>>>> putting it as a constant field in dispc_features, we can query the
> >>>>> DM to see if requested clock can be set or not and use it in
> >>>>> mode_valid().
> >>>>>
> >>>>> Replace constant "max_pclk_khz" in dispc_features with
> >>>>> max_successful_rate and max_attempted_rate, both of these in
> >>>>> tidss_device structure would be modified in runtime. In mode_valid()
> >>>>> call, check if a best frequency match for mode clock can be found or
> >>>>> not using "clk_round_rate()". Based on that, propagate
> >>>>> max_successful_rate and max_attempted_rate and query DM again only if
> >>>>> the requested mode clock is greater than max_attempted_rate. (As the
> >>>>> preferred display mode is usually the max resolution, driver ends up
> >>>>> checking the highest clock the first time itself which is used in
> >>>>> subsequent checks).
> >>>>>
> >>>>> Since TIDSS display controller provides clock tolerance of 5%, we use
> >>>>> this while checking the max_successful_rate. Also, move up
> >>>>> "dispc_pclk_diff()" before it is called.
> >>>>>
> >>>>> This will make the existing compatibles reusable if DSS features are
> >>>>> same across two SoCs with the only difference being the pixel clock.
> >>>>>
> >>>>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
> >>>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 +++++++++++++----------------
> >>>>> drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
> >>>>> drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
> >>>>> 3 files changed, 47 insertions(+), 50 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
> >>>>> index c0277fa36425..c2c0fe0d4a0f 100644
> >>>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
> >>>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
> >>>>> @@ -58,10 +58,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.
> >>>>> @@ -144,11 +140,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,
> >>>>> @@ -244,11 +235,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,
> >>>>> @@ -315,11 +301,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,
> >>>>> @@ -376,15 +357,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,
> >>>>> @@ -441,10 +413,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",
> >>>>> @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
> >>>>> + u32 hw_videoport, unsigned long clock)
> >>>>> +{
> >>>>> + unsigned long round_clock;
> >>>>> +
> >>>>> + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
> >>>>> + return 0;
> >>>>> +
> >>>>> + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
> >>>>> + return 0;
> >>>>> +
> >>>>> + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
> >>>>> + return -EINVAL;
> >>>>> +
> >>>>> + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
> >>>>> +
> >>>>> + if (dispc_pclk_diff(clock, round_clock) > 5)
> >>>>> + return -EINVAL;
> >>>>> +
> >>>>> + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
> >>>>> + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
> >>>>
> >>>> I still don't think this logic is sound. This is trying to find the
> >>>> maximum clock rate, and optimize by avoiding the calls to
> >>>> clk_round_rate() if possible. That makes sense.
> >>>>
> >>>> But checking for the 5% tolerance breaks it, in my opinion. If we find
> >>>> out that the PLL can do, say, 100M, but we need pclk of 90M, the current
> >>>> maximum is still the 100M, isn't it?
> >>>
> >>> 5% is pretty large indeed. We've been using .5% in multiple drivers and
> >>> it proved to be pretty ok. I would advise you tu use it too.
> >>
> >> The 5% comes from OMAP DSS, where we had to do pixel clock with a few
> >> dividers and multipliers. The rates were quite coarse, and we ended up
> >> having quite a large tolerance.
> >>
> >> I think with tidss, we always have a PLL we control, so we should always
> >> have very exact clocks. So I'm fine with dropping it to .5%. However,
> >> this patch and series is about removing the a-bit-too-hardcoded VP clk
> >> max rate code in the driver, so I would leave everything else to another
> >> series.
> >
> > Ack
> >
> >>> It's not clear to me why avoiding a clk_round_rate() call is something
> >>> worth doing though?
> >>
> >> Hard to say if it's worth doing, someone should make some perf tests.
> >> However, afaik, the calls do go to the firmware, so it involves
> >> inter-processor calls. On OMAP DSS checking the clock rates was slow, as
> >> it involved lots of iterating with dividers and multipliers. Perhaps
> >> it's much faster here.
> >
> > It's not like it's going to be called a lot anyway. It's called once for
> > each mode when EDID are read (using an I2C bus), and then once per
> > commit that change the mode.
> >
> > Both operations are super slow already, so I'm pretty sure you wouldn't
> > be able to tell.
> >
> >>> Even caching the maximum rate you have been able to reach before is
> >>> pretty fragile: if the PLL changes its rate, or if a sibling clock has
> >>> set some limits on what the PLL can do, your maximum isn't relevant
> >>> anymore.
> >>
> >> You're right, although afaik it should not happen with TI's SoCs. We
> >> would be in trouble anyway if that were the case (e.g. someone starts
> >> the camera, and suddenly we can't support 1080p anymore).
> >>
> >>> in other words, what's wrong with simply calling clk_round_rate() and
> >>> checking if it's within a .5% deviation?
> >>
> >> This started with discussions how to replace the hardcoded max VP clock
> >> rate (used to quickly weed out impossible rates), which in reality was
> >> actually PLL max clock rate. We don't know the PLL max rate, and can't
> >> query it, so this approach was taken.
> >
> > If it's fixed by the platform, you have clk_get_max_rate(), but also,
>
> We have what, where? I don't see clk_get_max_rate(), and when I looked,
> I didn't see any means to find out the limits of a clock.
Sorry, clk_get_(min|max)_rate is something is sent at some point, but never got merged, and I
recalled it did. Patch is here:
https://lore.kernel.org/linux-clk/20220516132527.328190-4-maxime@cerno.tech/
There's clk_hw_get_rate_range(), but it's only available to providers.
Maxime
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 273 bytes --]
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 0/3] Decouple max_pclk check from constant display feats
2025-08-21 12:09 ` [PATCH v5 0/3] Decouple max_pclk check from constant display feats Michael Walle
@ 2025-08-28 15:59 ` Swamil Jain
0 siblings, 0 replies; 22+ messages in thread
From: Swamil Jain @ 2025-08-28 15:59 UTC (permalink / raw)
To: Michael Walle, jyri.sarha, tomi.valkeinen, maarten.lankhorst,
mripard, tzimmermann, airlied, simona, aradhya.bhatia
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel
On 8/21/25 17:39, Michael Walle wrote:
> On Tue Aug 19, 2025 at 9:21 PM CEST, Swamil Jain wrote:
>> In an effort to make the existing compatibles more usable, we are
>> removing the max_pclk_khz form dispc_features structure and doing the
>> supported pixel clock checks using "max_successful_rate[]" and
>> "max_attempted_rate[]".
>>
>> Changes are fully backwards compatible.
>>
>> After integration of OLDI support[0], we need additional patches in
>> OLDI to identify the VP that has OLDI. We have to do this since
>> OLDI driver owns the VP clock (its serial clock) and we cannot perform
>> clock operations on those VP clock from tidss driver. This issue was
>> also reported upstream when DSI fixes[1] had some clock related calls
>> in tidss driver. When "clk_round_rate()" is called, ideally it should
>> have gone to "sci_clk_determine_rate()" to query DM but it doesn't since
>> clock is owned by OLDI not tidss.
>>
>> So add a member is_ext_vp_clk[] in tidss_device structure to identify
>> this and avoid performing clock operations for VP if it has OLDI panel.
>> For the same checks in OLDI driver, atomic_check() hook is added to its
>> bridge_funcs.
>> In the atomic_check() chain, first the bridge_atomic_check() is called
>> and then crtc_atomic_check() is called. So mode clock is first checked
>> in oldi driver and then skipped in tidss driver.
>>
>> Had the tidss_oldi structure been exposed to tidss_dispc.c, we could
>> have directly checked VP type in dispc but since the structure is defined
>> in tidss_oldi.c , we have to add additional member to tidss_device
>> structure.
>>
>> [0]: https://lore.kernel.org/all/20250528122544.817829-1-aradhya.bhatia@linux.dev/
>> [1]: https://lore.kernel.org/all/DA6TT575Z82D.3MPK8HG5GRL8U@kernel.org/
>
> Since that wasn't picked up from v4:
>
> Tested-by: Michael Walle <mwalle@kernel.org> # on am67a
Hi Michael,
Thanks for testing the patches. Sorry, I missed T-By.
Will add in v6.
Regards,
Swamil
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-08-27 8:49 ` Tomi Valkeinen
2025-08-27 9:27 ` Maxime Ripard
@ 2025-08-28 16:44 ` Swamil Jain
1 sibling, 0 replies; 22+ messages in thread
From: Swamil Jain @ 2025-08-28 16:44 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, jyri.sarha, maarten.lankhorst, mripard, tzimmermann,
airlied, simona, aradhya.bhatia
On 8/27/25 14:19, Tomi Valkeinen wrote:
> Hi,
>
> On 19/08/2025 22:21, Swamil Jain wrote:
>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>
>> TIDSS hardware by itself does not have variable max_pclk for each VP.
>> The maximum pixel clock is determined by the limiting factor between
>> the functional clock and the PLL (parent to the VP/pixel clock).
>
> Hmm, this is actually not in the driver, is it? We're not limiting the
> pclk based on the fclk.
Hi Tomi,
We are checking what all pclks can be supported, which is limited by
fclk, thats what Jayesh wanted to mention here.
>
>> The limitation that has been modeled till now comes from the clock
>> (PLL can only be programmed to a particular max value). Instead of
>> putting it as a constant field in dispc_features, we can query the
>> DM to see if requested clock can be set or not and use it in
>> mode_valid().
>>
>> Replace constant "max_pclk_khz" in dispc_features with
>> max_successful_rate and max_attempted_rate, both of these in
>> tidss_device structure would be modified in runtime. In mode_valid()
>> call, check if a best frequency match for mode clock can be found or
>> not using "clk_round_rate()". Based on that, propagate
>> max_successful_rate and max_attempted_rate and query DM again only if
>> the requested mode clock is greater than max_attempted_rate. (As the
>> preferred display mode is usually the max resolution, driver ends up
>> checking the highest clock the first time itself which is used in
>> subsequent checks).
>>
>> Since TIDSS display controller provides clock tolerance of 5%, we use
>> this while checking the max_successful_rate. Also, move up
>> "dispc_pclk_diff()" before it is called.
>>
>> This will make the existing compatibles reusable if DSS features are
>> same across two SoCs with the only difference being the pixel clock.
>>
>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
>> Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 +++++++++++++----------------
>> drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
>> drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
>> 3 files changed, 47 insertions(+), 50 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
>> index c0277fa36425..c2c0fe0d4a0f 100644
>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>> @@ -58,10 +58,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.
>> @@ -144,11 +140,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,
>> @@ -244,11 +235,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,
>> @@ -315,11 +301,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,
>> @@ -376,15 +357,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,
>> @@ -441,10 +413,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",
>> @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
>> + u32 hw_videoport, unsigned long clock)
>> +{
>> + unsigned long round_clock;
>> +
>> + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
>> + return 0;
>> +
>> + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
>> + return 0;
>> +
>> + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
>> + return -EINVAL;
>> +
>> + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
>> +
>> + if (dispc_pclk_diff(clock, round_clock) > 5)
>> + return -EINVAL;
>> +
>> + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
>> + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
>
> I still don't think this logic is sound. This is trying to find the
> maximum clock rate, and optimize by avoiding the calls to
> clk_round_rate() if possible. That makes sense.
>
> But checking for the 5% tolerance breaks it, in my opinion. If we find
> out that the PLL can do, say, 100M, but we need pclk of 90M, the current
> maximum is still the 100M, isn't it?
>
> Why can't we replace the "if (mode->clock > max_pclk)" check with a new
> check that only looks for the max rate? If we want to add tolerance
> checks to mode_valid (which are currently not there), let's add it in a
> separate patch.
>
Yeah, we can drop tolerance check. So, should we drop
check_pixel_clock() and also clk_round_rate(), but then how can we know
the maximum supported pclk?
Regards,
Swamil
> Tomi
>
>> + 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)
>> + if (check_pixel_clock(dispc, hw_videoport, mode->clock * 1000))
>> return MODE_CLOCK_HIGH;
>>
>> if (mode->hdisplay > 4096)
>> @@ -1437,17 +1437,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 b8614f62186c..45b1a8aa9089 100644
>> --- a/drivers/gpu/drm/tidss/tidss_dispc.h
>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.h
>> @@ -75,7 +75,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;
>>
>> diff --git a/drivers/gpu/drm/tidss/tidss_drv.h b/drivers/gpu/drm/tidss/tidss_drv.h
>> index 4e38cfa99e84..667c0d772519 100644
>> --- a/drivers/gpu/drm/tidss/tidss_drv.h
>> +++ b/drivers/gpu/drm/tidss/tidss_drv.h
>> @@ -23,7 +23,16 @@ struct tidss_device {
>> const struct dispc_features *feat;
>> struct dispc_device *dispc;
>> bool is_ext_vp_clk[TIDSS_MAX_PORTS];
>> -
>> + /*
>> + * Stores highest pixel clock value found to be valid while checking
>> + * supported modes for connected display
>> + */
>> + unsigned long max_successful_rate[TIDSS_MAX_PORTS];
>> + /*
>> + * Stores the highest attempted pixel clock rate whose validated
>> + * clock is within the tolerance range
>> + */
>> + unsigned long max_attempted_rate[TIDSS_MAX_PORTS];
>>
>> unsigned int num_crtcs;
>> struct drm_crtc *crtcs[TIDSS_MAX_PORTS];
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-08-27 9:27 ` Maxime Ripard
2025-08-27 9:49 ` Tomi Valkeinen
@ 2025-08-29 3:35 ` Swamil Jain
1 sibling, 0 replies; 22+ messages in thread
From: Swamil Jain @ 2025-08-29 3:35 UTC (permalink / raw)
To: Maxime Ripard, Tomi Valkeinen
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, jyri.sarha, maarten.lankhorst, tzimmermann, airlied,
simona, aradhya.bhatia
On 8/27/25 14:57, Maxime Ripard wrote:
> On Wed, Aug 27, 2025 at 11:49:22AM +0300, Tomi Valkeinen wrote:
>> On 19/08/2025 22:21, Swamil Jain wrote:
>>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>>
>>> TIDSS hardware by itself does not have variable max_pclk for each VP.
>>> The maximum pixel clock is determined by the limiting factor between
>>> the functional clock and the PLL (parent to the VP/pixel clock).
>>
>> Hmm, this is actually not in the driver, is it? We're not limiting the
>> pclk based on the fclk.
>>
>>> The limitation that has been modeled till now comes from the clock
>>> (PLL can only be programmed to a particular max value). Instead of
>>> putting it as a constant field in dispc_features, we can query the
>>> DM to see if requested clock can be set or not and use it in
>>> mode_valid().
>>>
>>> Replace constant "max_pclk_khz" in dispc_features with
>>> max_successful_rate and max_attempted_rate, both of these in
>>> tidss_device structure would be modified in runtime. In mode_valid()
>>> call, check if a best frequency match for mode clock can be found or
>>> not using "clk_round_rate()". Based on that, propagate
>>> max_successful_rate and max_attempted_rate and query DM again only if
>>> the requested mode clock is greater than max_attempted_rate. (As the
>>> preferred display mode is usually the max resolution, driver ends up
>>> checking the highest clock the first time itself which is used in
>>> subsequent checks).
>>>
>>> Since TIDSS display controller provides clock tolerance of 5%, we use
>>> this while checking the max_successful_rate. Also, move up
>>> "dispc_pclk_diff()" before it is called.
>>>
>>> This will make the existing compatibles reusable if DSS features are
>>> same across two SoCs with the only difference being the pixel clock.
>>>
>>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 +++++++++++++----------------
>>> drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
>>> drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
>>> 3 files changed, 47 insertions(+), 50 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
>>> index c0277fa36425..c2c0fe0d4a0f 100644
>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>>> @@ -58,10 +58,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.
>>> @@ -144,11 +140,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,
>>> @@ -244,11 +235,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,
>>> @@ -315,11 +301,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,
>>> @@ -376,15 +357,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,
>>> @@ -441,10 +413,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",
>>> @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
>>> + u32 hw_videoport, unsigned long clock)
>>> +{
>>> + unsigned long round_clock;
>>> +
>>> + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
>>> + return 0;
>>> +
>>> + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
>>> + return 0;
>>> +
>>> + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
>>> + return -EINVAL;
>>> +
>>> + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
>>> +
>>> + if (dispc_pclk_diff(clock, round_clock) > 5)
>>> + return -EINVAL;
>>> +
>>> + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
>>> + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
>>
>> I still don't think this logic is sound. This is trying to find the
>> maximum clock rate, and optimize by avoiding the calls to
>> clk_round_rate() if possible. That makes sense.
>>
>> But checking for the 5% tolerance breaks it, in my opinion. If we find
>> out that the PLL can do, say, 100M, but we need pclk of 90M, the current
>> maximum is still the 100M, isn't it?
>
> 5% is pretty large indeed. We've been using .5% in multiple drivers and
> it proved to be pretty ok. I would advise you tu use it too.
>
> It's not clear to me why avoiding a clk_round_rate() call is something
> worth doing though?
>
> Even caching the maximum rate you have been able to reach before is
> pretty fragile: if the PLL changes its rate, or if a sibling clock has
> set some limits on what the PLL can do, your maximum isn't relevant
> anymore.
>
> in other words, what's wrong with simply calling clk_round_rate() and
> checking if it's within a .5% deviation?
>
Hi Maxime,
Tomi already respoded for 5% tolerance.
> At the very least, this should be explained in comments or the commit
> message.
Sure, will add this in more detail in comments/commit message.
Regards,
Swamil
>
> Maxime
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-08-27 9:49 ` Tomi Valkeinen
2025-08-27 10:34 ` Maxime Ripard
@ 2025-08-29 3:37 ` Swamil Jain
2025-09-03 8:38 ` Swamil Jain
2 siblings, 0 replies; 22+ messages in thread
From: Swamil Jain @ 2025-08-29 3:37 UTC (permalink / raw)
To: Tomi Valkeinen, Maxime Ripard
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, jyri.sarha, maarten.lankhorst, tzimmermann, airlied,
simona, aradhya.bhatia
On 8/27/25 15:19, Tomi Valkeinen wrote:
> Hi,
>
> On 27/08/2025 12:27, Maxime Ripard wrote:
>> On Wed, Aug 27, 2025 at 11:49:22AM +0300, Tomi Valkeinen wrote:
>>> On 19/08/2025 22:21, Swamil Jain wrote:
>>>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>>>
>>>> TIDSS hardware by itself does not have variable max_pclk for each VP.
>>>> The maximum pixel clock is determined by the limiting factor between
>>>> the functional clock and the PLL (parent to the VP/pixel clock).
>>>
>>> Hmm, this is actually not in the driver, is it? We're not limiting the
>>> pclk based on the fclk.
>>>
>>>> The limitation that has been modeled till now comes from the clock
>>>> (PLL can only be programmed to a particular max value). Instead of
>>>> putting it as a constant field in dispc_features, we can query the
>>>> DM to see if requested clock can be set or not and use it in
>>>> mode_valid().
>>>>
>>>> Replace constant "max_pclk_khz" in dispc_features with
>>>> max_successful_rate and max_attempted_rate, both of these in
>>>> tidss_device structure would be modified in runtime. In mode_valid()
>>>> call, check if a best frequency match for mode clock can be found or
>>>> not using "clk_round_rate()". Based on that, propagate
>>>> max_successful_rate and max_attempted_rate and query DM again only if
>>>> the requested mode clock is greater than max_attempted_rate. (As the
>>>> preferred display mode is usually the max resolution, driver ends up
>>>> checking the highest clock the first time itself which is used in
>>>> subsequent checks).
>>>>
>>>> Since TIDSS display controller provides clock tolerance of 5%, we use
>>>> this while checking the max_successful_rate. Also, move up
>>>> "dispc_pclk_diff()" before it is called.
>>>>
>>>> This will make the existing compatibles reusable if DSS features are
>>>> same across two SoCs with the only difference being the pixel clock.
>>>>
>>>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
>>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 +++++++++++++----------------
>>>> drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
>>>> drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
>>>> 3 files changed, 47 insertions(+), 50 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
>>>> index c0277fa36425..c2c0fe0d4a0f 100644
>>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>>>> @@ -58,10 +58,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.
>>>> @@ -144,11 +140,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,
>>>> @@ -244,11 +235,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,
>>>> @@ -315,11 +301,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,
>>>> @@ -376,15 +357,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,
>>>> @@ -441,10 +413,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",
>>>> @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
>>>> + u32 hw_videoport, unsigned long clock)
>>>> +{
>>>> + unsigned long round_clock;
>>>> +
>>>> + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
>>>> + return 0;
>>>> +
>>>> + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
>>>> + return 0;
>>>> +
>>>> + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
>>>> + return -EINVAL;
>>>> +
>>>> + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
>>>> +
>>>> + if (dispc_pclk_diff(clock, round_clock) > 5)
>>>> + return -EINVAL;
>>>> +
>>>> + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
>>>> + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
>>>
>>> I still don't think this logic is sound. This is trying to find the
>>> maximum clock rate, and optimize by avoiding the calls to
>>> clk_round_rate() if possible. That makes sense.
>>>
>>> But checking for the 5% tolerance breaks it, in my opinion. If we find
>>> out that the PLL can do, say, 100M, but we need pclk of 90M, the current
>>> maximum is still the 100M, isn't it?
>>
>> 5% is pretty large indeed. We've been using .5% in multiple drivers and
>> it proved to be pretty ok. I would advise you tu use it too.
>
> The 5% comes from OMAP DSS, where we had to do pixel clock with a few
> dividers and multipliers. The rates were quite coarse, and we ended up
> having quite a large tolerance.
>
> I think with tidss, we always have a PLL we control, so we should always
> have very exact clocks. So I'm fine with dropping it to .5%. However,
> this patch and series is about removing the a-bit-too-hardcoded VP clk
> max rate code in the driver, so I would leave everything else to another
> series.
>
>> It's not clear to me why avoiding a clk_round_rate() call is something
>> worth doing though?
>
> Hard to say if it's worth doing, someone should make some perf tests.
> However, afaik, the calls do go to the firmware, so it involves
> inter-processor calls. On OMAP DSS checking the clock rates was slow, as
> it involved lots of iterating with dividers and multipliers. Perhaps
> it's much faster here.
>
>> Even caching the maximum rate you have been able to reach before is
>> pretty fragile: if the PLL changes its rate, or if a sibling clock has
>> set some limits on what the PLL can do, your maximum isn't relevant
>> anymore.
>
> You're right, although afaik it should not happen with TI's SoCs. We
> would be in trouble anyway if that were the case (e.g. someone starts
> the camera, and suddenly we can't support 1080p anymore).
>
>> in other words, what's wrong with simply calling clk_round_rate() and
>> checking if it's within a .5% deviation?
>
> This started with discussions how to replace the hardcoded max VP clock
> rate (used to quickly weed out impossible rates), which in reality was
> actually PLL max clock rate. We don't know the PLL max rate, and can't
> query it, so this approach was taken.
>
>> At the very least, this should be explained in comments or the commit
>> message.
>
> I agree.
>
> Swamil, can you do some perf tests with clk_round_rate()? If it's fast
> (enough), it will simplify the driver.
Yeah Tomi, will do a perf test.
Regards,
Swamil
>
> Tomi
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 3/3] drm/tidss: oldi: Add atomic_check hook for oldi bridge
2025-08-27 9:05 ` Tomi Valkeinen
@ 2025-08-29 3:50 ` Swamil Jain
2025-09-01 8:52 ` Tomi Valkeinen
0 siblings, 1 reply; 22+ messages in thread
From: Swamil Jain @ 2025-08-29 3:50 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, jyri.sarha, maarten.lankhorst, mripard, tzimmermann,
airlied, simona, aradhya.bhatia
Hi Tomi,
On 8/27/25 14:35, Tomi Valkeinen wrote:
> Hi,
>
> On 19/08/2025 22:21, Swamil Jain wrote:
>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>
>> Since OLDI consumes DSS VP clock directly as serial clock, certain
>> checks cannot be performed in tidss driver which should be checked
>
> I think this is a bit misleading. The OLDI input clock doesn't come from
> DSS, so I wouldn't call it "DSS VP clock". The point here is that the
> clock from the PLL is used by both OLDI and DSS, and in the current
> architecture the OLDI driver manages the clock, so the DSS driver can't
> really do checks, it just has to accept the clock rate. All checks need
> to be done in the OLDI driver.
>
>> in OLDI driver. Add check for mode clock and set max_successful_rate
>> and max_attempted_rate field for tidss in case the VP is OLDI.
>>
>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
>> Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
>> Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
>> Signed-off-by: Swamil Jain <s-jain1@ti.com>
>> ---
>> drivers/gpu/drm/tidss/tidss_oldi.c | 25 +++++++++++++++++++++++++
>> 1 file changed, 25 insertions(+)
>>
>> diff --git a/drivers/gpu/drm/tidss/tidss_oldi.c b/drivers/gpu/drm/tidss/tidss_oldi.c
>> index ef01ecc17a12..2ed2d0666ccb 100644
>> --- a/drivers/gpu/drm/tidss/tidss_oldi.c
>> +++ b/drivers/gpu/drm/tidss/tidss_oldi.c
>> @@ -309,6 +309,30 @@ static u32 *tidss_oldi_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
>> return input_fmts;
>> }
>>
>> +static int tidss_oldi_atomic_check(struct drm_bridge *bridge,
>> + struct drm_bridge_state *bridge_state,
>> + struct drm_crtc_state *crtc_state,
>> + struct drm_connector_state *conn_state)
>> +{
>> + struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
>> + struct drm_display_mode *adjusted_mode;
>> + unsigned long round_clock;
>> +
>> + adjusted_mode = &crtc_state->adjusted_mode;
>> +
>> + if (adjusted_mode->clock > oldi->tidss->max_successful_rate[oldi->parent_vp]) {
>
> You can change the above check to <=, and return 0 here early.
>
>> + round_clock = clk_round_rate(oldi->serial, adjusted_mode->clock * 7 * 1000);
>> +
>> + if (dispc_pclk_diff(adjusted_mode->clock * 7 * 1000, round_clock) > 5)
>> + return -EINVAL;
>> +
>> + oldi->tidss->max_successful_rate[oldi->parent_vp] = round_clock;
>> + oldi->tidss->max_attempted_rate[oldi->parent_vp] = adjusted_mode->clock * 7 * 1000;
>> + }
>
> This is not very nice. We should have a function in tidss that we call
> here, instead of poking into these tidss's variables directly.
>
> Actually... Do we even need to use the tidss->max_* fields? The above
> code is not checking the VP clock maximum, it's actually looking at the
> serial clock maximum. Currently those two clocks are linked, though, but
> would it make more sense to have the max_* fields here, in OLDI, for
> OLDI's serial clock?We don't require tidss->max_* fields here as we only have single mode
for OLDI. This is added to make it consistent with check_pixel_clock()
for validating modes.
You are right we shouldn't use tidss->* fields directly in OLDI driver.
As Maxime suggested we only have very few modes for OLDI, can we skip
caching maximum pixel clock in case of OLDI? What would you suggest Tomi?
Regards,
Swamil
>
> Tomi
>
>> +
>> + 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 +341,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,
>> + .atomic_check = tidss_oldi_atomic_check,
>> };
>>
>> static int get_oldi_mode(struct device_node *oldi_tx, int *companion_instance)
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 3/3] drm/tidss: oldi: Add atomic_check hook for oldi bridge
2025-08-29 3:50 ` Swamil Jain
@ 2025-09-01 8:52 ` Tomi Valkeinen
2025-09-01 8:59 ` Swamil Jain
0 siblings, 1 reply; 22+ messages in thread
From: Tomi Valkeinen @ 2025-09-01 8:52 UTC (permalink / raw)
To: Swamil Jain
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, jyri.sarha, maarten.lankhorst, mripard, tzimmermann,
airlied, simona, aradhya.bhatia
Hi,
On 29/08/2025 06:50, Swamil Jain wrote:
> Hi Tomi,
>
> On 8/27/25 14:35, Tomi Valkeinen wrote:
>> Hi,
>>
>> On 19/08/2025 22:21, Swamil Jain wrote:
>>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>>
>>> Since OLDI consumes DSS VP clock directly as serial clock, certain
>>> checks cannot be performed in tidss driver which should be checked
>>
>> I think this is a bit misleading. The OLDI input clock doesn't come from
>> DSS, so I wouldn't call it "DSS VP clock". The point here is that the
>> clock from the PLL is used by both OLDI and DSS, and in the current
>> architecture the OLDI driver manages the clock, so the DSS driver can't
>> really do checks, it just has to accept the clock rate. All checks need
>> to be done in the OLDI driver.
>>
>>> in OLDI driver. Add check for mode clock and set max_successful_rate
>>> and max_attempted_rate field for tidss in case the VP is OLDI.
>>>
>>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
>>> Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
>>> Signed-off-by: Swamil Jain <s-jain1@ti.com>
>>> ---
>>> drivers/gpu/drm/tidss/tidss_oldi.c | 25 +++++++++++++++++++++++++
>>> 1 file changed, 25 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/tidss/tidss_oldi.c b/drivers/gpu/drm/
>>> tidss/tidss_oldi.c
>>> index ef01ecc17a12..2ed2d0666ccb 100644
>>> --- a/drivers/gpu/drm/tidss/tidss_oldi.c
>>> +++ b/drivers/gpu/drm/tidss/tidss_oldi.c
>>> @@ -309,6 +309,30 @@ static u32
>>> *tidss_oldi_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
>>> return input_fmts;
>>> }
>>> +static int tidss_oldi_atomic_check(struct drm_bridge *bridge,
>>> + struct drm_bridge_state *bridge_state,
>>> + struct drm_crtc_state *crtc_state,
>>> + struct drm_connector_state *conn_state)
>>> +{
>>> + struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
>>> + struct drm_display_mode *adjusted_mode;
>>> + unsigned long round_clock;
>>> +
>>> + adjusted_mode = &crtc_state->adjusted_mode;
>>> +
>>> + if (adjusted_mode->clock > oldi->tidss-
>>> >max_successful_rate[oldi->parent_vp]) {
>>
>> You can change the above check to <=, and return 0 here early.
>>
>>> + round_clock = clk_round_rate(oldi->serial, adjusted_mode-
>>> >clock * 7 * 1000);
>>> +
>>> + if (dispc_pclk_diff(adjusted_mode->clock * 7 * 1000,
>>> round_clock) > 5)
>>> + return -EINVAL;
>>> +
>>> + oldi->tidss->max_successful_rate[oldi->parent_vp] =
>>> round_clock;
>>> + oldi->tidss->max_attempted_rate[oldi->parent_vp] =
>>> adjusted_mode->clock * 7 * 1000;
>>> + }
>>
>> This is not very nice. We should have a function in tidss that we call
>> here, instead of poking into these tidss's variables directly.
>>
>> Actually... Do we even need to use the tidss->max_* fields? The above
>> code is not checking the VP clock maximum, it's actually looking at the
>> serial clock maximum. Currently those two clocks are linked, though, but
>> would it make more sense to have the max_* fields here, in OLDI, for
>> OLDI's serial clock?We don't require tidss->max_* fields here as we
>> only have single mode
> for OLDI. This is added to make it consistent with check_pixel_clock()
> for validating modes.
>
> You are right we shouldn't use tidss->* fields directly in OLDI driver.
>
> As Maxime suggested we only have very few modes for OLDI, can we skip
> caching maximum pixel clock in case of OLDI? What would you suggest Tomi?
I think it's best to at least try the trivial option discussed in this
thread: just use clk_round_rate, without any code to cache or seek out
the max.
Tomi
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 3/3] drm/tidss: oldi: Add atomic_check hook for oldi bridge
2025-09-01 8:52 ` Tomi Valkeinen
@ 2025-09-01 8:59 ` Swamil Jain
0 siblings, 0 replies; 22+ messages in thread
From: Swamil Jain @ 2025-09-01 8:59 UTC (permalink / raw)
To: Tomi Valkeinen
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, jyri.sarha, maarten.lankhorst, mripard, tzimmermann,
airlied, simona, aradhya.bhatia
Hi Tomi,
On 9/1/25 14:22, Tomi Valkeinen wrote:
> Hi,
>
> On 29/08/2025 06:50, Swamil Jain wrote:
>> Hi Tomi,
>>
>> On 8/27/25 14:35, Tomi Valkeinen wrote:
>>> Hi,
>>>
>>> On 19/08/2025 22:21, Swamil Jain wrote:
>>>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>>>
>>>> Since OLDI consumes DSS VP clock directly as serial clock, certain
>>>> checks cannot be performed in tidss driver which should be checked
>>>
>>> I think this is a bit misleading. The OLDI input clock doesn't come from
>>> DSS, so I wouldn't call it "DSS VP clock". The point here is that the
>>> clock from the PLL is used by both OLDI and DSS, and in the current
>>> architecture the OLDI driver manages the clock, so the DSS driver can't
>>> really do checks, it just has to accept the clock rate. All checks need
>>> to be done in the OLDI driver.
>>>
>>>> in OLDI driver. Add check for mode clock and set max_successful_rate
>>>> and max_attempted_rate field for tidss in case the VP is OLDI.
>>>>
>>>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
>>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.com>
>>>> Signed-off-by: Jayesh Choudhary <j-choudhary@ti.com>
>>>> Signed-off-by: Swamil Jain <s-jain1@ti.com>
>>>> ---
>>>> drivers/gpu/drm/tidss/tidss_oldi.c | 25 +++++++++++++++++++++++++
>>>> 1 file changed, 25 insertions(+)
>>>>
>>>> diff --git a/drivers/gpu/drm/tidss/tidss_oldi.c b/drivers/gpu/drm/
>>>> tidss/tidss_oldi.c
>>>> index ef01ecc17a12..2ed2d0666ccb 100644
>>>> --- a/drivers/gpu/drm/tidss/tidss_oldi.c
>>>> +++ b/drivers/gpu/drm/tidss/tidss_oldi.c
>>>> @@ -309,6 +309,30 @@ static u32
>>>> *tidss_oldi_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
>>>> return input_fmts;
>>>> }
>>>> +static int tidss_oldi_atomic_check(struct drm_bridge *bridge,
>>>> + struct drm_bridge_state *bridge_state,
>>>> + struct drm_crtc_state *crtc_state,
>>>> + struct drm_connector_state *conn_state)
>>>> +{
>>>> + struct tidss_oldi *oldi = drm_bridge_to_tidss_oldi(bridge);
>>>> + struct drm_display_mode *adjusted_mode;
>>>> + unsigned long round_clock;
>>>> +
>>>> + adjusted_mode = &crtc_state->adjusted_mode;
>>>> +
>>>> + if (adjusted_mode->clock > oldi->tidss-
>>>>> max_successful_rate[oldi->parent_vp]) {
>>>
>>> You can change the above check to <=, and return 0 here early.
>>>
>>>> + round_clock = clk_round_rate(oldi->serial, adjusted_mode-
>>>>> clock * 7 * 1000);
>>>> +
>>>> + if (dispc_pclk_diff(adjusted_mode->clock * 7 * 1000,
>>>> round_clock) > 5)
>>>> + return -EINVAL;
>>>> +
>>>> + oldi->tidss->max_successful_rate[oldi->parent_vp] =
>>>> round_clock;
>>>> + oldi->tidss->max_attempted_rate[oldi->parent_vp] =
>>>> adjusted_mode->clock * 7 * 1000;
>>>> + }
>>>
>>> This is not very nice. We should have a function in tidss that we call
>>> here, instead of poking into these tidss's variables directly.
>>>
>>> Actually... Do we even need to use the tidss->max_* fields? The above
>>> code is not checking the VP clock maximum, it's actually looking at the
>>> serial clock maximum. Currently those two clocks are linked, though, but
>>> would it make more sense to have the max_* fields here, in OLDI, for
>>> OLDI's serial clock?We don't require tidss->max_* fields here as we
>>> only have single mode
>> for OLDI. This is added to make it consistent with check_pixel_clock()
>> for validating modes.
>>
>> You are right we shouldn't use tidss->* fields directly in OLDI driver.
>>
>> As Maxime suggested we only have very few modes for OLDI, can we skip
>> caching maximum pixel clock in case of OLDI? What would you suggest Tomi?
>
> I think it's best to at least try the trivial option discussed in this
> thread: just use clk_round_rate, without any code to cache or seek out
> the max.
Then for OLDI, we will drop caching the max_pclk.
Regards,
Swamil
>
> Tomi
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-08-27 9:49 ` Tomi Valkeinen
2025-08-27 10:34 ` Maxime Ripard
2025-08-29 3:37 ` Swamil Jain
@ 2025-09-03 8:38 ` Swamil Jain
2025-09-03 9:31 ` Tomi Valkeinen
2 siblings, 1 reply; 22+ messages in thread
From: Swamil Jain @ 2025-09-03 8:38 UTC (permalink / raw)
To: Tomi Valkeinen, Maxime Ripard
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, jyri.sarha, maarten.lankhorst, tzimmermann, airlied,
simona, aradhya.bhatia
Hi Tomi, Maxime,
On 8/27/25 15:19, Tomi Valkeinen wrote:
> Hi,
>
> On 27/08/2025 12:27, Maxime Ripard wrote:
>> On Wed, Aug 27, 2025 at 11:49:22AM +0300, Tomi Valkeinen wrote:
>>> On 19/08/2025 22:21, Swamil Jain wrote:
>>>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>>>
>>>> TIDSS hardware by itself does not have variable max_pclk for each VP.
>>>> The maximum pixel clock is determined by the limiting factor between
>>>> the functional clock and the PLL (parent to the VP/pixel clock).
>>>
>>> Hmm, this is actually not in the driver, is it? We're not limiting the
>>> pclk based on the fclk.
>>>
>>>> The limitation that has been modeled till now comes from the clock
>>>> (PLL can only be programmed to a particular max value). Instead of
>>>> putting it as a constant field in dispc_features, we can query the
>>>> DM to see if requested clock can be set or not and use it in
>>>> mode_valid().
>>>>
>>>> Replace constant "max_pclk_khz" in dispc_features with
>>>> max_successful_rate and max_attempted_rate, both of these in
>>>> tidss_device structure would be modified in runtime. In mode_valid()
>>>> call, check if a best frequency match for mode clock can be found or
>>>> not using "clk_round_rate()". Based on that, propagate
>>>> max_successful_rate and max_attempted_rate and query DM again only if
>>>> the requested mode clock is greater than max_attempted_rate. (As the
>>>> preferred display mode is usually the max resolution, driver ends up
>>>> checking the highest clock the first time itself which is used in
>>>> subsequent checks).
>>>>
>>>> Since TIDSS display controller provides clock tolerance of 5%, we use
>>>> this while checking the max_successful_rate. Also, move up
>>>> "dispc_pclk_diff()" before it is called.
>>>>
>>>> This will make the existing compatibles reusable if DSS features are
>>>> same across two SoCs with the only difference being the pixel clock.
>>>>
>>>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
>>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 +++++++++++++----------------
>>>> drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
>>>> drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
>>>> 3 files changed, 47 insertions(+), 50 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/tidss/tidss_dispc.c
>>>> index c0277fa36425..c2c0fe0d4a0f 100644
>>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>>>> @@ -58,10 +58,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.
>>>> @@ -144,11 +140,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,
>>>> @@ -244,11 +235,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,
>>>> @@ -315,11 +301,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,
>>>> @@ -376,15 +357,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,
>>>> @@ -441,10 +413,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",
>>>> @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
>>>> + u32 hw_videoport, unsigned long clock)
>>>> +{
>>>> + unsigned long round_clock;
>>>> +
>>>> + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
>>>> + return 0;
>>>> +
>>>> + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
>>>> + return 0;
>>>> +
>>>> + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
>>>> + return -EINVAL;
>>>> +
>>>> + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
>>>> +
>>>> + if (dispc_pclk_diff(clock, round_clock) > 5)
>>>> + return -EINVAL;
>>>> +
>>>> + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
>>>> + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
>>>
>>> I still don't think this logic is sound. This is trying to find the
>>> maximum clock rate, and optimize by avoiding the calls to
>>> clk_round_rate() if possible. That makes sense.
>>>
>>> But checking for the 5% tolerance breaks it, in my opinion. If we find
>>> out that the PLL can do, say, 100M, but we need pclk of 90M, the current
>>> maximum is still the 100M, isn't it?
>>
>> 5% is pretty large indeed. We've been using .5% in multiple drivers and
>> it proved to be pretty ok. I would advise you tu use it too.
>
> The 5% comes from OMAP DSS, where we had to do pixel clock with a few
> dividers and multipliers. The rates were quite coarse, and we ended up
> having quite a large tolerance.
>
> I think with tidss, we always have a PLL we control, so we should always
> have very exact clocks. So I'm fine with dropping it to .5%. However,
> this patch and series is about removing the a-bit-too-hardcoded VP clk
> max rate code in the driver, so I would leave everything else to another
> series.
>
>> It's not clear to me why avoiding a clk_round_rate() call is something
>> worth doing though?
>
> Hard to say if it's worth doing, someone should make some perf tests.
> However, afaik, the calls do go to the firmware, so it involves
> inter-processor calls. On OMAP DSS checking the clock rates was slow, as
> it involved lots of iterating with dividers and multipliers. Perhaps
> it's much faster here.
>
>> Even caching the maximum rate you have been able to reach before is
>> pretty fragile: if the PLL changes its rate, or if a sibling clock has
>> set some limits on what the PLL can do, your maximum isn't relevant
>> anymore.
>
> You're right, although afaik it should not happen with TI's SoCs. We
> would be in trouble anyway if that were the case (e.g. someone starts
> the camera, and suddenly we can't support 1080p anymore).
>
>> in other words, what's wrong with simply calling clk_round_rate() and
>> checking if it's within a .5% deviation?
>
> This started with discussions how to replace the hardcoded max VP clock
> rate (used to quickly weed out impossible rates), which in reality was
> actually PLL max clock rate. We don't know the PLL max rate, and can't
> query it, so this approach was taken.
>
>> At the very least, this should be explained in comments or the commit
>> message.
>
> I agree.
>
> Swamil, can you do some perf tests with clk_round_rate()? If it's fast
> (enough), it will simplify the driver.
Average execution time is around 112 us.
Trace file including the execution time for clk_round_rate():
https://gist.github.com/swamiljain/2abe86982cdeba1d69223d2d525e0cb6
It is better to reduce calls to clk_round_rate().
Need your suggestions for a better approach.
Regards,
Swamil
>
> Tomi
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-09-03 8:38 ` Swamil Jain
@ 2025-09-03 9:31 ` Tomi Valkeinen
2025-09-03 9:43 ` Swamil Jain
0 siblings, 1 reply; 22+ messages in thread
From: Tomi Valkeinen @ 2025-09-03 9:31 UTC (permalink / raw)
To: Swamil Jain, Maxime Ripard
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, jyri.sarha, maarten.lankhorst, tzimmermann, airlied,
simona, aradhya.bhatia
Hi,
On 03/09/2025 11:38, Swamil Jain wrote:
> Hi Tomi, Maxime,
>
> On 8/27/25 15:19, Tomi Valkeinen wrote:
>> Hi,
>>
>> On 27/08/2025 12:27, Maxime Ripard wrote:
>>> On Wed, Aug 27, 2025 at 11:49:22AM +0300, Tomi Valkeinen wrote:
>>>> On 19/08/2025 22:21, Swamil Jain wrote:
>>>>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>>>>
>>>>> TIDSS hardware by itself does not have variable max_pclk for each VP.
>>>>> The maximum pixel clock is determined by the limiting factor between
>>>>> the functional clock and the PLL (parent to the VP/pixel clock).
>>>>
>>>> Hmm, this is actually not in the driver, is it? We're not limiting the
>>>> pclk based on the fclk.
>>>>
>>>>> The limitation that has been modeled till now comes from the clock
>>>>> (PLL can only be programmed to a particular max value). Instead of
>>>>> putting it as a constant field in dispc_features, we can query the
>>>>> DM to see if requested clock can be set or not and use it in
>>>>> mode_valid().
>>>>>
>>>>> Replace constant "max_pclk_khz" in dispc_features with
>>>>> max_successful_rate and max_attempted_rate, both of these in
>>>>> tidss_device structure would be modified in runtime. In mode_valid()
>>>>> call, check if a best frequency match for mode clock can be found or
>>>>> not using "clk_round_rate()". Based on that, propagate
>>>>> max_successful_rate and max_attempted_rate and query DM again only if
>>>>> the requested mode clock is greater than max_attempted_rate. (As the
>>>>> preferred display mode is usually the max resolution, driver ends up
>>>>> checking the highest clock the first time itself which is used in
>>>>> subsequent checks).
>>>>>
>>>>> Since TIDSS display controller provides clock tolerance of 5%, we use
>>>>> this while checking the max_successful_rate. Also, move up
>>>>> "dispc_pclk_diff()" before it is called.
>>>>>
>>>>> This will make the existing compatibles reusable if DSS features are
>>>>> same across two SoCs with the only difference being the pixel clock.
>>>>>
>>>>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
>>>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 ++++++++++++
>>>>> +----------------
>>>>> drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
>>>>> drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
>>>>> 3 files changed, 47 insertions(+), 50 deletions(-)
>>>>>
>>>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/
>>>>> tidss/tidss_dispc.c
>>>>> index c0277fa36425..c2c0fe0d4a0f 100644
>>>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>>>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>>>>> @@ -58,10 +58,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.
>>>>> @@ -144,11 +140,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,
>>>>> @@ -244,11 +235,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,
>>>>> @@ -315,11 +301,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,
>>>>> @@ -376,15 +357,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,
>>>>> @@ -441,10 +413,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",
>>>>> @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
>>>>> + u32 hw_videoport, unsigned long clock)
>>>>> +{
>>>>> + unsigned long round_clock;
>>>>> +
>>>>> + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
>>>>> + return 0;
>>>>> +
>>>>> + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
>>>>> + return 0;
>>>>> +
>>>>> + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
>>>>> + return -EINVAL;
>>>>> +
>>>>> + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
>>>>> +
>>>>> + if (dispc_pclk_diff(clock, round_clock) > 5)
>>>>> + return -EINVAL;
>>>>> +
>>>>> + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
>>>>> + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
>>>>
>>>> I still don't think this logic is sound. This is trying to find the
>>>> maximum clock rate, and optimize by avoiding the calls to
>>>> clk_round_rate() if possible. That makes sense.
>>>>
>>>> But checking for the 5% tolerance breaks it, in my opinion. If we find
>>>> out that the PLL can do, say, 100M, but we need pclk of 90M, the
>>>> current
>>>> maximum is still the 100M, isn't it?
>>>
>>> 5% is pretty large indeed. We've been using .5% in multiple drivers and
>>> it proved to be pretty ok. I would advise you tu use it too.
>>
>> The 5% comes from OMAP DSS, where we had to do pixel clock with a few
>> dividers and multipliers. The rates were quite coarse, and we ended up
>> having quite a large tolerance.
>>
>> I think with tidss, we always have a PLL we control, so we should always
>> have very exact clocks. So I'm fine with dropping it to .5%. However,
>> this patch and series is about removing the a-bit-too-hardcoded VP clk
>> max rate code in the driver, so I would leave everything else to another
>> series.
>>
>>> It's not clear to me why avoiding a clk_round_rate() call is something
>>> worth doing though?
>>
>> Hard to say if it's worth doing, someone should make some perf tests.
>> However, afaik, the calls do go to the firmware, so it involves
>> inter-processor calls. On OMAP DSS checking the clock rates was slow, as
>> it involved lots of iterating with dividers and multipliers. Perhaps
>> it's much faster here.
>>
>>> Even caching the maximum rate you have been able to reach before is
>>> pretty fragile: if the PLL changes its rate, or if a sibling clock has
>>> set some limits on what the PLL can do, your maximum isn't relevant
>>> anymore.
>>
>> You're right, although afaik it should not happen with TI's SoCs. We
>> would be in trouble anyway if that were the case (e.g. someone starts
>> the camera, and suddenly we can't support 1080p anymore).
>>
>>> in other words, what's wrong with simply calling clk_round_rate() and
>>> checking if it's within a .5% deviation?
>>
>> This started with discussions how to replace the hardcoded max VP clock
>> rate (used to quickly weed out impossible rates), which in reality was
>> actually PLL max clock rate. We don't know the PLL max rate, and can't
>> query it, so this approach was taken.
>>
>>> At the very least, this should be explained in comments or the commit
>>> message.
>>
>> I agree.
>>
>> Swamil, can you do some perf tests with clk_round_rate()? If it's fast
>> (enough), it will simplify the driver.
>
> Average execution time is around 112 us.
> Trace file including the execution time for clk_round_rate(): https://
> gist.github.com/swamiljain/2abe86982cdeba1d69223d2d525e0cb6
> It is better to reduce calls to clk_round_rate().
>
> Need your suggestions for a better approach.
We can cache the clk_round_rate calls. Checking my monitor, there are 36
modes it offers me, but only 20 different pclk rates. Also, we could
have multiple clk_round_rate calls happening in the driver for the same
mode, and that would also be handled.
Even if clk_round_rate takes a bit long, it only happens once (I hope)
when an app does a modeset, and multiple times when a display is
connected, I wonder if 100 us is an issue?
Just using clk_round_rate() without any tricks would simplify the driver
nicely, so I think we should try to see if we can get that working.
Do you know if there's anything to improve on the clock side, ti-sci or
firmare?
Tomi
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features
2025-09-03 9:31 ` Tomi Valkeinen
@ 2025-09-03 9:43 ` Swamil Jain
0 siblings, 0 replies; 22+ messages in thread
From: Swamil Jain @ 2025-09-03 9:43 UTC (permalink / raw)
To: Tomi Valkeinen, Maxime Ripard
Cc: h-shenoy, devarsht, vigneshr, praneeth, u-kumar1, dri-devel,
linux-kernel, jyri.sarha, maarten.lankhorst, tzimmermann, airlied,
simona, aradhya.bhatia
Hi,
On 9/3/25 15:01, Tomi Valkeinen wrote:
> Hi,
>
> On 03/09/2025 11:38, Swamil Jain wrote:
>> Hi Tomi, Maxime,
>>
>> On 8/27/25 15:19, Tomi Valkeinen wrote:
>>> Hi,
>>>
>>> On 27/08/2025 12:27, Maxime Ripard wrote:
>>>> On Wed, Aug 27, 2025 at 11:49:22AM +0300, Tomi Valkeinen wrote:
>>>>> On 19/08/2025 22:21, Swamil Jain wrote:
>>>>>> From: Jayesh Choudhary <j-choudhary@ti.com>
>>>>>>
>>>>>> TIDSS hardware by itself does not have variable max_pclk for each VP.
>>>>>> The maximum pixel clock is determined by the limiting factor between
>>>>>> the functional clock and the PLL (parent to the VP/pixel clock).
>>>>>
>>>>> Hmm, this is actually not in the driver, is it? We're not limiting the
>>>>> pclk based on the fclk.
>>>>>
>>>>>> The limitation that has been modeled till now comes from the clock
>>>>>> (PLL can only be programmed to a particular max value). Instead of
>>>>>> putting it as a constant field in dispc_features, we can query the
>>>>>> DM to see if requested clock can be set or not and use it in
>>>>>> mode_valid().
>>>>>>
>>>>>> Replace constant "max_pclk_khz" in dispc_features with
>>>>>> max_successful_rate and max_attempted_rate, both of these in
>>>>>> tidss_device structure would be modified in runtime. In mode_valid()
>>>>>> call, check if a best frequency match for mode clock can be found or
>>>>>> not using "clk_round_rate()". Based on that, propagate
>>>>>> max_successful_rate and max_attempted_rate and query DM again only if
>>>>>> the requested mode clock is greater than max_attempted_rate. (As the
>>>>>> preferred display mode is usually the max resolution, driver ends up
>>>>>> checking the highest clock the first time itself which is used in
>>>>>> subsequent checks).
>>>>>>
>>>>>> Since TIDSS display controller provides clock tolerance of 5%, we use
>>>>>> this while checking the max_successful_rate. Also, move up
>>>>>> "dispc_pclk_diff()" before it is called.
>>>>>>
>>>>>> This will make the existing compatibles reusable if DSS features are
>>>>>> same across two SoCs with the only difference being the pixel clock.
>>>>>>
>>>>>> Fixes: 7246e0929945 ("drm/tidss: Add OLDI bridge support")
>>>>>> Reviewed-by: Devarsh Thakkar <devarsht@ti.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 | 85 ++++++++++++
>>>>>> +----------------
>>>>>> drivers/gpu/drm/tidss/tidss_dispc.h | 1 -
>>>>>> drivers/gpu/drm/tidss/tidss_drv.h | 11 +++-
>>>>>> 3 files changed, 47 insertions(+), 50 deletions(-)
>>>>>>
>>>>>> diff --git a/drivers/gpu/drm/tidss/tidss_dispc.c b/drivers/gpu/drm/
>>>>>> tidss/tidss_dispc.c
>>>>>> index c0277fa36425..c2c0fe0d4a0f 100644
>>>>>> --- a/drivers/gpu/drm/tidss/tidss_dispc.c
>>>>>> +++ b/drivers/gpu/drm/tidss/tidss_dispc.c
>>>>>> @@ -58,10 +58,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.
>>>>>> @@ -144,11 +140,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,
>>>>>> @@ -244,11 +235,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,
>>>>>> @@ -315,11 +301,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,
>>>>>> @@ -376,15 +357,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,
>>>>>> @@ -441,10 +413,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",
>>>>>> @@ -1347,25 +1315,57 @@ 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 int check_pixel_clock(struct dispc_device *dispc,
>>>>>> + u32 hw_videoport, unsigned long clock)
>>>>>> +{
>>>>>> + unsigned long round_clock;
>>>>>> +
>>>>>> + if (dispc->tidss->is_ext_vp_clk[hw_videoport])
>>>>>> + return 0;
>>>>>> +
>>>>>> + if (clock <= dispc->tidss->max_successful_rate[hw_videoport])
>>>>>> + return 0;
>>>>>> +
>>>>>> + if (clock < dispc->tidss->max_attempted_rate[hw_videoport])
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + round_clock = clk_round_rate(dispc->vp_clk[hw_videoport], clock);
>>>>>> +
>>>>>> + if (dispc_pclk_diff(clock, round_clock) > 5)
>>>>>> + return -EINVAL;
>>>>>> +
>>>>>> + dispc->tidss->max_successful_rate[hw_videoport] = round_clock;
>>>>>> + dispc->tidss->max_attempted_rate[hw_videoport] = clock;
>>>>>
>>>>> I still don't think this logic is sound. This is trying to find the
>>>>> maximum clock rate, and optimize by avoiding the calls to
>>>>> clk_round_rate() if possible. That makes sense.
>>>>>
>>>>> But checking for the 5% tolerance breaks it, in my opinion. If we find
>>>>> out that the PLL can do, say, 100M, but we need pclk of 90M, the
>>>>> current
>>>>> maximum is still the 100M, isn't it?
>>>>
>>>> 5% is pretty large indeed. We've been using .5% in multiple drivers and
>>>> it proved to be pretty ok. I would advise you tu use it too.
>>>
>>> The 5% comes from OMAP DSS, where we had to do pixel clock with a few
>>> dividers and multipliers. The rates were quite coarse, and we ended up
>>> having quite a large tolerance.
>>>
>>> I think with tidss, we always have a PLL we control, so we should always
>>> have very exact clocks. So I'm fine with dropping it to .5%. However,
>>> this patch and series is about removing the a-bit-too-hardcoded VP clk
>>> max rate code in the driver, so I would leave everything else to another
>>> series.
>>>
>>>> It's not clear to me why avoiding a clk_round_rate() call is something
>>>> worth doing though?
>>>
>>> Hard to say if it's worth doing, someone should make some perf tests.
>>> However, afaik, the calls do go to the firmware, so it involves
>>> inter-processor calls. On OMAP DSS checking the clock rates was slow, as
>>> it involved lots of iterating with dividers and multipliers. Perhaps
>>> it's much faster here.
>>>
>>>> Even caching the maximum rate you have been able to reach before is
>>>> pretty fragile: if the PLL changes its rate, or if a sibling clock has
>>>> set some limits on what the PLL can do, your maximum isn't relevant
>>>> anymore.
>>>
>>> You're right, although afaik it should not happen with TI's SoCs. We
>>> would be in trouble anyway if that were the case (e.g. someone starts
>>> the camera, and suddenly we can't support 1080p anymore).
>>>
>>>> in other words, what's wrong with simply calling clk_round_rate() and
>>>> checking if it's within a .5% deviation?
>>>
>>> This started with discussions how to replace the hardcoded max VP clock
>>> rate (used to quickly weed out impossible rates), which in reality was
>>> actually PLL max clock rate. We don't know the PLL max rate, and can't
>>> query it, so this approach was taken.
>>>
>>>> At the very least, this should be explained in comments or the commit
>>>> message.
>>>
>>> I agree.
>>>
>>> Swamil, can you do some perf tests with clk_round_rate()? If it's fast
>>> (enough), it will simplify the driver.
>>
>> Average execution time is around 112 us.
>> Trace file including the execution time for clk_round_rate(): https://
>> gist.github.com/swamiljain/2abe86982cdeba1d69223d2d525e0cb6
>> It is better to reduce calls to clk_round_rate().
>>
>> Need your suggestions for a better approach.
>
> We can cache the clk_round_rate calls. Checking my monitor, there are 36
> modes it offers me, but only 20 different pclk rates. Also, we could
> have multiple clk_round_rate calls happening in the driver for the same
> mode, and that would also be handled.
>
> Even if clk_round_rate takes a bit long, it only happens once (I hope)
> when an app does a modeset, and multiple times when a display is
> connected, I wonder if 100 us is an issue?
>
> Just using clk_round_rate() without any tricks would simplify the driver
> nicely, so I think we should try to see if we can get that working.
>
Are you suggesting to just check each time, a clock can be set using
clk_round_rate() and not cache max_pclk? But then we have to make sure
round_rate should be within some range(5% for us).
> Do you know if there's anything to improve on the clock side, ti-sci or
> firmare?
Not sure.
Regards,
Swamil
>
> Tomi
>
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2025-09-03 9:44 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-19 19:21 [PATCH v5 0/3] Decouple max_pclk check from constant display feats Swamil Jain
2025-08-19 19:21 ` [PATCH v5 1/3] drm/tidss: oldi: Add property to identify OLDI supported VP Swamil Jain
2025-08-19 19:21 ` [PATCH v5 2/3] drm/tidss: Remove max_pclk_khz from tidss display features Swamil Jain
2025-08-27 8:49 ` Tomi Valkeinen
2025-08-27 9:27 ` Maxime Ripard
2025-08-27 9:49 ` Tomi Valkeinen
2025-08-27 10:34 ` Maxime Ripard
2025-08-27 10:39 ` Tomi Valkeinen
2025-08-27 11:25 ` Maxime Ripard
2025-08-29 3:37 ` Swamil Jain
2025-09-03 8:38 ` Swamil Jain
2025-09-03 9:31 ` Tomi Valkeinen
2025-09-03 9:43 ` Swamil Jain
2025-08-29 3:35 ` Swamil Jain
2025-08-28 16:44 ` Swamil Jain
2025-08-19 19:21 ` [PATCH v5 3/3] drm/tidss: oldi: Add atomic_check hook for oldi bridge Swamil Jain
2025-08-27 9:05 ` Tomi Valkeinen
2025-08-29 3:50 ` Swamil Jain
2025-09-01 8:52 ` Tomi Valkeinen
2025-09-01 8:59 ` Swamil Jain
2025-08-21 12:09 ` [PATCH v5 0/3] Decouple max_pclk check from constant display feats Michael Walle
2025-08-28 15:59 ` Swamil Jain
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).