* [PATCH v2 1/6] phy: rockchip: samsung-hdptx: Fix rate recalculation for high bpc
2026-05-11 18:21 [PATCH v2 0/6] phy: rockchip: samsung-hdptx: Clock fixes and API transition cleanups Cristian Ciocaltea
@ 2026-05-11 18:21 ` Cristian Ciocaltea
2026-05-12 22:51 ` sashiko-bot
2026-05-11 18:21 ` [PATCH v2 2/6] phy: rockchip: samsung-hdptx: Handle uncommitted PHY config changes Cristian Ciocaltea
` (4 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Cristian Ciocaltea @ 2026-05-11 18:21 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Algea Cao,
Dmitry Baryshkov
Cc: kernel, linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Thomas Niederprüm, Simon Wright
The PHY PLL can be programmed by an external component, e.g. the
bootloader, just before the recalc_rate() callback is invoked during
devm_clk_hw_register() in the probe path.
Therefore rk_hdptx_phy_clk_recalc_rate() finds the PLL enabled and
attempts to compute the clock rate, while making use of the bpc value
from the HDMI PHY configuration, which always defaults to 8 because
phy_configure() was not run at that point. As a consequence, the
(re)calculated rate is incorrect when the actual bpc was higher than 8.
Do not rely on any of the hdmi_cfg members when computing the clock rate
and, instead, read the required input data (i.e. bpc), directly from the
hardware registers.
Fixes: 3481fc04d969 ("phy: rockchip: samsung-hdptx: Compute clk rate from PLL config")
Tested-by: Thomas Niederprüm <dubito@online.de>
Tested-by: Simon Wright <simon@symple.nz>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 2d973bc37f07..7fb1c22318bb 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -2168,7 +2168,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx)
struct lcpll_config lcpll_hw;
struct ropll_config ropll_hw;
u64 fout, sdm;
- u32 mode, val;
+ u32 mode, bpc, val;
int ret, i;
ret = regmap_read(hdptx->regmap, CMN_REG(0008), &mode);
@@ -2266,6 +2266,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx)
if (ret)
return 0;
ropll_hw.pms_sdiv = ((val & PLL_PCG_POSTDIV_SEL_MASK) >> 4) + 1;
+ bpc = (FIELD_GET(PLL_PCG_CLK_SEL_MASK, val) << 1) + 8;
fout = PLL_REF_CLK * ropll_hw.pms_mdiv;
if (ropll_hw.sdm_en) {
@@ -2280,7 +2281,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx)
fout = fout + sdm;
}
- return div_u64(fout * 2, ropll_hw.pms_sdiv * 10);
+ return div_u64(fout * 2 * 8, ropll_hw.pms_sdiv * 10 * bpc);
}
static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw,
@@ -2288,19 +2289,13 @@ static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw,
{
struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
u32 status;
- u64 rate;
int ret;
ret = regmap_read(hdptx->grf, GRF_HDPTX_CON0, &status);
if (ret || !(status & HDPTX_I_PLL_EN))
return 0;
- rate = rk_hdptx_phy_clk_calc_rate_from_pll_cfg(hdptx);
-
- if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
- return rate;
-
- return DIV_ROUND_CLOSEST_ULL(rate * 8, hdptx->hdmi_cfg.bpc);
+ return rk_hdptx_phy_clk_calc_rate_from_pll_cfg(hdptx);
}
static int rk_hdptx_phy_clk_determine_rate(struct clk_hw *hw,
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 1/6] phy: rockchip: samsung-hdptx: Fix rate recalculation for high bpc
2026-05-11 18:21 ` [PATCH v2 1/6] phy: rockchip: samsung-hdptx: Fix rate recalculation for high bpc Cristian Ciocaltea
@ 2026-05-12 22:51 ` sashiko-bot
2026-05-13 16:12 ` Cristian Ciocaltea
0 siblings, 1 reply; 11+ messages in thread
From: sashiko-bot @ 2026-05-12 22:51 UTC (permalink / raw)
To: Cristian Ciocaltea; +Cc: neil.armstrong, olteanv, vkoul, linux-phy
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Clock rate calculation drops a rounding step, replacing DIV_ROUND_CLOSEST_ULL() with truncation via div_u64(), which can cause a 1 Hz clock rate mismatch.
--
commit f013b4b7bc1d4ae235d28b10cad3143d009d131c
Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
phy: rockchip: samsung-hdptx: Fix rate recalculation for high bpc
This commit fixes the clock rate recalculation in the HDMI PHY probe path
by directly reading the bpc value from hardware registers instead of
relying on uninitialized configuration struct members.
> diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
> index 2d973bc37f076..7fb1c22318bbf 100644
> --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
> +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
> @@ -2280,7 +2281,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx)
> fout = fout + sdm;
> }
>
> - return div_u64(fout * 2, ropll_hw.pms_sdiv * 10);
> + return div_u64(fout * 2 * 8, ropll_hw.pms_sdiv * 10 * bpc);
> }
>
> static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw,
> @@ -2288,19 +2289,13 @@ static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw,
> {
> struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
> u32 status;
> - u64 rate;
> int ret;
>
> ret = regmap_read(hdptx->grf, GRF_HDPTX_CON0, &status);
> if (ret || !(status & HDPTX_I_PLL_EN))
> return 0;
>
> - rate = rk_hdptx_phy_clk_calc_rate_from_pll_cfg(hdptx);
> -
> - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
> - return rate;
> -
> - return DIV_ROUND_CLOSEST_ULL(rate * 8, hdptx->hdmi_cfg.bpc);
> + return rk_hdptx_phy_clk_calc_rate_from_pll_cfg(hdptx);
Does this inadvertently change the rounding behavior?
By moving the * 8 and / bpc math into rk_hdptx_phy_clk_calc_rate_from_pll_cfg()
and using div_u64(), the result is now truncated instead of rounded.
For example, if the exact calculated value ends in .6 or .8, the truncation
will yield a value 1 Hz lower than the rounded value.
Since rk_hdptx_phy_clk_determine_rate() still calculates the expected rate
using DIV_ROUND_CLOSEST_ULL(), could this truncation lead to a 1 Hz mismatch
in the Common Clock Framework?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260511-hdptx-clk-fixes-v2-0-664e41379cab@collabora.com?part=1
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v2 1/6] phy: rockchip: samsung-hdptx: Fix rate recalculation for high bpc
2026-05-12 22:51 ` sashiko-bot
@ 2026-05-13 16:12 ` Cristian Ciocaltea
0 siblings, 0 replies; 11+ messages in thread
From: Cristian Ciocaltea @ 2026-05-13 16:12 UTC (permalink / raw)
To: sashiko; +Cc: neil.armstrong, olteanv, vkoul, linux-phy
On 5/13/26 1:51 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] Clock rate calculation drops a rounding step, replacing DIV_ROUND_CLOSEST_ULL() with truncation via div_u64(), which can cause a 1 Hz clock rate mismatch.
> --
>
> commit f013b4b7bc1d4ae235d28b10cad3143d009d131c
> Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
>
> phy: rockchip: samsung-hdptx: Fix rate recalculation for high bpc
>
> This commit fixes the clock rate recalculation in the HDMI PHY probe path
> by directly reading the bpc value from hardware registers instead of
> relying on uninitialized configuration struct members.
>
>> diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
>> index 2d973bc37f076..7fb1c22318bbf 100644
>> --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
>> +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
>> @@ -2280,7 +2281,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx)
>> fout = fout + sdm;
>> }
>>
>> - return div_u64(fout * 2, ropll_hw.pms_sdiv * 10);
>> + return div_u64(fout * 2 * 8, ropll_hw.pms_sdiv * 10 * bpc);
>> }
>>
>> static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw,
>> @@ -2288,19 +2289,13 @@ static unsigned long rk_hdptx_phy_clk_recalc_rate(struct clk_hw *hw,
>> {
>> struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
>> u32 status;
>> - u64 rate;
>> int ret;
>>
>> ret = regmap_read(hdptx->grf, GRF_HDPTX_CON0, &status);
>> if (ret || !(status & HDPTX_I_PLL_EN))
>> return 0;
>>
>> - rate = rk_hdptx_phy_clk_calc_rate_from_pll_cfg(hdptx);
>> -
>> - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
>> - return rate;
>> -
>> - return DIV_ROUND_CLOSEST_ULL(rate * 8, hdptx->hdmi_cfg.bpc);
>> + return rk_hdptx_phy_clk_calc_rate_from_pll_cfg(hdptx);
>
> Does this inadvertently change the rounding behavior?
>
> By moving the * 8 and / bpc math into rk_hdptx_phy_clk_calc_rate_from_pll_cfg()
> and using div_u64(), the result is now truncated instead of rounded.
>
> For example, if the exact calculated value ends in .6 or .8, the truncation
> will yield a value 1 Hz lower than the rounded value.
>
> Since rk_hdptx_phy_clk_determine_rate() still calculates the expected rate
> using DIV_ROUND_CLOSEST_ULL(), could this truncation lead to a 1 Hz mismatch
> in the Common Clock Framework?
I couldn't notice any change in the functional behavior, but for consistency we
can stick with DIV_ROUND_CLOSEST_ULL(). I'll make this change in the next
revision, if any required, otherwise it will be submitted as part of the FRL
TxFFE level control series:
https://lore.kernel.org/all/20260328-hdptx-ffe-v1-0-53ebd5dea20a@collabora.com/
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 2/6] phy: rockchip: samsung-hdptx: Handle uncommitted PHY config changes
2026-05-11 18:21 [PATCH v2 0/6] phy: rockchip: samsung-hdptx: Clock fixes and API transition cleanups Cristian Ciocaltea
2026-05-11 18:21 ` [PATCH v2 1/6] phy: rockchip: samsung-hdptx: Fix rate recalculation for high bpc Cristian Ciocaltea
@ 2026-05-11 18:21 ` Cristian Ciocaltea
2026-05-12 23:57 ` sashiko-bot
2026-05-11 18:21 ` [PATCH v2 3/6] phy: rockchip: samsung-hdptx: Drop TMDS rate setup workaround Cristian Ciocaltea
` (3 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Cristian Ciocaltea @ 2026-05-11 18:21 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Algea Cao,
Dmitry Baryshkov
Cc: kernel, linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Thomas Niederprüm, Simon Wright
Any changes to the PHY link rate and/or color depth done via the HDMI
PHY configuration API are not immediately programmed into the hardware,
but are delayed until the PHY usage count gets incremented from 0 to 1,
that is when it is powered on or when the PLL clock exposed through
the CCF API is prepared, whichever comes first.
Since the clock might remain in prepared state after subsequent PHY
config changes, the programming can also be triggered via
clk_ops.set_rate(). However, from the clock consumer perspective (i.e.
VOP2 display controller), the (pixel) clock rate doesn't vary with bpc,
as that is handled internally by the PHY and reflected in the TDMS
character rate only.
As a consequence, changing the bpc while preserving the modeline may
lead to out-of-sync issues between CCF and HDMI PHY config state,
because the .set_rate() callback is not invoked when clock rate remains
constant. This may also happen when the PHY PLL has been pre-programmed
by an external entity, e.g. the bootloader, which is actually a
regression introduced by the recent FRL patches.
Introduce a pll_config_dirty flag to keep track of uncommitted PHY
config changes and use it in clk_ops.determine_rate() to invalidate the
current clock rate (as known by CCF) and, consequently, ensure those
changes are programmed into hardware via clk_ops.set_rate().
Moreover, proceed with a similar fix in phy_ops.power_on() callback, to
handle the scenario where the CCF API is not used due to operating in
FRL mode, while the clock is still in a prepared state and thus
preventing rk_hdptx_phy_consumer_get() to apply the updated PHY
configuration.
Fixes: de5dba833118 ("phy: rockchip: samsung-hdptx: Add HDMI 2.1 FRL support")
Fixes: 9d0ec51d7c22 ("phy: rockchip: samsung-hdptx: Add high color depth management")
Tested-by: Thomas Niederprüm <dubito@online.de>
Tested-by: Simon Wright <simon@symple.nz>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 85 +++++++++++++----------
1 file changed, 48 insertions(+), 37 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 7fb1c22318bb..12c259d9544c 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -413,6 +413,7 @@ struct rk_hdptx_phy {
/* clk provider */
struct clk_hw hw;
+ bool pll_config_dirty;
bool restrict_rate_change;
atomic_t usage_count;
@@ -1260,13 +1261,19 @@ static int rk_hdptx_tmds_ropll_cmn_config(struct rk_hdptx_phy *hdptx)
static int rk_hdptx_pll_cmn_config(struct rk_hdptx_phy *hdptx)
{
+ int ret;
+
if (hdptx->hdmi_cfg.rate <= HDMI20_MAX_RATE)
- return rk_hdptx_tmds_ropll_cmn_config(hdptx);
+ ret = rk_hdptx_tmds_ropll_cmn_config(hdptx);
+ else if (hdptx->hdmi_cfg.rate == FRL_8G4L_RATE)
+ ret = rk_hdptx_frl_lcpll_ropll_cmn_config(hdptx);
+ else
+ ret = rk_hdptx_frl_lcpll_cmn_config(hdptx);
- if (hdptx->hdmi_cfg.rate == FRL_8G4L_RATE)
- return rk_hdptx_frl_lcpll_ropll_cmn_config(hdptx);
+ if (!ret)
+ hdptx->pll_config_dirty = false;
- return rk_hdptx_frl_lcpll_cmn_config(hdptx);
+ return ret;
}
static int rk_hdptx_frl_lcpll_mode_config(struct rk_hdptx_phy *hdptx)
@@ -1347,25 +1354,22 @@ static int rk_hdptx_phy_consumer_get(struct rk_hdptx_phy *hdptx)
return 0;
ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &status);
- if (ret)
- goto dec_usage;
-
- if (status & HDPTX_O_PLL_LOCK_DONE)
- dev_warn(hdptx->dev, "PLL locked by unknown consumer!\n");
+ if (ret) {
+ atomic_dec(&hdptx->usage_count);
+ return ret;
+ }
if (mode == PHY_MODE_DP) {
rk_hdptx_dp_reset(hdptx);
} else {
- ret = rk_hdptx_pll_cmn_config(hdptx);
- if (ret)
- goto dec_usage;
+ /*
+ * Ignore PLL config errors at this point as pll_config_dirty
+ * was not reset and, therefore, operation will be retried.
+ */
+ rk_hdptx_pll_cmn_config(hdptx);
}
return 0;
-
-dec_usage:
- atomic_dec(&hdptx->usage_count);
- return ret;
}
static int rk_hdptx_phy_consumer_put(struct rk_hdptx_phy *hdptx, bool force)
@@ -1700,16 +1704,20 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
if (ret)
rk_hdptx_phy_consumer_put(hdptx, true);
} else {
- regmap_write(hdptx->grf, GRF_HDPTX_CON0,
- HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0));
+ if (hdptx->pll_config_dirty)
+ ret = rk_hdptx_pll_cmn_config(hdptx);
- if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
- ret = rk_hdptx_frl_lcpll_mode_config(hdptx);
- else
- ret = rk_hdptx_tmds_ropll_mode_config(hdptx);
+ if (!ret) {
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0,
+ HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0));
- if (ret)
+ if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
+ ret = rk_hdptx_frl_lcpll_mode_config(hdptx);
+ else
+ ret = rk_hdptx_tmds_ropll_mode_config(hdptx);
+ } else {
rk_hdptx_phy_consumer_put(hdptx, true);
+ }
}
return ret;
@@ -2081,7 +2089,10 @@ static int rk_hdptx_phy_configure(struct phy *phy, union phy_configure_opts *opt
dev_err(hdptx->dev, "invalid hdmi params for phy configure\n");
} else {
hdptx->restrict_rate_change = true;
- dev_dbg(hdptx->dev, "%s rate=%llu bpc=%u\n", __func__,
+ hdptx->pll_config_dirty = true;
+
+ dev_dbg(hdptx->dev, "%s %s rate=%llu bpc=%u\n", __func__,
+ hdptx->hdmi_cfg.mode ? "FRL" : "TMDS",
hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc);
}
@@ -2303,8 +2314,19 @@ static int rk_hdptx_phy_clk_determine_rate(struct clk_hw *hw,
{
struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
- if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
- return hdptx->hdmi_cfg.rate;
+ /*
+ * Invalidate current clock rate to ensure rk_hdptx_phy_clk_set_rate()
+ * will be invoked to commit PLL configuration.
+ */
+ if (hdptx->pll_config_dirty) {
+ req->rate = 0;
+ return 0;
+ }
+
+ if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) {
+ req->rate = hdptx->hdmi_cfg.rate;
+ return 0;
+ }
/*
* FIXME: Temporarily allow altering TMDS char rate via CCF.
@@ -2336,17 +2358,6 @@ static int rk_hdptx_phy_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
- unsigned long long link_rate = rate;
-
- if (hdptx->hdmi_cfg.mode != PHY_HDMI_MODE_FRL)
- link_rate = DIV_ROUND_CLOSEST_ULL(rate * hdptx->hdmi_cfg.bpc, 8);
-
- /* Revert any unlikely link rate change since determine_rate() */
- if (hdptx->hdmi_cfg.rate != link_rate) {
- dev_warn(hdptx->dev, "Reverting unexpected rate change from %llu to %llu\n",
- link_rate, hdptx->hdmi_cfg.rate);
- hdptx->hdmi_cfg.rate = link_rate;
- }
/*
* The link rate would be normally programmed in HW during
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 2/6] phy: rockchip: samsung-hdptx: Handle uncommitted PHY config changes
2026-05-11 18:21 ` [PATCH v2 2/6] phy: rockchip: samsung-hdptx: Handle uncommitted PHY config changes Cristian Ciocaltea
@ 2026-05-12 23:57 ` sashiko-bot
2026-05-13 17:15 ` Cristian Ciocaltea
0 siblings, 1 reply; 11+ messages in thread
From: sashiko-bot @ 2026-05-12 23:57 UTC (permalink / raw)
To: Cristian Ciocaltea; +Cc: vkoul, olteanv, linux-phy, neil.armstrong
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Abuse of CCF determine_rate corrupts the clock tree rate cache.
- [High] Ignored return value in clk_ops.prepare masks hardware initialization failures.
- [High] Missing mutual exclusion between CCF and PHY frameworks causes concurrent hardware corruption.
- [High] Unbalanced PHY usage count on lane mode configuration failure in rk_hdptx_phy_power_on.
--
commit 53b6461d3d35f9f83438d0a57cf5379f7c1fcba8
Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
phy: rockchip: samsung-hdptx: Handle uncommitted PHY config changes
Any changes to the PHY link rate and/or color depth done via the HDMI
PHY configuration API are not immediately programmed into the hardware,
but are delayed until the PHY usage count gets incremented from 0 to 1,
that is when it is powered on or when the PLL clock exposed through
the CCF API is prepared, whichever comes first.
[ ... ]
> diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
> index 7fb1c22318bbf..12c259d9544cf 100644
> --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
> +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
> @@ -1347,25 +1354,22 @@ static int rk_hdptx_phy_consumer_get(struct rk_hdptx_phy *hdptx)
> return 0;
>
> ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &status);
> - if (ret)
> - goto dec_usage;
> -
> - if (status & HDPTX_O_PLL_LOCK_DONE)
> - dev_warn(hdptx->dev, "PLL locked by unknown consumer!\n");
> + if (ret) {
> + atomic_dec(&hdptx->usage_count);
> + return ret;
> + }
>
> if (mode == PHY_MODE_DP) {
> rk_hdptx_dp_reset(hdptx);
> } else {
> - ret = rk_hdptx_pll_cmn_config(hdptx);
> - if (ret)
> - goto dec_usage;
> + /*
> + * Ignore PLL config errors at this point as pll_config_dirty
> + * was not reset and, therefore, operation will be retried.
> + */
> + rk_hdptx_pll_cmn_config(hdptx);
> }
>
> return 0;
Does dropping the return value of rk_hdptx_pll_cmn_config() mask hardware
initialization failures?
If pll_config_dirty is false (for example, the clock was previously prepared
and unprepared), a subsequent failure won't be retried later.
Also, if a caller invokes clk_prepare_enable() but not clk_set_rate() or
phy_power_on(), will the hardware remain unconfigured while CCF incorrectly
assumes the clock is running?
> @@ -1700,16 +1704,20 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
> if (ret)
> rk_hdptx_phy_consumer_put(hdptx, true);
> } else {
> - regmap_write(hdptx->grf, GRF_HDPTX_CON0,
> - HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0));
> + if (hdptx->pll_config_dirty)
> + ret = rk_hdptx_pll_cmn_config(hdptx);
>
> - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
> - ret = rk_hdptx_frl_lcpll_mode_config(hdptx);
> - else
> - ret = rk_hdptx_tmds_ropll_mode_config(hdptx);
> + if (!ret) {
> + regmap_write(hdptx->grf, GRF_HDPTX_CON0,
> + HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0));
>
> - if (ret)
> + if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
> + ret = rk_hdptx_frl_lcpll_mode_config(hdptx);
> + else
> + ret = rk_hdptx_tmds_ropll_mode_config(hdptx);
> + } else {
> rk_hdptx_phy_consumer_put(hdptx, true);
> + }
> }
If rk_hdptx_tmds_ropll_mode_config() or rk_hdptx_frl_lcpll_mode_config()
fails, ret is set but the else block is never executed. Does this leave
usage_count unbalanced and prevent the PHY from being powered down? I noted
this was addressed later in the series by commit "phy: rockchip:
samsung-hdptx: Drop TMDS rate setup workaround", but wanted to point it out.
Also, does this logic introduce a race condition between CCF (clk_prepare)
and the PHY framework (phy_power_on)? The usage_count is atomic, but there
appears to be no mutual exclusion for the hardware configuration itself.
If Thread A calls clk_prepare() and increments the count to 1, it starts the
lengthy rk_hdptx_pll_cmn_config() hardware sequence.
If Thread B calls phy_power_on() concurrently and increments the count to 2,
it returns immediately from rk_hdptx_phy_consumer_get(). Thread B will then
evaluate hdptx->pll_config_dirty, see it is still true, and concurrently
execute rk_hdptx_pll_cmn_config() while Thread A is already modifying the
same registers.
> @@ -2303,8 +2314,19 @@ static int rk_hdptx_phy_clk_determine_rate(struct clk_hw *hw,
> {
> struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
>
> - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
> - return hdptx->hdmi_cfg.rate;
> + /*
> + * Invalidate current clock rate to ensure rk_hdptx_phy_clk_set_rate()
> + * will be invoked to commit PLL configuration.
> + */
> + if (hdptx->pll_config_dirty) {
> + req->rate = 0;
> + return 0;
> + }
Does returning req->rate = 0 here corrupt the clock tree rate cache?
By returning 0 to bypass CCF's optimization and force set_rate(), the Common
Clock Framework will cache 0 Hz as the actual hardware rate and propagate it
downstream.
Could this cause downstream consumers (like the VOP2 display clock dclk)
or rate change notifiers to receive 0 Hz, potentially leading to
division-by-zero errors or broken pixel clock math?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260511-hdptx-clk-fixes-v2-0-664e41379cab@collabora.com?part=2
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 11+ messages in thread* Re: [PATCH v2 2/6] phy: rockchip: samsung-hdptx: Handle uncommitted PHY config changes
2026-05-12 23:57 ` sashiko-bot
@ 2026-05-13 17:15 ` Cristian Ciocaltea
0 siblings, 0 replies; 11+ messages in thread
From: Cristian Ciocaltea @ 2026-05-13 17:15 UTC (permalink / raw)
To: sashiko-reviews; +Cc: vkoul, olteanv, linux-phy, neil.armstrong
On 5/13/26 2:57 AM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> - [High] Abuse of CCF determine_rate corrupts the clock tree rate cache.
> - [High] Ignored return value in clk_ops.prepare masks hardware initialization failures.
> - [High] Missing mutual exclusion between CCF and PHY frameworks causes concurrent hardware corruption.
> - [High] Unbalanced PHY usage count on lane mode configuration failure in rk_hdptx_phy_power_on.
> --
>
> commit 53b6461d3d35f9f83438d0a57cf5379f7c1fcba8
> Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
>
> phy: rockchip: samsung-hdptx: Handle uncommitted PHY config changes
>
> Any changes to the PHY link rate and/or color depth done via the HDMI
> PHY configuration API are not immediately programmed into the hardware,
> but are delayed until the PHY usage count gets incremented from 0 to 1,
> that is when it is powered on or when the PLL clock exposed through
> the CCF API is prepared, whichever comes first.
>
> [ ... ]
>
>> diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
>> index 7fb1c22318bbf..12c259d9544cf 100644
>> --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
>> +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
>> @@ -1347,25 +1354,22 @@ static int rk_hdptx_phy_consumer_get(struct rk_hdptx_phy *hdptx)
>> return 0;
>>
>> ret = regmap_read(hdptx->grf, GRF_HDPTX_STATUS, &status);
>> - if (ret)
>> - goto dec_usage;
>> -
>> - if (status & HDPTX_O_PLL_LOCK_DONE)
>> - dev_warn(hdptx->dev, "PLL locked by unknown consumer!\n");
>> + if (ret) {
>> + atomic_dec(&hdptx->usage_count);
>> + return ret;
>> + }
>>
>> if (mode == PHY_MODE_DP) {
>> rk_hdptx_dp_reset(hdptx);
>> } else {
>> - ret = rk_hdptx_pll_cmn_config(hdptx);
>> - if (ret)
>> - goto dec_usage;
>> + /*
>> + * Ignore PLL config errors at this point as pll_config_dirty
>> + * was not reset and, therefore, operation will be retried.
>> + */
>> + rk_hdptx_pll_cmn_config(hdptx);
>> }
>>
>> return 0;
>
> Does dropping the return value of rk_hdptx_pll_cmn_config() mask hardware
> initialization failures?
No, it's done on purpose, per the comment above (the error is not critical at
this point and we don't want to bail out too early).
>
> If pll_config_dirty is false (for example, the clock was previously prepared
> and unprepared), a subsequent failure won't be retried later.
The retry is done either by rk_hdptx_phy_clk_set_rate() or
rk_hdptx_phy_power_on(), whichever comes first.
>
> Also, if a caller invokes clk_prepare_enable() but not clk_set_rate() or
> phy_power_on(), will the hardware remain unconfigured while CCF incorrectly
> assumes the clock is running?
That's not a valid usecase, the hardware will be eventually programmed by one of
the two functions above - the clock provider is dependent on the PHY
configuration, which may happen after clk_prepare_enable().
>
>> @@ -1700,16 +1704,20 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
>> if (ret)
>> rk_hdptx_phy_consumer_put(hdptx, true);
>> } else {
>> - regmap_write(hdptx->grf, GRF_HDPTX_CON0,
>> - HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0));
>> + if (hdptx->pll_config_dirty)
>> + ret = rk_hdptx_pll_cmn_config(hdptx);
>>
>> - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
>> - ret = rk_hdptx_frl_lcpll_mode_config(hdptx);
>> - else
>> - ret = rk_hdptx_tmds_ropll_mode_config(hdptx);
>> + if (!ret) {
>> + regmap_write(hdptx->grf, GRF_HDPTX_CON0,
>> + HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0));
>>
>> - if (ret)
>> + if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
>> + ret = rk_hdptx_frl_lcpll_mode_config(hdptx);
>> + else
>> + ret = rk_hdptx_tmds_ropll_mode_config(hdptx);
>> + } else {
>> rk_hdptx_phy_consumer_put(hdptx, true);
>> + }
>> }
>
> If rk_hdptx_tmds_ropll_mode_config() or rk_hdptx_frl_lcpll_mode_config()
> fails, ret is set but the else block is never executed. Does this leave
> usage_count unbalanced and prevent the PHY from being powered down? I noted
> this was addressed later in the series by commit "phy: rockchip:
> samsung-hdptx: Drop TMDS rate setup workaround", but wanted to point it out.
If rk_hdptx_pll_cmn_config() above succeeds, it's very unlikely any of the
*_mode_config() calls fails. The indicated commit does indeed eliminate this
theoretical issue - I can still handle it in this patch if a new revision is
required.
>
> Also, does this logic introduce a race condition between CCF (clk_prepare)
> and the PHY framework (phy_power_on)? The usage_count is atomic, but there
> appears to be no mutual exclusion for the hardware configuration itself.
>
> If Thread A calls clk_prepare() and increments the count to 1, it starts the
> lengthy rk_hdptx_pll_cmn_config() hardware sequence.
>
> If Thread B calls phy_power_on() concurrently and increments the count to 2,
> it returns immediately from rk_hdptx_phy_consumer_get(). Thread B will then
> evaluate hdptx->pll_config_dirty, see it is still true, and concurrently
> execute rk_hdptx_pll_cmn_config() while Thread A is already modifying the
> same registers.
No, that's not a valid usecase - the calls are orchestrated by the DRM subystem,
which guaranties the sequence of operations, e.g. configuring PHY (w/o HW
programming) -> setting clk rate (w/ HW programming) -> powering PHY (w/ HW
progamming).
>
>> @@ -2303,8 +2314,19 @@ static int rk_hdptx_phy_clk_determine_rate(struct clk_hw *hw,
>> {
>> struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
>>
>> - if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
>> - return hdptx->hdmi_cfg.rate;
>> + /*
>> + * Invalidate current clock rate to ensure rk_hdptx_phy_clk_set_rate()
>> + * will be invoked to commit PLL configuration.
>> + */
>> + if (hdptx->pll_config_dirty) {
>> + req->rate = 0;
>> + return 0;
>> + }
>
> Does returning req->rate = 0 here corrupt the clock tree rate cache?
>
> By returning 0 to bypass CCF's optimization and force set_rate(), the Common
> Clock Framework will cache 0 Hz as the actual hardware rate and propagate it
> downstream.
>
> Could this cause downstream consumers (like the VOP2 display clock dclk)
> or rate change notifiers to receive 0 Hz, potentially leading to
> division-by-zero errors or broken pixel clock math?
No, invalidating the CCF rate doesn't have any negative side effect - this is
very short-lived state which ensures a set_rate() call from VOP2 is imminent.
This is really necessary in order to address the usecases described in the
commit message, e.g. pixel clock rate not varying with bpc (from consumer/VOP2
perspective).
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 3/6] phy: rockchip: samsung-hdptx: Drop TMDS rate setup workaround
2026-05-11 18:21 [PATCH v2 0/6] phy: rockchip: samsung-hdptx: Clock fixes and API transition cleanups Cristian Ciocaltea
2026-05-11 18:21 ` [PATCH v2 1/6] phy: rockchip: samsung-hdptx: Fix rate recalculation for high bpc Cristian Ciocaltea
2026-05-11 18:21 ` [PATCH v2 2/6] phy: rockchip: samsung-hdptx: Handle uncommitted PHY config changes Cristian Ciocaltea
@ 2026-05-11 18:21 ` Cristian Ciocaltea
2026-05-11 18:21 ` [PATCH v2 4/6] phy: rockchip: samsung-hdptx: Drop restrict_rate_change handling Cristian Ciocaltea
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Cristian Ciocaltea @ 2026-05-11 18:21 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Algea Cao,
Dmitry Baryshkov
Cc: kernel, linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Thomas Niederprüm, Simon Wright
Since commit ba9c2fe18c17 ("drm/rockchip: dw_hdmi_qp: Switch to
phy_configure()") the TMDS rate setup doesn't rely anymore on the
unconventional usage of the bus width, instead it is managed exclusively
through the HDMI PHY configuration API.
Drop the now obsolete workaround to retrieve the TMDS character rate via
phy_get_bus_width() during power_on().
While at it, get rid of the extra call to rk_hdptx_phy_consumer_put() by
moving the statement at the end of the function.
Tested-by: Thomas Niederprüm <dubito@online.de>
Tested-by: Simon Wright <simon@symple.nz>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 26 ++++++-----------------
1 file changed, 6 insertions(+), 20 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 12c259d9544c..b5354a24b6eb 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -1660,22 +1660,6 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
enum phy_mode mode = phy_get_mode(phy);
int ret, lane;
- if (mode != PHY_MODE_DP) {
- if (!hdptx->hdmi_cfg.rate && hdptx->hdmi_cfg.mode != PHY_HDMI_MODE_FRL) {
- /*
- * FIXME: Temporary workaround to setup TMDS char rate
- * from the RK DW HDMI QP bridge driver.
- * Will be removed as soon the switch to the HDMI PHY
- * configuration API has been completed on both ends.
- */
- hdptx->hdmi_cfg.rate = phy_get_bus_width(hdptx->phy) & 0xfffffff;
- hdptx->hdmi_cfg.rate *= 100;
- }
-
- dev_dbg(hdptx->dev, "%s rate=%llu bpc=%u\n", __func__,
- hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc);
- }
-
ret = rk_hdptx_phy_consumer_get(hdptx);
if (ret)
return ret;
@@ -1701,9 +1685,10 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
rk_hdptx_dp_pll_init(hdptx);
ret = rk_hdptx_dp_aux_init(hdptx);
- if (ret)
- rk_hdptx_phy_consumer_put(hdptx, true);
} else {
+ dev_dbg(hdptx->dev, "%s rate=%llu bpc=%u\n", __func__,
+ hdptx->hdmi_cfg.rate, hdptx->hdmi_cfg.bpc);
+
if (hdptx->pll_config_dirty)
ret = rk_hdptx_pll_cmn_config(hdptx);
@@ -1715,11 +1700,12 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
ret = rk_hdptx_frl_lcpll_mode_config(hdptx);
else
ret = rk_hdptx_tmds_ropll_mode_config(hdptx);
- } else {
- rk_hdptx_phy_consumer_put(hdptx, true);
}
}
+ if (ret)
+ rk_hdptx_phy_consumer_put(hdptx, true);
+
return ret;
}
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 4/6] phy: rockchip: samsung-hdptx: Drop restrict_rate_change handling
2026-05-11 18:21 [PATCH v2 0/6] phy: rockchip: samsung-hdptx: Clock fixes and API transition cleanups Cristian Ciocaltea
` (2 preceding siblings ...)
2026-05-11 18:21 ` [PATCH v2 3/6] phy: rockchip: samsung-hdptx: Drop TMDS rate setup workaround Cristian Ciocaltea
@ 2026-05-11 18:21 ` Cristian Ciocaltea
2026-05-11 18:21 ` [PATCH v2 5/6] phy: rockchip: samsung-hdptx: Simplify GRF access with FIELD_PREP_WM16() Cristian Ciocaltea
2026-05-11 18:21 ` [PATCH v2 6/6] phy: rockchip: samsung-hdptx: Consistently use bitfield macros Cristian Ciocaltea
5 siblings, 0 replies; 11+ messages in thread
From: Cristian Ciocaltea @ 2026-05-11 18:21 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Algea Cao,
Dmitry Baryshkov
Cc: kernel, linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Thomas Niederprüm, Simon Wright
Since commit 6efbd0f46dd8 ("phy: rockchip: samsung-hdptx: Restrict
altering TMDS char rate via CCF"), adjusting the rate via the Common
Clock Framework API has been disallowed.
To avoid breaking existing users until switching to the PHY config API,
it introduced a temporary exception to the rule, controlled via the
'restrict_rate_change' flag.
As the API transition completed, remove the now deprecated exception
logic.
Tested-by: Thomas Niederprüm <dubito@online.de>
Tested-by: Simon Wright <simon@symple.nz>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 42 +++++------------------
1 file changed, 8 insertions(+), 34 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index b5354a24b6eb..5c3a9b4b1737 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -414,7 +414,6 @@ struct rk_hdptx_phy {
/* clk provider */
struct clk_hw hw;
bool pll_config_dirty;
- bool restrict_rate_change;
atomic_t usage_count;
@@ -2074,7 +2073,6 @@ static int rk_hdptx_phy_configure(struct phy *phy, union phy_configure_opts *opt
if (ret) {
dev_err(hdptx->dev, "invalid hdmi params for phy configure\n");
} else {
- hdptx->restrict_rate_change = true;
hdptx->pll_config_dirty = true;
dev_dbg(hdptx->dev, "%s %s rate=%llu bpc=%u\n", __func__,
@@ -2301,41 +2299,17 @@ static int rk_hdptx_phy_clk_determine_rate(struct clk_hw *hw,
struct rk_hdptx_phy *hdptx = to_rk_hdptx_phy(hw);
/*
- * Invalidate current clock rate to ensure rk_hdptx_phy_clk_set_rate()
- * will be invoked to commit PLL configuration.
+ * For uncommitted PLL configuration, invalidate the current clock rate
+ * to ensure rk_hdptx_phy_clk_set_rate() will be always invoked.
+ * Otherwise, restrict the rate according to the PHY link setup.
*/
- if (hdptx->pll_config_dirty) {
+ if (hdptx->pll_config_dirty)
req->rate = 0;
- return 0;
- }
-
- if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL) {
+ else if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
req->rate = hdptx->hdmi_cfg.rate;
- return 0;
- }
-
- /*
- * FIXME: Temporarily allow altering TMDS char rate via CCF.
- * To be dropped as soon as the RK DW HDMI QP bridge driver
- * switches to make use of phy_configure().
- */
- if (!hdptx->restrict_rate_change && req->rate != hdptx->hdmi_cfg.rate) {
- struct phy_configure_opts_hdmi hdmi = {
- .tmds_char_rate = req->rate,
- };
-
- int ret = rk_hdptx_phy_verify_hdmi_config(hdptx, &hdmi, &hdptx->hdmi_cfg);
-
- if (ret)
- return ret;
- }
-
- /*
- * The TMDS char rate shall be adjusted via phy_configure() only,
- * hence ensure rk_hdptx_phy_clk_set_rate() won't be invoked with
- * a different rate argument.
- */
- req->rate = DIV_ROUND_CLOSEST_ULL(hdptx->hdmi_cfg.rate * 8, hdptx->hdmi_cfg.bpc);
+ else
+ req->rate = DIV_ROUND_CLOSEST_ULL(hdptx->hdmi_cfg.rate * 8,
+ hdptx->hdmi_cfg.bpc);
return 0;
}
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 5/6] phy: rockchip: samsung-hdptx: Simplify GRF access with FIELD_PREP_WM16()
2026-05-11 18:21 [PATCH v2 0/6] phy: rockchip: samsung-hdptx: Clock fixes and API transition cleanups Cristian Ciocaltea
` (3 preceding siblings ...)
2026-05-11 18:21 ` [PATCH v2 4/6] phy: rockchip: samsung-hdptx: Drop restrict_rate_change handling Cristian Ciocaltea
@ 2026-05-11 18:21 ` Cristian Ciocaltea
2026-05-11 18:21 ` [PATCH v2 6/6] phy: rockchip: samsung-hdptx: Consistently use bitfield macros Cristian Ciocaltea
5 siblings, 0 replies; 11+ messages in thread
From: Cristian Ciocaltea @ 2026-05-11 18:21 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Algea Cao,
Dmitry Baryshkov
Cc: kernel, linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Thomas Niederprüm, Simon Wright
The 16 most significant bits of the general-purpose register (GRF) are
used as a write-enable mask for the remaining 16 bits.
Make use of the recently introduced FIELD_PREP_WM16() macro to avoid
open-coding the bit shift operations and improve code readability.
Tested-by: Thomas Niederprüm <dubito@online.de>
Tested-by: Simon Wright <simon@symple.nz>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 52 +++++++++++------------
1 file changed, 25 insertions(+), 27 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 5c3a9b4b1737..611425e44b26 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
- * Copyright (c) 2024 Collabora Ltd.
+ * Copyright (c) 2024-2026 Collabora Ltd.
*
* Author: Algea Cao <algea.cao@rock-chips.com>
* Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
@@ -10,6 +10,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
+#include <linux/hw_bitfield.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -949,7 +950,9 @@ static void rk_hdptx_pre_power_up(struct rk_hdptx_phy *hdptx)
reset_control_assert(hdptx->rsts[RST_CMN].rstc);
reset_control_assert(hdptx->rsts[RST_INIT].rstc);
- val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16;
+ val = (FIELD_PREP_WM16(HDPTX_I_PLL_EN, 0) |
+ FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 0) |
+ FIELD_PREP_WM16(HDPTX_I_BGR_EN, 0));
regmap_write(hdptx->grf, GRF_HDPTX_CON0, val);
}
@@ -960,8 +963,8 @@ static int rk_hdptx_post_enable_lane(struct rk_hdptx_phy *hdptx)
reset_control_deassert(hdptx->rsts[RST_LANE].rstc);
- val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 |
- HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN;
+ val = (FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 1) |
+ FIELD_PREP_WM16(HDPTX_I_BGR_EN, 1));
regmap_write(hdptx->grf, GRF_HDPTX_CON0, val);
/* 3 lanes FRL mode */
@@ -990,16 +993,15 @@ static int rk_hdptx_post_enable_pll(struct rk_hdptx_phy *hdptx)
u32 val;
int ret;
- val = (HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16 |
- HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN;
+ val = (FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 1) |
+ FIELD_PREP_WM16(HDPTX_I_BGR_EN, 1));
regmap_write(hdptx->grf, GRF_HDPTX_CON0, val);
usleep_range(10, 15);
reset_control_deassert(hdptx->rsts[RST_INIT].rstc);
usleep_range(10, 15);
- val = HDPTX_I_PLL_EN << 16 | HDPTX_I_PLL_EN;
- regmap_write(hdptx->grf, GRF_HDPTX_CON0, val);
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(HDPTX_I_PLL_EN, 1));
usleep_range(10, 15);
reset_control_deassert(hdptx->rsts[RST_CMN].rstc);
@@ -1037,7 +1039,9 @@ static void rk_hdptx_phy_disable(struct rk_hdptx_phy *hdptx)
reset_control_assert(hdptx->rsts[RST_CMN].rstc);
reset_control_assert(hdptx->rsts[RST_INIT].rstc);
- val = (HDPTX_I_PLL_EN | HDPTX_I_BIAS_EN | HDPTX_I_BGR_EN) << 16;
+ val = (FIELD_PREP_WM16(HDPTX_I_PLL_EN, 0) |
+ FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 0) |
+ FIELD_PREP_WM16(HDPTX_I_BGR_EN, 0));
regmap_write(hdptx->grf, GRF_HDPTX_CON0, val);
}
@@ -1135,7 +1139,7 @@ static int rk_hdptx_frl_lcpll_cmn_config(struct rk_hdptx_phy *hdptx)
rk_hdptx_pre_power_up(hdptx);
- regmap_write(hdptx->grf, GRF_HDPTX_CON0, LC_REF_CLK_SEL << 16);
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(LC_REF_CLK_SEL, 0));
rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_cmn_init_seq);
rk_hdptx_multi_reg_write(hdptx, rk_hdptx_frl_lcpll_cmn_init_seq);
@@ -1178,8 +1182,7 @@ static int rk_hdptx_frl_lcpll_ropll_cmn_config(struct rk_hdptx_phy *hdptx)
rk_hdptx_pre_power_up(hdptx);
/* ROPLL input reference clock from LCPLL (cascade mode) */
- regmap_write(hdptx->grf, GRF_HDPTX_CON0,
- (LC_REF_CLK_SEL << 16) | LC_REF_CLK_SEL);
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(LC_REF_CLK_SEL, 1));
rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_cmn_init_seq);
rk_hdptx_multi_reg_write(hdptx, rk_hdptx_frl_lcpll_ropll_cmn_init_seq);
@@ -1218,7 +1221,7 @@ static int rk_hdptx_tmds_ropll_cmn_config(struct rk_hdptx_phy *hdptx)
rk_hdptx_pre_power_up(hdptx);
- regmap_write(hdptx->grf, GRF_HDPTX_CON0, LC_REF_CLK_SEL << 16);
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(LC_REF_CLK_SEL, 0));
rk_hdptx_multi_reg_write(hdptx, rk_hdptx_common_cmn_init_seq);
rk_hdptx_multi_reg_write(hdptx, rk_hdptx_tmds_cmn_init_seq);
@@ -1336,11 +1339,9 @@ static void rk_hdptx_dp_reset(struct rk_hdptx_phy *hdptx)
FIELD_PREP(LN_TX_DRV_EI_EN_MASK, 0));
regmap_write(hdptx->grf, GRF_HDPTX_CON0,
- HDPTX_I_PLL_EN << 16 | FIELD_PREP(HDPTX_I_PLL_EN, 0x0));
- regmap_write(hdptx->grf, GRF_HDPTX_CON0,
- HDPTX_I_BIAS_EN << 16 | FIELD_PREP(HDPTX_I_BIAS_EN, 0x0));
- regmap_write(hdptx->grf, GRF_HDPTX_CON0,
- HDPTX_I_BGR_EN << 16 | FIELD_PREP(HDPTX_I_BGR_EN, 0x0));
+ FIELD_PREP_WM16(HDPTX_I_PLL_EN, 0) |
+ FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 0) |
+ FIELD_PREP_WM16(HDPTX_I_BGR_EN, 0));
}
static int rk_hdptx_phy_consumer_get(struct rk_hdptx_phy *hdptx)
@@ -1616,9 +1617,8 @@ static int rk_hdptx_dp_aux_init(struct rk_hdptx_phy *hdptx)
FIELD_PREP(OVRD_SB_VREG_EN_MASK, 0x1));
regmap_write(hdptx->grf, GRF_HDPTX_CON0,
- HDPTX_I_BGR_EN << 16 | FIELD_PREP(HDPTX_I_BGR_EN, 0x1));
- regmap_write(hdptx->grf, GRF_HDPTX_CON0,
- HDPTX_I_BIAS_EN << 16 | FIELD_PREP(HDPTX_I_BIAS_EN, 0x1));
+ FIELD_PREP_WM16(HDPTX_I_BGR_EN, 1) |
+ FIELD_PREP_WM16(HDPTX_I_BIAS_EN, 1));
usleep_range(20, 25);
reset_control_deassert(hdptx->rsts[RST_INIT].rstc);
@@ -1665,7 +1665,7 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
if (mode == PHY_MODE_DP) {
regmap_write(hdptx->grf, GRF_HDPTX_CON0,
- HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x1));
+ FIELD_PREP_WM16(HDPTX_MODE_SEL, 1));
for (lane = 0; lane < 4; lane++) {
regmap_update_bits(hdptx->regmap, LANE_REG(031e) + 0x400 * lane,
@@ -1693,7 +1693,7 @@ static int rk_hdptx_phy_power_on(struct phy *phy)
if (!ret) {
regmap_write(hdptx->grf, GRF_HDPTX_CON0,
- HDPTX_MODE_SEL << 16 | FIELD_PREP(HDPTX_MODE_SEL, 0x0));
+ FIELD_PREP_WM16(HDPTX_MODE_SEL, 0));
if (hdptx->hdmi_cfg.mode == PHY_HDMI_MODE_FRL)
ret = rk_hdptx_frl_lcpll_mode_config(hdptx);
@@ -1828,8 +1828,7 @@ static int rk_hdptx_phy_set_rate(struct rk_hdptx_phy *hdptx,
u32 bw, status;
int ret;
- regmap_write(hdptx->grf, GRF_HDPTX_CON0,
- HDPTX_I_PLL_EN << 16 | FIELD_PREP(HDPTX_I_PLL_EN, 0x0));
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(HDPTX_I_PLL_EN, 0));
switch (dp->link_rate) {
case 1620:
@@ -1885,8 +1884,7 @@ static int rk_hdptx_phy_set_rate(struct rk_hdptx_phy *hdptx,
regmap_update_bits(hdptx->regmap, CMN_REG(0095), DP_TX_LINK_BW_MASK,
FIELD_PREP(DP_TX_LINK_BW_MASK, bw));
- regmap_write(hdptx->grf, GRF_HDPTX_CON0,
- HDPTX_I_PLL_EN << 16 | FIELD_PREP(HDPTX_I_PLL_EN, 0x1));
+ regmap_write(hdptx->grf, GRF_HDPTX_CON0, FIELD_PREP_WM16(HDPTX_I_PLL_EN, 1));
ret = regmap_read_poll_timeout(hdptx->grf, GRF_HDPTX_STATUS,
status, FIELD_GET(HDPTX_O_PLL_LOCK_DONE, status),
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v2 6/6] phy: rockchip: samsung-hdptx: Consistently use bitfield macros
2026-05-11 18:21 [PATCH v2 0/6] phy: rockchip: samsung-hdptx: Clock fixes and API transition cleanups Cristian Ciocaltea
` (4 preceding siblings ...)
2026-05-11 18:21 ` [PATCH v2 5/6] phy: rockchip: samsung-hdptx: Simplify GRF access with FIELD_PREP_WM16() Cristian Ciocaltea
@ 2026-05-11 18:21 ` Cristian Ciocaltea
5 siblings, 0 replies; 11+ messages in thread
From: Cristian Ciocaltea @ 2026-05-11 18:21 UTC (permalink / raw)
To: Vinod Koul, Neil Armstrong, Heiko Stuebner, Algea Cao,
Dmitry Baryshkov
Cc: kernel, linux-phy, linux-arm-kernel, linux-rockchip, linux-kernel,
Thomas Niederprüm, Simon Wright
Make the code more robust and improve readability by using the available
bitfield macros (e.g. FIELD_PREP, FIELD_GET) whenever possible, instead
of open coding the related bit operations.
Tested-by: Thomas Niederprüm <dubito@online.de>
Tested-by: Simon Wright <simon@symple.nz>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c | 24 ++++++++++++++++-------
1 file changed, 17 insertions(+), 7 deletions(-)
diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
index 611425e44b26..2bd794360661 100644
--- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
@@ -53,6 +53,12 @@
/* CMN_REG(001e) */
#define LCPLL_PI_EN_MASK BIT(5)
#define LCPLL_100M_CLK_EN_MASK BIT(0)
+/* CMN_REG(0022) */
+#define ANA_LCPLL_PMS_PDIV_MASK GENMASK(7, 4)
+#define ANA_LCPLL_PMS_REFDIV_MASK GENMASK(3, 0)
+/* CMN_REG(0023) */
+#define LCPLL_PMS_SDIV_RBR_MASK GENMASK(7, 4)
+#define LCPLL_PMS_SDIV_HBR_MASK GENMASK(3, 0)
/* CMN_REG(0025) */
#define LCPLL_PMS_IQDIV_RSTN_MASK BIT(4)
/* CMN_REG(0028) */
@@ -1157,9 +1163,11 @@ static int rk_hdptx_frl_lcpll_cmn_config(struct rk_hdptx_phy *hdptx)
regmap_write(hdptx->regmap, CMN_REG(0020), cfg->pms_mdiv);
regmap_write(hdptx->regmap, CMN_REG(0021), cfg->pms_mdiv_afc);
regmap_write(hdptx->regmap, CMN_REG(0022),
- (cfg->pms_pdiv << 4) | cfg->pms_refdiv);
+ FIELD_PREP(ANA_LCPLL_PMS_PDIV_MASK, cfg->pms_pdiv) |
+ FIELD_PREP(ANA_LCPLL_PMS_REFDIV_MASK, cfg->pms_refdiv));
regmap_write(hdptx->regmap, CMN_REG(0023),
- (cfg->pms_sdiv << 4) | cfg->pms_sdiv);
+ FIELD_PREP(LCPLL_PMS_SDIV_RBR_MASK, cfg->pms_sdiv) |
+ FIELD_PREP(LCPLL_PMS_SDIV_HBR_MASK, cfg->pms_sdiv));
regmap_write(hdptx->regmap, CMN_REG(002a), cfg->sdm_deno);
regmap_write(hdptx->regmap, CMN_REG(002b), cfg->sdm_num_sign);
regmap_write(hdptx->regmap, CMN_REG(002c), cfg->sdm_num);
@@ -1229,8 +1237,10 @@ static int rk_hdptx_tmds_ropll_cmn_config(struct rk_hdptx_phy *hdptx)
regmap_write(hdptx->regmap, CMN_REG(0051), cfg->pms_mdiv);
regmap_write(hdptx->regmap, CMN_REG(0055), cfg->pms_mdiv_afc);
regmap_write(hdptx->regmap, CMN_REG(0059),
- (cfg->pms_pdiv << 4) | cfg->pms_refdiv);
- regmap_write(hdptx->regmap, CMN_REG(005a), cfg->pms_sdiv << 4);
+ FIELD_PREP(ANA_ROPLL_PMS_PDIV_MASK, cfg->pms_pdiv) |
+ FIELD_PREP(ANA_ROPLL_PMS_REFDIV_MASK, cfg->pms_refdiv));
+ regmap_write(hdptx->regmap, CMN_REG(005a),
+ FIELD_PREP(ROPLL_PMS_SDIV_RBR_MASK, cfg->pms_sdiv));
regmap_update_bits(hdptx->regmap, CMN_REG(005e), ROPLL_SDM_EN_MASK,
FIELD_PREP(ROPLL_SDM_EN_MASK, cfg->sdm_en));
@@ -2177,7 +2187,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx)
ret = regmap_read(hdptx->regmap, CMN_REG(0023), &val);
if (ret)
return 0;
- lcpll_hw.pms_sdiv = val & 0xf;
+ lcpll_hw.pms_sdiv = FIELD_GET(LCPLL_PMS_SDIV_HBR_MASK, val);
ret = regmap_read(hdptx->regmap, CMN_REG(002B), &val);
if (ret)
@@ -2197,7 +2207,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx)
ret = regmap_read(hdptx->regmap, CMN_REG(002D), &val);
if (ret)
return 0;
- lcpll_hw.sdc_n = (val & LCPLL_SDC_N_MASK) >> 1;
+ lcpll_hw.sdc_n = FIELD_GET(LCPLL_SDC_N_MASK, val);
for (i = 0; i < ARRAY_SIZE(rk_hdptx_frl_lcpll_cfg); i++) {
const struct lcpll_config *cfg = &rk_hdptx_frl_lcpll_cfg[i];
@@ -2258,7 +2268,7 @@ static u64 rk_hdptx_phy_clk_calc_rate_from_pll_cfg(struct rk_hdptx_phy *hdptx)
ret = regmap_read(hdptx->regmap, CMN_REG(0086), &val);
if (ret)
return 0;
- ropll_hw.pms_sdiv = ((val & PLL_PCG_POSTDIV_SEL_MASK) >> 4) + 1;
+ ropll_hw.pms_sdiv = FIELD_GET(PLL_PCG_POSTDIV_SEL_MASK, val) + 1;
bpc = (FIELD_GET(PLL_PCG_CLK_SEL_MASK, val) << 1) + 8;
fout = PLL_REF_CLK * ropll_hw.pms_mdiv;
--
2.53.0
--
linux-phy mailing list
linux-phy@lists.infradead.org
https://lists.infradead.org/mailman/listinfo/linux-phy
^ permalink raw reply related [flat|nested] 11+ messages in thread