mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-05-26 11:40:24 +02:00
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:
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user