phy: qcom: qmp-usbc: add DP link and vco_div clocks for DP PHY

USB3DP PHY requires link and vco_div clocks when operating in DP mode.
Extend qmp_usbc_register_clocks and the clock provider logic to register
these clocks along with the existing pipe clock, to support both USB and
DP configurations.

Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Xiangxu Yin <xiangxu.yin@oss.qualcomm.com>
Link: https://patch.msgid.link/20251215-add-displayport-support-for-qcs615-platform-v8-6-cbc72c88a44e@oss.qualcomm.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
This commit is contained in:
Xiangxu Yin
2025-12-15 20:42:02 +08:00
committed by Vinod Koul
parent 5b2dd08459
commit 049e708e77
+203 -6
View File
@@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/usb/typec.h>
#include <linux/usb/typec_mux.h>
#include <dt-bindings/phy/phy-qcom-qmp.h>
#include "phy-qcom-qmp-common.h"
@@ -851,9 +852,23 @@ static int qmp_usbc_clk_init(struct qmp_usbc *qmp)
return devm_clk_bulk_get_optional(dev, num, qmp->clks);
}
static void phy_clk_release_provider(void *res)
static struct clk_hw *qmp_usbc_clks_hw_get(struct of_phandle_args *clkspec, void *data)
{
of_clk_del_provider(res);
struct qmp_usbc *qmp = data;
if (clkspec->args_count == 0)
return &qmp->pipe_clk_fixed.hw;
switch (clkspec->args[0]) {
case QMP_USB43DP_USB3_PIPE_CLK:
return &qmp->pipe_clk_fixed.hw;
case QMP_USB43DP_DP_LINK_CLK:
return &qmp->dp_link_hw;
case QMP_USB43DP_DP_VCO_DIV_CLK:
return &qmp->dp_pixel_hw;
}
return ERR_PTR(-EINVAL);
}
/*
@@ -878,12 +893,14 @@ static int phy_pipe_clk_register(struct qmp_usbc *qmp, struct device_node *np)
{
struct clk_fixed_rate *fixed = &qmp->pipe_clk_fixed;
struct clk_init_data init = { };
char name[64];
int ret;
ret = of_property_read_string(np, "clock-output-names", &init.name);
if (ret) {
dev_err(qmp->dev, "%pOFn: No clock-output-names\n", np);
return ret;
/* Clock name is not mandatory. */
snprintf(name, sizeof(name), "%s::pipe_clk", dev_name(qmp->dev));
init.name = name;
}
init.ops = &clk_fixed_rate_ops;
@@ -892,10 +909,183 @@ static int phy_pipe_clk_register(struct qmp_usbc *qmp, struct device_node *np)
fixed->fixed_rate = 125000000;
fixed->hw.init = &init;
ret = devm_clk_hw_register(qmp->dev, &fixed->hw);
return devm_clk_hw_register(qmp->dev, &fixed->hw);
}
/*
* Display Port PLL driver block diagram for branch clocks
*
* +------------------------------+
* | DP_VCO_CLK |
* | |
* | +-------------------+ |
* | | (DP PLL/VCO) | |
* | +---------+---------+ |
* | v |
* | +----------+-----------+ |
* | | hsclk_divsel_clk_src | |
* | +----------+-----------+ |
* +------------------------------+
* |
* +---------<---------v------------>----------+
* | |
* +--------v----------------+ |
* | dp_phy_pll_link_clk | |
* | link_clk | |
* +--------+----------------+ |
* | |
* | |
* v v
* Input to DISPCC block |
* for link clk, crypto clk |
* and interface clock |
* |
* |
* +--------<------------+-----------------+---<---+
* | | |
* +----v---------+ +--------v-----+ +--------v------+
* | vco_divided | | vco_divided | | vco_divided |
* | _clk_src | | _clk_src | | _clk_src |
* | | | | | |
* |divsel_six | | divsel_two | | divsel_four |
* +-------+------+ +-----+--------+ +--------+------+
* | | |
* v---->----------v-------------<------v
* |
* +----------+-----------------+
* | dp_phy_pll_vco_div_clk |
* +---------+------------------+
* |
* v
* Input to DISPCC block
* for DP pixel clock
*
*/
static int qmp_dp_pixel_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
{
switch (req->rate) {
case 1620000000UL / 2:
case 2700000000UL / 2:
/* 5.4 is same link rate as 2.7GHz, i.e. div 4 */
return 0;
default:
return -EINVAL;
}
}
static unsigned long qmp_dp_pixel_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
const struct qmp_usbc *qmp;
const struct phy_configure_opts_dp *dp_opts;
qmp = container_of(hw, struct qmp_usbc, dp_pixel_hw);
dp_opts = &qmp->dp_opts;
switch (dp_opts->link_rate) {
case 1620:
return 1620000000UL / 2;
case 2700:
return 2700000000UL / 2;
case 5400:
return 5400000000UL / 4;
default:
return 0;
}
}
static const struct clk_ops qmp_dp_pixel_clk_ops = {
.determine_rate = qmp_dp_pixel_clk_determine_rate,
.recalc_rate = qmp_dp_pixel_clk_recalc_rate,
};
static int qmp_dp_link_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req)
{
switch (req->rate) {
case 162000000:
case 270000000:
case 540000000:
return 0;
default:
return -EINVAL;
}
}
static unsigned long qmp_dp_link_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
const struct qmp_usbc *qmp;
const struct phy_configure_opts_dp *dp_opts;
qmp = container_of(hw, struct qmp_usbc, dp_link_hw);
dp_opts = &qmp->dp_opts;
switch (dp_opts->link_rate) {
case 1620:
case 2700:
case 5400:
return dp_opts->link_rate * 100000;
default:
return 0;
}
}
static const struct clk_ops qmp_dp_link_clk_ops = {
.determine_rate = qmp_dp_link_clk_determine_rate,
.recalc_rate = qmp_dp_link_clk_recalc_rate,
};
static int phy_dp_clks_register(struct qmp_usbc *qmp, struct device_node *np)
{
struct clk_init_data init = { };
char name[64];
int ret;
snprintf(name, sizeof(name), "%s::link_clk", dev_name(qmp->dev));
init.ops = &qmp_dp_link_clk_ops;
init.name = name;
qmp->dp_link_hw.init = &init;
ret = devm_clk_hw_register(qmp->dev, &qmp->dp_link_hw);
if (ret < 0) {
dev_err(qmp->dev, "link clk reg fail ret=%d\n", ret);
return ret;
}
snprintf(name, sizeof(name), "%s::vco_div_clk", dev_name(qmp->dev));
init.ops = &qmp_dp_pixel_clk_ops;
init.name = name;
qmp->dp_pixel_hw.init = &init;
ret = devm_clk_hw_register(qmp->dev, &qmp->dp_pixel_hw);
if (ret) {
dev_err(qmp->dev, "pxl clk reg fail ret=%d\n", ret);
return ret;
}
return 0;
}
static void phy_clk_release_provider(void *res)
{
of_clk_del_provider(res);
}
static int qmp_usbc_register_clocks(struct qmp_usbc *qmp, struct device_node *np)
{
struct clk_fixed_rate *fixed = &qmp->pipe_clk_fixed;
int ret;
ret = phy_pipe_clk_register(qmp, np);
if (ret)
return ret;
if (qmp->dp_serdes != 0) {
ret = phy_dp_clks_register(qmp, np);
if (ret)
return ret;
}
if (np == qmp->dev->of_node)
return devm_of_clk_add_hw_provider(qmp->dev, qmp_usbc_clks_hw_get, qmp);
ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fixed->hw);
if (ret)
return ret;
@@ -1040,6 +1230,13 @@ static int qmp_usbc_parse_dt(struct qmp_usbc *qmp)
if (IS_ERR(base))
return PTR_ERR(base);
if (offs->dp_serdes != 0) {
qmp->dp_serdes = base + offs->dp_serdes;
qmp->dp_tx = base + offs->dp_txa;
qmp->dp_tx2 = base + offs->dp_txb;
qmp->dp_dp_phy = base + offs->dp_dp_phy;
}
qmp->serdes = base + offs->serdes;
qmp->pcs = base + offs->pcs;
if (offs->pcs_misc)
@@ -1148,7 +1345,7 @@ static int qmp_usbc_probe(struct platform_device *pdev)
*/
pm_runtime_forbid(dev);
ret = phy_pipe_clk_register(qmp, np);
ret = qmp_usbc_register_clocks(qmp, np);
if (ret)
goto err_node_put;