[PATCH v3 2/4] phy: qcom-mipi-csi2: Make OPP table optional

From: Anusha Arun Nandi

Date: Thu Nov 05 2026 - 18:51:59 EST


Not every SoC the generic MIPI CSI2 PHY driver supports needs an OPP
table: a CSIPHY with only a single, unscaled power-domain has no
performance state to vote on, so running its "core" clock through
the OPP framework buys nothing over a plain clk_set_rate(). Add a
no_opp flag to struct mipi_csi2phy_soc_cfg and skip
devm_pm_opp_set_clkname()/devm_pm_opp_of_add_table() in probe() and
the OPP/performance-state handling in
phy_qcom_mipi_csi2_set_clock_rates() when it's set, falling back to
clk_round_rate()/clk_set_rate() on the timer clock only.

x1e80100 doesn't set no_opp, so this is a no-op for it. The "core"/
"timer" clock-name strings themselves stay hardcoded literals: every
SoC this driver supports uses the same two names, so there's nothing
to make configurable there.

Reviewed-by: Jigarkumar Zala <jigarkumar.zala@xxxxxxxxxxxxxxxx>
Signed-off-by: Anusha Arun Nandi <anusha.nandi@xxxxxxxxxxxxxxxx>
---
.../phy/qualcomm/phy-qcom-mipi-csi2-core.c | 72 ++++++++++---------
drivers/phy/qualcomm/phy-qcom-mipi-csi2.h | 3 +
2 files changed, 42 insertions(+), 33 deletions(-)

diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
index 269232499fca..8bebc943a771 100644
--- a/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
+++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2-core.c
@@ -32,40 +32,43 @@ phy_qcom_mipi_csi2_set_clock_rates(struct mipi_csi2phy_device *csi2phy,
unsigned long opp_rate = link_freq / 4;
struct dev_pm_opp *opp;
long timer_rate;
- int i, pstate;
+ int i = 0, pstate;
int ret;

- opp = dev_pm_opp_find_freq_ceil(dev, &opp_rate);
- if (IS_ERR(opp)) {
- dev_err(csi2phy->dev, "Couldn't find ceiling for %lld Hz\n",
- link_freq);
- return PTR_ERR(opp);
- }
+ if (!csi2phy->soc_cfg->no_opp) {
+ opp = dev_pm_opp_find_freq_ceil(dev, &opp_rate);
+ if (IS_ERR(opp)) {
+ dev_err(csi2phy->dev, "Couldn't find ceiling for %lld Hz\n",
+ link_freq);
+ return PTR_ERR(opp);
+ }

- pstate = 0;
- for (i = 0; csi2phy->pd_list && i < csi2phy->pd_list->num_pds; i++) {
- unsigned int perf;
+ pstate = 0;
+ for (i = 0; csi2phy->pd_list && i < csi2phy->pd_list->num_pds; i++) {
+ unsigned int perf;

- if (!csi2phy->soc_cfg->genpds[i].scaled)
- continue;
+ if (!csi2phy->soc_cfg->genpds[i].scaled)
+ continue;

- perf = dev_pm_opp_get_required_pstate(opp, pstate);
- pstate += 1;
+ perf = dev_pm_opp_get_required_pstate(opp, pstate);
+ pstate += 1;

- ret = dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i], perf);
- if (ret) {
- dev_err(csi2phy->dev, "Couldn't set perf state %u\n",
- perf);
- dev_pm_opp_put(opp);
- goto unset_pstate;
+ ret = dev_pm_genpd_set_performance_state(csi2phy->pd_list->pd_devs[i],
+ perf);
+ if (ret) {
+ dev_err(csi2phy->dev, "Couldn't set perf state %u\n",
+ perf);
+ dev_pm_opp_put(opp);
+ goto unset_pstate;
+ }
}
- }
- dev_pm_opp_put(opp);
+ dev_pm_opp_put(opp);

- ret = dev_pm_opp_set_rate(dev, opp_rate);
- if (ret) {
- dev_err(csi2phy->dev, "dev_pm_opp_set_rate() fail\n");
- goto unset_opp_rate;
+ ret = dev_pm_opp_set_rate(dev, opp_rate);
+ if (ret) {
+ dev_err(csi2phy->dev, "dev_pm_opp_set_rate() fail\n");
+ goto unset_opp_rate;
+ }
}

timer_rate = clk_round_rate(csi2phy->timer_clk, link_freq / 4);
@@ -83,7 +86,8 @@ phy_qcom_mipi_csi2_set_clock_rates(struct mipi_csi2phy_device *csi2phy,
return 0;

unset_opp_rate:
- dev_pm_opp_set_rate(dev, 0);
+ if (!csi2phy->soc_cfg->no_opp)
+ dev_pm_opp_set_rate(dev, 0);

unset_pstate:
while (i--) {
@@ -405,13 +409,15 @@ static int phy_qcom_mipi_csi2_probe(struct platform_device *pdev)
if (!csi2phy->timer_clk)
return dev_err_probe(dev, -ENODEV, "no timer clock\n");

- ret = devm_pm_opp_set_clkname(dev, "core");
- if (ret)
- return dev_err_probe(dev, ret, "Failed to set opp clkname\n");
+ if (!csi2phy->soc_cfg->no_opp) {
+ ret = devm_pm_opp_set_clkname(dev, "core");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set opp clkname\n");

- ret = devm_pm_opp_of_add_table(dev);
- if (ret)
- return dev_err_probe(dev, ret, "invalid OPP table in device tree\n");
+ ret = devm_pm_opp_of_add_table(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "invalid OPP table in device tree\n");
+ }

num_supplies = csi2phy->soc_cfg->num_supplies;
csi2phy->supplies = devm_kzalloc(dev, sizeof(*csi2phy->supplies) * num_supplies,
diff --git a/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
index 7e55ae007370..6cf9e5b956f7 100644
--- a/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
+++ b/drivers/phy/qualcomm/phy-qcom-mipi-csi2.h
@@ -68,6 +68,9 @@ struct mipi_csi2phy_soc_cfg {
const char ** const clk_names;
const unsigned int num_clk;

+ /* Skip OPP-based clock/pstate scaling. */
+ const bool no_opp;
+
const struct mipi_csi2_genpd *genpds;
const unsigned int num_genpds;
};
--
2.34.1