[DPU PATCH 3/3] drm/msm/dp: add support for DP PLL driver

Sean Paul sean at poorly.run
Thu Nov 1 21:03:15 UTC 2018


On Wed, Oct 10, 2018 at 10:15:59AM -0700, Chandan Uddaraju wrote:
> Add the needed DP PLL specific files to support
> display port interface on msm targets.
> 
> The DP driver calls the DP PLL driver registration.
> The DP driver sets the link and pixel clock sources.
> 
> Signed-off-by: Chandan Uddaraju <chandanu at codeaurora.org>
> ---
>  drivers/gpu/drm/msm/Kconfig                   |  16 +
>  drivers/gpu/drm/msm/Makefile                  |   6 +
>  drivers/gpu/drm/msm/dp/dp_ctrl.c              |   1 +
>  drivers/gpu/drm/msm/dp/dp_display.c           |  50 +++
>  drivers/gpu/drm/msm/dp/dp_display.h           |   3 +
>  drivers/gpu/drm/msm/dp/dp_parser.h            |   3 +
>  drivers/gpu/drm/msm/dp/dp_power.c             |  77 +++-
>  drivers/gpu/drm/msm/dp/dp_power.h             |   2 +
>  drivers/gpu/drm/msm/dp/pll/dp_pll.c           | 153 ++++++++
>  drivers/gpu/drm/msm/dp/pll/dp_pll.h           |  64 ++++
>  drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c      | 401 +++++++++++++++++++
>  drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h      |  94 +++++
>  drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c | 531 ++++++++++++++++++++++++++
>  13 files changed, 1389 insertions(+), 12 deletions(-)
>  create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.c
>  create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll.h
>  create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c
>  create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h
>  create mode 100644 drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c
> 
> diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
> index c363f24..1e0b9158 100644
> --- a/drivers/gpu/drm/msm/Kconfig
> +++ b/drivers/gpu/drm/msm/Kconfig
> @@ -58,6 +58,22 @@ config DRM_MSM_DP
>  	  driver. DP external display support is enabled
>  	  through this config option. It can be primary or
>  	  secondary display on device.
> +
> +config DRM_MSM_DP_PLL
> +	bool "Enable DP PLL driver in MSM DRM"
> +	depends on DRM_MSM_DP && COMMON_CLK
> +	default y

The default should be n

> +	help
> +	  Choose this option to enable DP PLL driver which provides DP
> +	  source clocks under common clock framework.
> +
> +config DRM_MSM_DP_10NM_PLL
> +	bool "Enable DP 10nm PLL driver in MSM DRM (used by SDM845)"
> +	depends on DRM_MSM_DP

Should this be DRM_MSM_DP_PLL instead?


> +	default y

The default should be n

> +	help
> +	  Choose this option if DP PLL on SDM845 is used on the platform.
> +
>  config DRM_MSM_DSI
>  	bool "Enable DSI support in MSM DRM driver"
>  	depends on DRM_MSM
> diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
> index 765a8d8..8d18353 100644
> --- a/drivers/gpu/drm/msm/Makefile
> +++ b/drivers/gpu/drm/msm/Makefile
> @@ -137,4 +137,10 @@ msm-$(CONFIG_DRM_MSM_DSI_14NM_PHY) += dsi/pll/dsi_pll_14nm.o
>  msm-$(CONFIG_DRM_MSM_DSI_10NM_PHY) += dsi/pll/dsi_pll_10nm.o
>  endif
>  
> +ifeq ($(CONFIG_DRM_MSM_DP_PLL),y)
> +msm-y += dp/pll/dp_pll.o
> +msm-y += dp/pll/dp_pll_10nm.o
> +msm-y += dp/pll/dp_pll_10nm_util.o

Instead of the ifeq, these should follow the form:

msm-$(CONFIG_BLAH) +=

> +endif
> +
>  obj-$(CONFIG_DRM_MSM)	+= msm.o
> diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> index 08a52f5..e23beee 100644
> --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
> +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
> @@ -1051,6 +1051,7 @@ static int dp_ctrl_enable_mainlink_clocks(struct dp_ctrl_private *ctrl)
>  {
>  	int ret = 0;
>  
> +	ctrl->power->set_link_clk_parent(ctrl->power);
>  	ctrl->power->set_pixel_clk_parent(ctrl->power);
>  
>  	dp_ctrl_set_clock_rate(ctrl, "ctrl_link_clk",
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
> index 8c98399..2bf6635 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.c
> +++ b/drivers/gpu/drm/msm/dp/dp_display.c
> @@ -72,6 +72,48 @@ struct dp_display_private {
>  	{}
>  };
>  
> +static int dp_get_pll(struct dp_display_private *dp_priv)
> +{
> +	struct platform_device *pdev = NULL;
> +	struct platform_device *pll_pdev;
> +	struct device_node *pll_node;
> +	struct dp_parser *dp_parser = NULL;
> +
> +	if (!dp_priv) {
> +		pr_err("Invalid Arguments\n");
> +		return -EINVAL;
> +	}
> +
> +	pdev = dp_priv->pdev;
> +	dp_parser = dp_priv->parser;
> +
> +	if (!dp_parser) {
> +		pr_err("Parser not initialized.\n");
> +		return -EINVAL;
> +	}

Can we please remove the unnecessary null checks from this patch too?

> +
> +	pll_node = of_parse_phandle(pdev->dev.of_node, "pll-node", 0);
> +	if (!pll_node) {
> +		dev_err(&pdev->dev, "cannot find pll device\n");
> +		return -ENXIO;
> +	}
> +
> +	pll_pdev = of_find_device_by_node(pll_node);
> +	if (pll_pdev)
> +		dp_parser->pll = platform_get_drvdata(pll_pdev);

What if the pll driver goes away before the dp driver?

> +
> +	of_node_put(pll_node);
> +
> +	if (!pll_pdev || !dp_parser->pll) {
> +		dev_err(&pdev->dev, "%s: pll driver is not ready\n", __func__);

This patch (and even just this function) has both pr_err and dev_err, oy!

Please convert everything to one logging medium. The msm driver was recently
converted to DRM_DEV_*, so let's go with that for all of msm/dp/*


> +		return -EPROBE_DEFER;
> +	}
> +
> +	dp_parser->pll_dev = get_device(&pll_pdev->dev);
> +
> +	return 0;
> +}
> +
>  static irqreturn_t dp_display_irq(int irq, void *dev_id)
>  {
>  	struct dp_display_private *dp = dev_id;
> @@ -125,6 +167,12 @@ static int dp_display_bind(struct device *dev, struct device *master,
>  		goto end;
>  	}
>  
> +	rc = dp_get_pll(dp);
> +	if (rc) {
> +		pr_err(" DP get PLL instance failed\n");

Print rc, here and everywhere else.

> +		goto end;
> +	}
> +
>  	rc = dp->aux->drm_aux_register(dp->aux);
>  	if (rc) {
>  		pr_err("DRM DP AUX register failed\n");
> @@ -921,6 +969,7 @@ int __init msm_dp_register(void)
>  {
>  	int ret;
>  
> +	msm_dp_pll_driver_register();
>  	ret = platform_driver_register(&dp_display_driver);
>  	if (ret) {
>  		pr_err("driver register failed");
> @@ -932,6 +981,7 @@ int __init msm_dp_register(void)
>  
>  void __exit msm_dp_unregister(void)
>  {
> +	msm_dp_pll_driver_unregister();
>  	platform_driver_unregister(&dp_display_driver);
>  }
>  
> diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h
> index ca5eae5..9cd6a6b 100644
> --- a/drivers/gpu/drm/msm/dp/dp_display.h
> +++ b/drivers/gpu/drm/msm/dp/dp_display.h
> @@ -52,4 +52,7 @@ struct msm_dp {
>  
>  int dp_display_get_num_of_displays(void);
>  int dp_display_get_displays(void **displays, int count);
> +void __init msm_dp_pll_driver_register(void);
> +void __exit msm_dp_pll_driver_unregister(void);
> +
>  #endif /* _DP_DISPLAY_H_ */
> diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h
> index b39ea02..372997e 100644
> --- a/drivers/gpu/drm/msm/dp/dp_parser.h
> +++ b/drivers/gpu/drm/msm/dp/dp_parser.h
> @@ -16,6 +16,7 @@
>  #define _DP_PARSER_H_
>  
>  #include "dpu_io_util.h"
> +#include "pll/dp_pll.h"
>  
>  #define DP_LABEL "MDSS DP DISPLAY"
>  #define AUX_CFG_LEN	10
> @@ -175,6 +176,8 @@ struct dp_parser {
>  	struct dp_pinctrl pinctrl;
>  	struct dp_io io;
>  	struct dp_display_data disp_data;
> +	struct msm_dp_pll *pll;
> +	struct device *pll_dev;

Store pll_dev in msm_dp_pll as 'dev' and remove this?

>  
>  	u8 l_map[4];
>  	struct dp_aux_cfg aux_cfg[AUX_CFG_LEN];
> diff --git a/drivers/gpu/drm/msm/dp/dp_power.c b/drivers/gpu/drm/msm/dp/dp_power.c
> index 1d58480..3a1679c 100644
> --- a/drivers/gpu/drm/msm/dp/dp_power.c
> +++ b/drivers/gpu/drm/msm/dp/dp_power.c
> @@ -23,7 +23,9 @@ struct dp_power_private {
>  	struct dp_parser *parser;
>  	struct platform_device *pdev;
>  	struct clk *pixel_clk_rcg;
> -	struct clk *pixel_parent;
> +	struct clk *link_clk_src;
> +	struct clk *pixel_provider;
> +	struct clk *link_provider;
>  
>  	struct dp_power dp_power;
>  
> @@ -154,6 +156,16 @@ static int dp_power_clk_init(struct dp_power_private *power, bool enable)
>  		goto exit;
>  	}
>  
> +	if (power->parser->pll && power->parser->pll->get_provider) {
> +		rc = power->parser->pll->get_provider(power->parser->pll,
> +				&power->link_provider, &power->pixel_provider);

Hopefully this gets simplified when you de-abstract the rest of the dp driver.

> +		if (rc) {
> +			pr_info("%s: can't get provider from pll, don't set parent\n",
> +				__func__);
> +			return 0;
> +		}
> +	}
> +
>  	if (enable) {
>  		rc = msm_dss_get_clk(dev, core->clk_config, core->num_clk);
>  		if (rc) {
> @@ -173,17 +185,10 @@ static int dp_power_clk_init(struct dp_power_private *power, bool enable)
>  		if (IS_ERR(power->pixel_clk_rcg)) {
>  			pr_debug("Unable to get DP pixel clk RCG\n");
>  			power->pixel_clk_rcg = NULL;
> -		}
> -
> -		power->pixel_parent = devm_clk_get(dev, "pixel_parent");
> -		if (IS_ERR(power->pixel_parent)) {
> -			pr_debug("Unable to get DP pixel RCG parent\n");
> -			power->pixel_parent = NULL;
> +			rc = -ENODEV;
> +			goto ctrl_get_error;
>  		}
>  	} else {
> -		if (power->pixel_parent)
> -			devm_clk_put(dev, power->pixel_parent);
> -
>  		if (power->pixel_clk_rcg)
>  			devm_clk_put(dev, power->pixel_clk_rcg);
>  
> @@ -459,6 +464,49 @@ static void dp_power_client_deinit(struct dp_power *dp_power)
>  
>  }
>  
> +static int dp_power_set_link_clk_parent(struct dp_power *dp_power)
> +{
> +	int rc = 0;
> +	struct dp_power_private *power;
> +	u32 num;
> +	struct dss_clk *cfg;
> +	char *name = "ctrl_link_clk";
> +
> +	if (!dp_power) {
> +		pr_err("invalid power data\n");
> +		rc = -EINVAL;
> +		goto exit;
> +	}
> +
> +	power = container_of(dp_power, struct dp_power_private, dp_power);
> +
> +	num = power->parser->mp[DP_CTRL_PM].num_clk;
> +	cfg = power->parser->mp[DP_CTRL_PM].clk_config;
> +
> +	while (num && strcmp(cfg->clk_name, name)) {

I think I commented on this in the other patch, but don't use strings to do
lookups, please just store the pointer directly if you need a reference.

> +		num--;
> +		cfg++;
> +	}
> +
> +	if (num && power->link_provider) {
> +		power->link_clk_src = clk_get_parent(cfg->clk);

Check return for ERR_PTR

> +			if (power->link_clk_src) {

Indenting is wrong on this block

> +				clk_set_parent(power->link_clk_src, power->link_provider);
> +				pr_debug("%s: is the parent of clk=%s\n",
> +						__clk_get_name(power->link_provider),
> +						__clk_get_name(power->link_clk_src));
> +			} else {
> +				pr_err("couldn't get parent for clk=%s\n", name);
> +				rc = -EINVAL;

Make thi:

        if (!power->link_clk_src) {
                DRM_DEV_ERROR(...)
                return -EINVAL;
        }

        ret = clk_set_parent(power->link_clk_src, power->link_provider);
        if (ret) {
                /* propertly handle error */
        }
        pr_debug("%s: is the parent of clk=%s\n",
                 __clk_get_name(power->link_provider),
                 __clk_get_name(power->link_clk_src));


> +			}
> +	} else {
> +		pr_err("%s clock could not be set parent\n", name);
> +		rc = -EINVAL;
> +	}

Same thing here, flip the if condition to check for error and return early, thus
saving yourself a level of indentation for the successful case.

> +exit:
> +	return rc;

Remove exit label and return directly above

> +}
> +
>  static int dp_power_set_pixel_clk_parent(struct dp_power *dp_power)
>  {
>  	int rc = 0;
> @@ -472,8 +520,12 @@ static int dp_power_set_pixel_clk_parent(struct dp_power *dp_power)
>  
>  	power = container_of(dp_power, struct dp_power_private, dp_power);
>  
> -	if (power->pixel_clk_rcg && power->pixel_parent)
> -		clk_set_parent(power->pixel_clk_rcg, power->pixel_parent);
> +	if (power->pixel_clk_rcg && power->pixel_provider) {
> +		clk_set_parent(power->pixel_clk_rcg, power->pixel_provider);

s/pixel_parent/pixel_provider/ isn't related to this change, can you please use
the final name in the main dp patch so it's correct in this one?

> +		pr_debug("%s: is the parent of clk=%s\n",
> +					__clk_get_name(power->pixel_provider),
> +					__clk_get_name(power->pixel_clk_rcg));

Same comment here, this debug can go in the main patch.

> +	}
>  exit:
>  	return rc;
>  }
> @@ -577,6 +629,7 @@ struct dp_power *dp_power_get(struct dp_parser *parser)
>  	dp_power->init = dp_power_init;
>  	dp_power->deinit = dp_power_deinit;
>  	dp_power->clk_enable = dp_power_clk_enable;
> +	dp_power->set_link_clk_parent = dp_power_set_link_clk_parent;

Same comment here regarding the unnecessary indirection, just call this thing
directly where applicable.

>  	dp_power->set_pixel_clk_parent = dp_power_set_pixel_clk_parent;
>  	dp_power->power_client_init = dp_power_client_init;
>  	dp_power->power_client_deinit = dp_power_client_deinit;
> diff --git a/drivers/gpu/drm/msm/dp/dp_power.h b/drivers/gpu/drm/msm/dp/dp_power.h
> index d1adaaf..5effd32 100644
> --- a/drivers/gpu/drm/msm/dp/dp_power.h
> +++ b/drivers/gpu/drm/msm/dp/dp_power.h
> @@ -24,6 +24,7 @@
>   * @init: initializes the regulators/core clocks/GPIOs/pinctrl
>   * @deinit: turns off the regulators/core clocks/GPIOs/pinctrl
>   * @clk_enable: enable/disable the DP clocks
> + * @set_link_clk_parent: set the parent of DP link clock
>   * @set_pixel_clk_parent: set the parent of DP pixel clock
>   */
>  struct dp_power {
> @@ -31,6 +32,7 @@ struct dp_power {
>  	int (*deinit)(struct dp_power *power);
>  	int (*clk_enable)(struct dp_power *power, enum dp_pm_type pm_type,
>  				bool enable);
> +	int (*set_link_clk_parent)(struct dp_power *power);
>  	int (*set_pixel_clk_parent)(struct dp_power *power);
>  	int (*power_client_init)(struct dp_power *power);
>  	void (*power_client_deinit)(struct dp_power *power);
> diff --git a/drivers/gpu/drm/msm/dp/pll/dp_pll.c b/drivers/gpu/drm/msm/dp/pll/dp_pll.c
> new file mode 100644
> index 0000000..a8612b5
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/pll/dp_pll.c
> @@ -0,0 +1,153 @@
> +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */

SPDX please (and elsewhere).

> +
> +#include "dp_pll.h"
> +
> +int msm_dp_pll_util_parse_dt_clock(struct platform_device *pdev,
> +					struct msm_dp_pll *pll)
> +{
> +	u32 i = 0, rc = 0;
> +	struct dss_module_power *mp = &pll->mp;
> +	const char *clock_name;
> +	u32 clock_rate;
> +
> +	mp->num_clk = of_property_count_strings(pdev->dev.of_node,
> +							"clock-names");
> +	if (mp->num_clk <= 0) {
> +		pr_err("clocks are not defined\n");
> +		goto clk_err;
> +	}
> +
> +	mp->clk_config = devm_kzalloc(&pdev->dev,
> +			sizeof(struct dss_clk) * mp->num_clk, GFP_KERNEL);
> +	if (!mp->clk_config) {
> +		rc = -ENOMEM;
> +		mp->num_clk = 0;
> +		goto clk_err;
> +	}
> +
> +	for (i = 0; i < mp->num_clk; i++) {
> +		of_property_read_string_index(pdev->dev.of_node, "clock-names",
> +							i, &clock_name);
> +		strlcpy(mp->clk_config[i].clk_name, clock_name,
> +				sizeof(mp->clk_config[i].clk_name));
> +
> +		of_property_read_u32_index(pdev->dev.of_node, "clock-rate",
> +							i, &clock_rate);
> +		mp->clk_config[i].rate = clock_rate;
> +
> +		if (!clock_rate)
> +			mp->clk_config[i].type = DSS_CLK_AHB;
> +		else
> +			mp->clk_config[i].type = DSS_CLK_PCLK;
> +	}
> +
> +clk_err:

remove clk_err and return directly above

> +	return rc;
> +}
> +
> +struct msm_dp_pll *msm_dp_pll_init(struct platform_device *pdev,

static please

> +			enum msm_dp_pll_type type, int id)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct msm_dp_pll *pll;
> +
> +	switch (type) {
> +	case MSM_DP_PLL_10NM:
> +		pll = msm_dp_pll_10nm_init(pdev, id);
> +		break;
> +	default:
> +		pll = ERR_PTR(-ENXIO);
> +		break;
> +	}
> +
> +	if (IS_ERR(pll)) {
> +		dev_err(dev, "%s: failed to init DP PLL\n", __func__);
> +		return pll;
> +	}
> +
> +	pll->type = type;

This should be set in the type-specific init function (ie:
msm_dp_pll_10nm_init()). That will let you simplify this entire function to:

{
        switch (type) {
        case MSM_DP_PLL_10NM:
                return msm_dp_pll_10nm_init(pdev, id);
	default:
                DRM_DEV_ERROR(&pdev->dev, "Invalid pll type: %d\n", type);
                return ERR_PTR(-ENXIO);
	}
}

> +
> +	DBG("DP:%d PLL registered", id);
> +
> +	return pll;
> +}
> +
> +static const struct of_device_id dp_pll_dt_match[] = {
> +#ifdef CONFIG_DRM_MSM_DP_10NM_PLL

I don't think you need this ifdef check, you've already provided a stub for the
init function

> +	{ .compatible = "qcom,dp-pll-10nm" },
> +#endif
> +	{}
> +};
> +
> +static int dp_pll_driver_probe(struct platform_device *pdev)
> +{
> +	struct msm_dp_pll *pll;
> +	struct device *dev = &pdev->dev;
> +	const struct of_device_id *match;
> +	enum msm_dp_pll_type type;
> +
> +	match = of_match_node(dp_pll_dt_match, dev->of_node);
> +	if (!match)
> +		return -ENODEV;
> +
> +	if (!strcmp(match->compatible, "qcom,dp-pll-10nm"))
> +		type = MSM_DP_PLL_10NM;

This is what of_device_id->data is for, use it to store the type. Can you also
check the rest of the driver and do the same there?

> +	else
> +		type = MSM_DP_PLL_MAX;
> +
> +	pll = msm_dp_pll_init(pdev, type, 0);
> +	if (IS_ERR_OR_NULL(pll)) {
> +		dev_info(dev,

Why info level?

> +			"%s: pll init failed: %ld, need separate pll clk driver\n",
> +			__func__, PTR_ERR(pll));
> +		return -ENODEV;
> +	}
> +
> +	platform_set_drvdata(pdev, pll);
> +
> +	return 0;
> +}
> +
> +static int dp_pll_driver_remove(struct platform_device *pdev)
> +{
> +	struct msm_dp_pll *pll = platform_get_drvdata(pdev);
> +
> +	if (pll) {
> +		//msm_dsi_pll_destroy(pll);

Hmm.

> +		pll = NULL;

No need to clear a local variable

> +	}
> +
> +	platform_set_drvdata(pdev, NULL);

You don't need to clear this either

> +
> +	return 0;
> +}
> +
> +static struct platform_driver dp_pll_platform_driver = {
> +	.probe      = dp_pll_driver_probe,
> +	.remove     = dp_pll_driver_remove,
> +	.driver     = {
> +		.name   = "msm_dp_pll",
> +		.of_match_table = dp_pll_dt_match,
> +	},
> +};
> +
> +void __init msm_dp_pll_driver_register(void)
> +{
> +	platform_driver_register(&dp_pll_platform_driver);
> +}
> +
> +void __exit msm_dp_pll_driver_unregister(void)
> +{
> +	platform_driver_unregister(&dp_pll_platform_driver);
> +}
> diff --git a/drivers/gpu/drm/msm/dp/pll/dp_pll.h b/drivers/gpu/drm/msm/dp/pll/dp_pll.h
> new file mode 100644
> index 0000000..d52a81a
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/pll/dp_pll.h
> @@ -0,0 +1,64 @@
> +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef __DP_PLL_H
> +#define __DP_PLL_H
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/platform_device.h>
> +
> +#include "dpu_io_util.h"
> +#include "msm_drv.h"
> +
> +#define PLL_REG_W(base, offset, data)	\
> +				writel_relaxed((data), (base) + (offset))
> +#define PLL_REG_R(base, offset)	readl_relaxed((base) + (offset))

These macros aren't really useful, tbh. It'd be better to have a function that
takes a msm_dp_pll, offset and data.

> +
> +enum msm_dp_pll_type {
> +	MSM_DP_PLL_10NM,
> +	MSM_DP_PLL_MAX
> +};
> +
> +struct msm_dp_pll {
> +	enum msm_dp_pll_type type;

Storing this doesn't seem to gain you anything. So move the dp_pll_type enum
into msm_dp_pll.c, turf the _MAX and make embed it in the .data element of the
of_device_id structs. Then you can use it when you're initializing and promptly
throw it away.

> +	struct clk_hw clk_hw;
> +	unsigned long	rate;		/* current vco rate */
> +	u64		min_rate;	/* min vco rate */
> +	u64		max_rate;	/* max vco rate */
> +	bool		pll_on;

The clk_hw/etc part of this patch should be reviewed by swboyd (cc'd). It's not
really in my wheelhouse, tbh.

> +	void		*priv;

Was going to comment on using an opaque pointer, but it not used anywhere \o/

Please remove

> +	/* Pll specific resources like GPIO, power supply, clocks, etc*/
> +	struct dss_module_power mp;
> +	int (*get_provider)(struct msm_dp_pll *pll,
> +			struct clk **link_clk_provider,
> +			struct clk **pixel_clk_provider);
> +};
> +
> +#define hw_clk_to_pll(x) container_of(x, struct msm_dp_pll, clk_hw)

Please make this a type-safe inline function.

> +
> +struct msm_dp_pll *msm_dp_pll_init(struct platform_device *pdev,
> +			enum msm_dp_pll_type type, int id);

Remove this and make that func static

> +
> +int msm_dp_pll_util_parse_dt_clock(struct platform_device *pdev,
> +					struct msm_dp_pll *pll);
> +
> +#ifdef CONFIG_DRM_MSM_DP_10NM_PLL
> +struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device *pdev, int id);
> +#else
> +struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device *pdev, int id)
> +{
> +	return ERR_PTR(-ENODEV);
> +}
> +#endif
> +#endif /* __DP_PLL_H */
> diff --git a/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c
> new file mode 100644
> index 0000000..a180413
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.c
> @@ -0,0 +1,401 @@
> +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +/*
> + * Display Port PLL driver block diagram for branch clocks
> + *
> + *		+------------------------------+
> + *		|         DP_VCO_CLK           |
> + *		|                              |
> + *		|    +-------------------+     |
> + *		|    |   (DP PLL/VCO)    |     |
> + *		|    +---------+---------+     |
> + *		|              v               |
> + *		|   +----------+-----------+   |
> + *		|   | hsclk_divsel_clk_src |   |
> + *		|   +----------+-----------+   |
> + *		+------------------------------+
> + *				|
> + *	 +------------<---------v------------>----------+
> + *	 |                                              |
> + * +-----v------------+                                 |
> + * | dp_link_clk_src  |                                 |
> + * |    divsel_ten    |                                 |
> + * +---------+--------+                                 |
> + *	|                                               |
> + *	|                                               |
> + *	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
> + *                         |
> + *		+----------+---------+
> + *		|   vco_divided_clk  |
> + *		|       _src_mux     |
> + *		+---------+----------+
> + *                        |
> + *                        v
> + *              Input to DISPCC block
> + *              for DP pixel clock

Some of your vertical lines seem misaligned here, can you please fix this up?

> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/err.h>
> +#include <linux/delay.h>
> +#include <linux/regmap.h>
> +#include <linux/clk.h>
> +
> +#include "dp_pll_10nm.h"
> +
> +#define NUM_PROVIDED_CLKS		2
> +
> +#define DP_LINK_CLK_SRC			0
> +#define DP_PIXEL_CLK_SRC		1

Instead of using an array to store the clocks in one place, why not just store a
pointer for each clk? Then you can get rid of these and just use the clk
directly.

> +
> +static struct dp_pll_10nm *dp_pdb;

This isn't used (nor should it be).

> +
> +/* Op structures */
> +static const struct clk_ops dp_10nm_vco_clk_ops = {
> +	.recalc_rate = dp_vco_recalc_rate_10nm,
> +	.set_rate = dp_vco_set_rate_10nm,
> +	.round_rate = dp_vco_round_rate_10nm,
> +	.prepare = dp_vco_prepare_10nm,
> +	.unprepare = dp_vco_unprepare_10nm,
> +};
> +
> +struct dp_pll_10nm_pclksel {
> +	struct clk_hw hw;
> +
> +	/* divider params */
> +	u8 shift;
> +	u8 width;
> +	u8 flags; /* same flags as used by clk_divider struct */
> +
> +	struct dp_pll_10nm *pll;
> +};
> +#define to_pll_10nm_pclksel(_hw) container_of(_hw, struct dp_pll_10nm_pclksel, hw)

typesafe static inline please. Everywhere you use pclksel, you just grab ->pll
from the result and never use pclksel again. So make the function return
dp_pll_10nm * directly and save yourself the local var.

> +
> +static int dp_mux_set_parent_10nm(struct clk_hw *hw, u8 val)
> +{
> +	struct dp_pll_10nm_pclksel *pclksel = to_pll_10nm_pclksel(hw);
> +	struct dp_pll_10nm *dp_res = pclksel->pll;
> +	u32 auxclk_div;
> +
> +	auxclk_div = PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
> +	auxclk_div &= ~0x03;	/* bits 0 to 1 */

This comment isn't really useful :) Could you please #define all of the
hardcoded values in the patch?

> +
> +	if (val == 0) /* mux parent index = 0 */
> +		auxclk_div |= 1;
> +	else if (val == 1) /* mux parent index = 1 */
> +		auxclk_div |= 2;
> +	else if (val == 2) /* mux parent index = 2 */
> +		auxclk_div |= 0;
> +
> +	PLL_REG_W(dp_res->phy_base,
> +			DP_PHY_VCO_DIV, auxclk_div);
> +	/* Make sure the PHY registers writes are done */
> +	wmb();

Same comments about needing wmb to work around using _relaxed

> +	pr_debug("%s: mux=%d auxclk_div=%x\n", __func__, val, auxclk_div);
> +
> +	return 0;
> +}
> +
> +static u8 dp_mux_get_parent_10nm(struct clk_hw *hw)
> +{
> +	u32 auxclk_div = 0;
> +	struct dp_pll_10nm_pclksel *pclksel = to_pll_10nm_pclksel(hw);
> +	struct dp_pll_10nm *dp_res = pclksel->pll;
> +	u8 val = 0;
> +
> +	pr_err("clk_hw->init->name = %s\n", hw->init->name);
> +	auxclk_div = PLL_REG_R(dp_res->phy_base, DP_PHY_VCO_DIV);
> +	auxclk_div &= 0x03;
> +
> +	if (auxclk_div == 1) /* Default divider */
> +		val = 0;
> +	else if (auxclk_div == 2)
> +		val = 1;
> +	else if (auxclk_div == 0)
> +		val = 2;
> +
> +	pr_debug("%s: auxclk_div=%d, val=%d\n", __func__, auxclk_div, val);
> +
> +	return val;
> +}
> +
> +static int clk_mux_determine_rate(struct clk_hw *hw,
> +				     struct clk_rate_request *req)
> +{
> +	int ret = 0;

no need to init this to 0

> +
> +	ret = __clk_mux_determine_rate_closest(hw, req);
> +	if (ret)
> +		return ret;
> +
> +	/* Set the new parent of mux if there is a new valid parent */
> +	if (hw->clk && req->best_parent_hw->clk)
> +		clk_set_parent(hw->clk, req->best_parent_hw->clk);

What about the return value? All other clk_set_parent calls in this patch are
also unchecked, so please add those as well.

> +
> +	return 0;
> +}
> +
> +static unsigned long mux_recalc_rate(struct clk_hw *hw,
> +					unsigned long parent_rate)
> +{
> +	struct clk *div_clk = NULL, *vco_clk = NULL;
> +	struct msm_dp_pll *vco = NULL;
> +
> +	div_clk = clk_get_parent(hw->clk);
> +	if (!div_clk)

According to the header documentation, clk_get_parent can return ERR_PTR as
well. Same comment applies to other callsites.

> +		return 0;
> +
> +	vco_clk = clk_get_parent(div_clk);
> +	if (!vco_clk)
> +		return 0;
> +
> +	vco = hw_clk_to_pll(__clk_get_hw(vco_clk));
> +	if (!vco)
> +		return 0;
> +
> +	if (vco->rate == DP_VCO_HSCLK_RATE_8100MHZDIV1000)
> +		return (vco->rate / 6);

Unnecessary () here and below

> +	else if (vco->rate == DP_VCO_HSCLK_RATE_5400MHZDIV1000)
> +		return (vco->rate / 4);
> +	else
> +		return (vco->rate / 2);
> +}
> +
> +static int dp_pll_10nm_get_provider(struct msm_dp_pll *pll,
> +				     struct clk **link_clk_provider,
> +				     struct clk **pixel_clk_provider)
> +{
> +	struct dp_pll_10nm *pll_10nm = to_dp_pll_10nm(pll);
> +	struct clk_hw_onecell_data *hw_data = pll_10nm->hw_data;
> +
> +	if (link_clk_provider)
> +		*link_clk_provider = hw_data->hws[DP_LINK_CLK_SRC]->clk;
> +	if (pixel_clk_provider)
> +		*pixel_clk_provider = hw_data->hws[DP_PIXEL_CLK_SRC]->clk;
> +
> +	return 0;

Why have a return value when the only place always returns 0? Make it void and
simplify error checking at the callsite.

> +}
> +
> +static const struct clk_ops dp_10nm_pclksel_clk_ops = {
> +	.get_parent = dp_mux_get_parent_10nm,
> +	.set_parent = dp_mux_set_parent_10nm,
> +	.recalc_rate = mux_recalc_rate,
> +	.determine_rate = clk_mux_determine_rate,
> +};
> +
> +static struct clk_hw *dp_pll_10nm_pixel_clk_sel(struct dp_pll_10nm *pll_10nm)
> +{
> +	struct device *dev = &pll_10nm->pdev->dev;
> +	struct dp_pll_10nm_pclksel *pll_pclksel;
> +	struct clk_init_data pclksel_init = {
> +		.parent_names = (const char *[]){
> +				"dp_vco_divsel_two_clk_src",
> +				"dp_vco_divsel_four_clk_src",
> +				"dp_vco_divsel_six_clk_src" },
> +		.num_parents = 3,
> +		.name = "dp_vco_divided_clk_src_mux",
> +		.flags = CLK_IGNORE_UNUSED,
> +		.ops = &dp_10nm_pclksel_clk_ops,
> +	};
> +	int ret;
> +
> +	pll_pclksel = devm_kzalloc(dev, sizeof(*pll_pclksel), GFP_KERNEL);
> +	if (!pll_pclksel)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pll_pclksel->pll = pll_10nm;
> +	pll_pclksel->shift = 0;
> +	pll_pclksel->width = 4;
> +	pll_pclksel->flags = CLK_DIVIDER_ONE_BASED;
> +	pll_pclksel->hw.init = &pclksel_init;

pclksel_init goes out of scope at the end of the function, yet it is persisted
in pll_pclksel. That should be fixed.

> +
> +	ret = clk_hw_register(dev, &pll_pclksel->hw);
> +	if (ret)
> +		return ERR_PTR(ret);
> +
> +	return &pll_pclksel->hw;
> +}
> +
> +static int dp_pll_10nm_register(struct dp_pll_10nm *pll_10nm)
> +{
> +	char clk_name[32], parent[32], vco_name[32];
> +	struct clk_init_data vco_init = {
> +		.parent_names = (const char *[]){ "bi_tcxo" },
> +		.num_parents = 1,
> +		.name = vco_name,
> +		.flags = CLK_IGNORE_UNUSED,
> +		.ops = &dp_10nm_vco_clk_ops,
> +	};
> +	struct device *dev = &pll_10nm->pdev->dev;
> +	struct clk_hw **hws = pll_10nm->hws;
> +	struct clk_hw_onecell_data *hw_data;
> +	struct clk_hw *hw;
> +	int num = 0;
> +	int ret;
> +
> +	DBG("DP->id = %d", pll_10nm->id);
> +
> +	hw_data = devm_kzalloc(dev, sizeof(*hw_data) +
> +			       NUM_PROVIDED_CLKS * sizeof(struct clk_hw *),
> +			       GFP_KERNEL);
> +	if (!hw_data)
> +		return -ENOMEM;
> +
> +	snprintf(vco_name, 32, "dp_vco_clk");
> +	pll_10nm->base.clk_hw.init = &vco_init;

Same comment about scope here

> +	ret = clk_hw_register(dev, &pll_10nm->base.clk_hw);
> +	if (ret)
> +		return ret;
> +	hws[num++] = &pll_10nm->base.clk_hw;
> +
> +	snprintf(clk_name, 32, "dp_link_clk_divsel_ten");
> +	snprintf(parent, 32, "dp_vco_clk");
> +	hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
> +					  CLK_SET_RATE_PARENT, 1, 10);
> +	if (IS_ERR(hw))
> +		return PTR_ERR(hw);
> +	hws[num++] = hw;
> +	hw_data->hws[DP_LINK_CLK_SRC] = hw;
> +
> +	snprintf(clk_name, 32, "dp_vco_divsel_two_clk_src");
> +	snprintf(parent, 32, "dp_vco_clk");
> +	hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
> +					  0, 1, 2);
> +	if (IS_ERR(hw))
> +		return PTR_ERR(hw);
> +	hws[num++] = hw;
> +
> +	snprintf(clk_name, 32, "dp_vco_divsel_four_clk_src");
> +	snprintf(parent, 32, "dp_vco_clk");
> +	hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
> +					  0, 1, 4);
> +	if (IS_ERR(hw))
> +		return PTR_ERR(hw);
> +	hws[num++] = hw;
> +
> +	snprintf(clk_name, 32, "dp_vco_divsel_six_clk_src");
> +	snprintf(parent, 32, "dp_vco_clk");
> +	hw = clk_hw_register_fixed_factor(dev, clk_name, parent,
> +					  0, 1, 6);
> +	if (IS_ERR(hw))
> +		return PTR_ERR(hw);
> +	hws[num++] = hw;
> +
> +	hw = dp_pll_10nm_pixel_clk_sel(pll_10nm);
> +	if (IS_ERR(hw))
> +		return PTR_ERR(hw);
> +
> +	hws[num++] = hw;
> +	hw_data->hws[DP_PIXEL_CLK_SRC] = hw;

I'm going to leave this chunk for Stephen to comment on, but again I'd rather
not store these clocks as an array and do string lookups on them.

> +
> +	pll_10nm->num_hws = num;
> +
> +	hw_data->num = NUM_PROVIDED_CLKS;
> +	pll_10nm->hw_data = hw_data;
> +
> +	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
> +				     pll_10nm->hw_data);
> +	if (ret) {
> +		dev_err(dev, "failed to register clk provider: %d\n", ret);
> +		return ret;
> +	}
> +
> +	return ret;
> +}
> +
> +struct msm_dp_pll *msm_dp_pll_10nm_init(struct platform_device *pdev, int id)
> +{
> +	struct dp_pll_10nm *dp_10nm_pll;
> +	struct msm_dp_pll *pll;
> +	int ret;
> +
> +	if (!pdev)
> +		return ERR_PTR(-ENODEV);
> +
> +	dp_10nm_pll = devm_kzalloc(&pdev->dev, sizeof(*dp_10nm_pll), GFP_KERNEL);
> +	if (!dp_10nm_pll)
> +		return ERR_PTR(-ENOMEM);
> +
> +	DBG("DP PLL%d", id);

Please remove (or convert to DRM_DEV_DEBUG)

> +
> +	dp_10nm_pll->pdev = pdev;
> +	dp_10nm_pll->id = id;
> +	dp_pdb = dp_10nm_pll;
> +
> +	dp_10nm_pll->pll_base = msm_ioremap(pdev, "pll_base", "DP_PLL");
> +	if (IS_ERR_OR_NULL(dp_10nm_pll->pll_base)) {
> +		dev_err(&pdev->dev, "failed to map CMN PLL base\n");

Print the error if pll_base is ERR_PTR, same for below.

> +		return ERR_PTR(-ENOMEM);

You should preserve the error if pll_base is an ERR_PTR, same for below.

> +	}
> +
> +	dp_10nm_pll->phy_base = msm_ioremap(pdev, "phy_base", "DP_PHY");
> +	if (IS_ERR_OR_NULL(dp_10nm_pll->phy_base)) {
> +		dev_err(&pdev->dev, "failed to map CMN PHY base\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	dp_10nm_pll->ln_tx0_base = msm_ioremap(pdev, "ln_tx0_base", "DP_LN_TX0");
> +	if (IS_ERR_OR_NULL(dp_10nm_pll->ln_tx0_base)) {
> +		dev_err(&pdev->dev, "failed to map CMN LN_TX0 base\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	dp_10nm_pll->ln_tx1_base = msm_ioremap(pdev, "ln_tx1_base", "DP_LN_TX1");
> +	if (IS_ERR_OR_NULL(dp_10nm_pll->ln_tx1_base)) {
> +		dev_err(&pdev->dev, "failed to map CMN LN_TX1 base\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
> +				&dp_10nm_pll->index);
> +	if (ret) {
> +		pr_err("Unable to get the cell-index ret=%d\n", ret);

If this is truly an error condition, we should return an error. If it's not,
downgrade this to info

> +		dp_10nm_pll->index = 0;
> +	}
> +
> +	ret = msm_dp_pll_util_parse_dt_clock(pdev, &dp_10nm_pll->base);
> +	if (ret) {
> +		pr_err("Unable to parse dt clocks ret=%d\n", ret);
> +		return ERR_PTR(ret);
> +	}
> +
> +	ret = dp_pll_10nm_register(dp_10nm_pll);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to register PLL: %d\n", ret);
> +		return ERR_PTR(ret);
> +	}
> +
> +	pll = &dp_10nm_pll->base;
> +	pll->min_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
> +	pll->max_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
> +	pll->get_provider = dp_pll_10nm_get_provider;
> +
> +	return pll;
> +}
> diff --git a/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h
> new file mode 100644
> index 0000000..f966beb
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm.h
> @@ -0,0 +1,94 @@
> +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#ifndef __DP_PLL_10NM_H
> +#define __DP_PLL_10NM_H
> +
> +#include "dp_pll.h"
> +#include "dp_reg.h"
> +
> +#define DP_VCO_HSCLK_RATE_1620MHZDIV1000	1620000UL

Isn't MHZDIV1000 just KHZ? Same for below

> +#define DP_VCO_HSCLK_RATE_2700MHZDIV1000	2700000UL
> +#define DP_VCO_HSCLK_RATE_5400MHZDIV1000	5400000UL
> +#define DP_VCO_HSCLK_RATE_8100MHZDIV1000	8100000UL
> +
> +#define NUM_DP_CLOCKS_MAX			6
> +
> +#define DP_PHY_PLL_POLL_SLEEP_US		500
> +#define DP_PHY_PLL_POLL_TIMEOUT_US		10000

These can go in the c file (and probably the others too)

> +
> +#define DP_VCO_RATE_8100MHZDIV1000		8100000UL
> +#define DP_VCO_RATE_9720MHZDIV1000		9720000UL
> +#define DP_VCO_RATE_10800MHZDIV1000		10800000UL
> +
> +struct dp_pll_10nm {
> +	struct msm_dp_pll base;
> +
> +	int id;
> +	struct platform_device *pdev;
> +
> +	void __iomem *pll_base;
> +	void __iomem *phy_base;
> +	void __iomem *ln_tx0_base;
> +	void __iomem *ln_tx1_base;
> +
> +	/* private clocks: */
> +	struct clk_hw *hws[NUM_DP_CLOCKS_MAX];
> +	u32 num_hws;
> +
> +	/* clock-provider: */
> +	struct clk_hw_onecell_data *hw_data;
> +
> +	/* lane and orientation settings */
> +	u8 lane_cnt;
> +	u8 orientation;
> +
> +	/* COM PHY settings */
> +	u32 hsclk_sel;
> +	u32 dec_start_mode0;
> +	u32 div_frac_start1_mode0;
> +	u32 div_frac_start2_mode0;
> +	u32 div_frac_start3_mode0;
> +	u32 integloop_gain0_mode0;
> +	u32 integloop_gain1_mode0;
> +	u32 vco_tune_map;
> +	u32 lock_cmp1_mode0;
> +	u32 lock_cmp2_mode0;
> +	u32 lock_cmp3_mode0;
> +	u32 lock_cmp_en;
> +
> +	/* PHY vco divider */
> +	u32 phy_vco_div;
> +	/*
> +	 * Certain pll's needs to update the same vco rate after resume in
> +	 * suspend/resume scenario. Cached the vco rate for such plls.
> +	 */
> +	unsigned long	vco_cached_rate;
> +	u32		cached_cfg0;
> +	u32		cached_cfg1;
> +	u32		cached_outdiv;
> +
> +	uint32_t index;
> +};
> +
> +#define to_dp_pll_10nm(x)	container_of(x, struct dp_pll_10nm, base)

typesafe inline pls

> +
> +int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
> +				unsigned long parent_rate);
> +unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
> +				unsigned long parent_rate);
> +long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
> +				unsigned long *parent_rate);
> +int dp_vco_prepare_10nm(struct clk_hw *hw);
> +void dp_vco_unprepare_10nm(struct clk_hw *hw);
> +#endif /* __DP_PLL_10NM_H */
> diff --git a/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c
> new file mode 100644
> index 0000000..9eb6881
> --- /dev/null
> +++ b/drivers/gpu/drm/msm/dp/pll/dp_pll_10nm_util.c
> @@ -0,0 +1,531 @@
> +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#define pr_fmt(fmt)	"%s: " fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/err.h>
> +#include <linux/iopoll.h>
> +#include <linux/delay.h>
> +
> +#include "dp_pll.h"
> +#include "dp_pll_10nm.h"
> +#include "dp_extcon.h"
> +
> +static int dp_vco_pll_init_db_10nm(struct msm_dp_pll *pll,
> +		unsigned long rate)
> +{
> +	struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +	u32 spare_value = 0;
> +
> +	spare_value = PLL_REG_R(dp_res->phy_base, DP_PHY_SPARE0);
> +	dp_res->lane_cnt = spare_value & 0x0F;
> +	dp_res->orientation = (spare_value & 0xF0) >> 4;
> +
> +	pr_debug("%s: spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n",
> +			__func__, spare_value, dp_res->lane_cnt, dp_res->orientation);
> +
> +	switch (rate) {
> +	case DP_VCO_HSCLK_RATE_1620MHZDIV1000:
> +		pr_debug("%s: VCO rate: %ld\n", __func__,
> +				DP_VCO_RATE_9720MHZDIV1000);
> +		dp_res->hsclk_sel = 0x0c;
> +		dp_res->dec_start_mode0 = 0x69;
> +		dp_res->div_frac_start1_mode0 = 0x00;
> +		dp_res->div_frac_start2_mode0 = 0x80;
> +		dp_res->div_frac_start3_mode0 = 0x07;
> +		dp_res->integloop_gain0_mode0 = 0x3f;
> +		dp_res->integloop_gain1_mode0 = 0x00;
> +		dp_res->vco_tune_map = 0x00;
> +		dp_res->lock_cmp1_mode0 = 0x6f;
> +		dp_res->lock_cmp2_mode0 = 0x08;
> +		dp_res->lock_cmp3_mode0 = 0x00;
> +		dp_res->phy_vco_div = 0x1;
> +		dp_res->lock_cmp_en = 0x00;
> +		break;
> +	case DP_VCO_HSCLK_RATE_2700MHZDIV1000:
> +		pr_debug("%s: VCO rate: %ld\n", __func__,
> +				DP_VCO_RATE_10800MHZDIV1000);
> +		dp_res->hsclk_sel = 0x04;
> +		dp_res->dec_start_mode0 = 0x69;
> +		dp_res->div_frac_start1_mode0 = 0x00;
> +		dp_res->div_frac_start2_mode0 = 0x80;
> +		dp_res->div_frac_start3_mode0 = 0x07;
> +		dp_res->integloop_gain0_mode0 = 0x3f;
> +		dp_res->integloop_gain1_mode0 = 0x00;
> +		dp_res->vco_tune_map = 0x00;
> +		dp_res->lock_cmp1_mode0 = 0x0f;
> +		dp_res->lock_cmp2_mode0 = 0x0e;
> +		dp_res->lock_cmp3_mode0 = 0x00;
> +		dp_res->phy_vco_div = 0x1;
> +		dp_res->lock_cmp_en = 0x00;
> +		break;
> +	case DP_VCO_HSCLK_RATE_5400MHZDIV1000:
> +		pr_debug("%s: VCO rate: %ld\n", __func__,
> +				DP_VCO_RATE_10800MHZDIV1000);
> +		dp_res->hsclk_sel = 0x00;
> +		dp_res->dec_start_mode0 = 0x8c;
> +		dp_res->div_frac_start1_mode0 = 0x00;
> +		dp_res->div_frac_start2_mode0 = 0x00;
> +		dp_res->div_frac_start3_mode0 = 0x0a;
> +		dp_res->integloop_gain0_mode0 = 0x3f;
> +		dp_res->integloop_gain1_mode0 = 0x00;
> +		dp_res->vco_tune_map = 0x00;
> +		dp_res->lock_cmp1_mode0 = 0x1f;
> +		dp_res->lock_cmp2_mode0 = 0x1c;
> +		dp_res->lock_cmp3_mode0 = 0x00;
> +		dp_res->phy_vco_div = 0x2;
> +		dp_res->lock_cmp_en = 0x00;
> +		break;
> +	case DP_VCO_HSCLK_RATE_8100MHZDIV1000:
> +		pr_debug("%s: VCO rate: %ld\n", __func__,
> +				DP_VCO_RATE_8100MHZDIV1000);
> +		dp_res->hsclk_sel = 0x03;
> +		dp_res->dec_start_mode0 = 0x69;
> +		dp_res->div_frac_start1_mode0 = 0x00;
> +		dp_res->div_frac_start2_mode0 = 0x80;
> +		dp_res->div_frac_start3_mode0 = 0x07;
> +		dp_res->integloop_gain0_mode0 = 0x3f;
> +		dp_res->integloop_gain1_mode0 = 0x00;
> +		dp_res->vco_tune_map = 0x00;
> +		dp_res->lock_cmp1_mode0 = 0x2f;
> +		dp_res->lock_cmp2_mode0 = 0x2a;
> +		dp_res->lock_cmp3_mode0 = 0x00;
> +		dp_res->phy_vco_div = 0x0;
> +		dp_res->lock_cmp_en = 0x08;
> +		break;

Since this is all static, a static const lookup table would probably be more
appropriate.

> +	default:
> +		return -EINVAL;

Log this please

> +	}
> +	return 0;
> +}
> +
> +static int dp_config_vco_rate_10nm(struct msm_dp_pll *pll,
> +		unsigned long rate)
> +{
> +	u32 res = 0;
> +	struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +
> +	res = dp_vco_pll_init_db_10nm(pll, rate);
> +	if (res) {
> +		pr_err("VCO Init DB failed\n");
> +		return res;
> +	}
> +
> +	if (dp_res->lane_cnt != 4) {
> +		if (dp_res->orientation == ORIENTATION_CC2)
> +			PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x6d);
> +		else
> +			PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x75);
> +	} else {
> +		PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x7d);
> +	}
> +
> +	/* Make sure the PHY register writes are done */
> +	wmb();
> +
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_EN_SEL, 0x37);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYS_CLK_CTRL, 0x02);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_ENABLE1, 0x0e);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x06);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_CLK_SEL, 0x30);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_CMN_CONFIG, 0x02);
> +
> +	/* Different for each clock rates */
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_HSCLK_SEL, dp_res->hsclk_sel);
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_DEC_START_MODE0, dp_res->dec_start_mode0);
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_DIV_FRAC_START1_MODE0, dp_res->div_frac_start1_mode0);
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_DIV_FRAC_START2_MODE0, dp_res->div_frac_start2_mode0);
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_DIV_FRAC_START3_MODE0, dp_res->div_frac_start3_mode0);
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_INTEGLOOP_GAIN0_MODE0, dp_res->integloop_gain0_mode0);
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_INTEGLOOP_GAIN1_MODE0, dp_res->integloop_gain1_mode0);
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_VCO_TUNE_MAP, dp_res->vco_tune_map);
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_LOCK_CMP1_MODE0, dp_res->lock_cmp1_mode0);
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_LOCK_CMP2_MODE0, dp_res->lock_cmp2_mode0);
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_LOCK_CMP3_MODE0, dp_res->lock_cmp3_mode0);
> +	/* Make sure the PLL register writes are done */
> +	wmb();
> +
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_BG_TIMER, 0x0a);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORECLK_DIV_MODE0, 0x0a);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_VCO_TUNE_CTRL, 0x00);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3f);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_CORE_CLK_EN, 0x1f);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_IVCO, 0x07);
> +	PLL_REG_W(dp_res->pll_base,
> +		QSERDES_COM_LOCK_CMP_EN, dp_res->lock_cmp_en);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_CCTRL_MODE0, 0x36);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_CP_CTRL_MODE0, 0x06);
> +	/* Make sure the PHY register writes are done */
> +	wmb();
> +
> +	if (dp_res->orientation == ORIENTATION_CC2)
> +		PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0x4c);
> +	else
> +		PLL_REG_W(dp_res->phy_base, DP_PHY_MODE, 0x5c);
> +	/* Make sure the PLL register writes are done */
> +	wmb();
> +
> +	/* TX Lane configuration */
> +	PLL_REG_W(dp_res->phy_base,
> +			DP_PHY_TX0_TX1_LANE_CTL, 0x05);
> +	PLL_REG_W(dp_res->phy_base,
> +			DP_PHY_TX2_TX3_LANE_CTL, 0x05);
> +
> +	/* TX-0 register configuration */
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_TRANSCEIVER_BIAS_EN, 0x1a);
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_VMODE_CTRL1, 0x40);
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_INTERFACE_SELECT, 0x3d);
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_CLKBUF_ENABLE, 0x0f);
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_RESET_TSYNC_EN, 0x03);
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_TRAN_DRVR_EMP_EN, 0x03);
> +	PLL_REG_W(dp_res->ln_tx0_base,
> +		TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_INTERFACE_MODE, 0x00);
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_BAND, 0x4);
> +
> +	/* TX-1 register configuration */
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_TRANSCEIVER_BIAS_EN, 0x1a);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_VMODE_CTRL1, 0x40);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_PRE_STALL_LDO_BOOST_EN, 0x30);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_INTERFACE_SELECT, 0x3d);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_CLKBUF_ENABLE, 0x0f);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_RESET_TSYNC_EN, 0x03);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_TRAN_DRVR_EMP_EN, 0x03);
> +	PLL_REG_W(dp_res->ln_tx1_base,
> +		TXn_PARRATE_REC_DETECT_IDLE_EN, 0x00);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_INTERFACE_MODE, 0x00);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_BAND, 0x4);
> +	/* Make sure the PHY register writes are done */
> +	wmb();
> +
> +	/* dependent on the vco frequency */
> +	PLL_REG_W(dp_res->phy_base, DP_PHY_VCO_DIV, dp_res->phy_vco_div);
> +
> +	return res;
> +}
> +
> +static bool dp_10nm_pll_lock_status(struct dp_pll_10nm *dp_res)
> +{
> +	u32 status;
> +	bool pll_locked;
> +
> +	/* poll for PLL lock status */
> +	if (readl_poll_timeout_atomic((dp_res->pll_base +
> +			QSERDES_COM_C_READY_STATUS),
> +			status,
> +			((status & BIT(0)) > 0),
> +			DP_PHY_PLL_POLL_SLEEP_US,
> +			DP_PHY_PLL_POLL_TIMEOUT_US)) {
> +		pr_err("%s: C_READY status is not high. Status=%x\n",
> +				__func__, status);
> +		pll_locked = false;
> +	} else {
> +		pll_locked = true;
> +	}
> +
> +	return pll_locked;
> +}
> +
> +static bool dp_10nm_phy_rdy_status(struct dp_pll_10nm *dp_res)
> +{
> +	u32 status;
> +	bool phy_ready = true;
> +
> +	/* poll for PHY ready status */
> +	if (readl_poll_timeout_atomic((dp_res->phy_base +
> +			DP_PHY_STATUS),
> +			status,
> +			((status & (BIT(1))) > 0),
> +			DP_PHY_PLL_POLL_SLEEP_US,
> +			DP_PHY_PLL_POLL_TIMEOUT_US)) {
> +		pr_err("%s: Phy_ready is not high. Status=%x\n",
> +				__func__, status);
> +		phy_ready = false;
> +	}
> +
> +	return phy_ready;
> +}
> +
> +static int dp_pll_enable_10nm(struct clk_hw *hw)
> +{
> +	int rc = 0;
> +	struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +	struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +	u32 bias_en, drvr_en;
> +
> +	PLL_REG_W(dp_res->phy_base, DP_PHY_AUX_CFG2, 0x04);
> +	PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
> +	PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x05);
> +	PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x01);
> +	PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x09);
> +	wmb(); /* Make sure the PHY register writes are done */
> +
> +	PLL_REG_W(dp_res->pll_base, QSERDES_COM_RESETSM_CNTRL, 0x20);
> +	wmb();	/* Make sure the PLL register writes are done */
> +
> +	if (!dp_10nm_pll_lock_status(dp_res)) {
> +		rc = -EINVAL;
> +		goto lock_err;
> +	}
> +
> +	PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x19);
> +	/* Make sure the PHY register writes are done */
> +	wmb();
> +	/* poll for PHY ready status */
> +	if (!dp_10nm_phy_rdy_status(dp_res)) {
> +		rc = -EINVAL;
> +		goto lock_err;
> +	}
> +
> +	pr_debug("%s: PLL is locked\n", __func__);
> +
> +	if (dp_res->lane_cnt == 1) {
> +		bias_en = 0x3e;
> +		drvr_en = 0x13;
> +	} else {
> +		bias_en = 0x3f;
> +		drvr_en = 0x10;
> +	}
> +
> +	if (dp_res->lane_cnt != 4) {
> +		if (dp_res->orientation == ORIENTATION_CC1) {
> +			PLL_REG_W(dp_res->ln_tx1_base,
> +				TXn_HIGHZ_DRVR_EN, drvr_en);
> +			PLL_REG_W(dp_res->ln_tx1_base,
> +				TXn_TRANSCEIVER_BIAS_EN, bias_en);
> +		} else {
> +			PLL_REG_W(dp_res->ln_tx0_base,
> +				TXn_HIGHZ_DRVR_EN, drvr_en);
> +			PLL_REG_W(dp_res->ln_tx0_base,
> +				TXn_TRANSCEIVER_BIAS_EN, bias_en);
> +		}
> +	} else {
> +		PLL_REG_W(dp_res->ln_tx0_base, TXn_HIGHZ_DRVR_EN, drvr_en);
> +		PLL_REG_W(dp_res->ln_tx0_base,
> +			TXn_TRANSCEIVER_BIAS_EN, bias_en);
> +		PLL_REG_W(dp_res->ln_tx1_base, TXn_HIGHZ_DRVR_EN, drvr_en);
> +		PLL_REG_W(dp_res->ln_tx1_base,
> +			TXn_TRANSCEIVER_BIAS_EN, bias_en);
> +	}

I think you could probably simplify this code block with a bit of effort,
especially the top condition.

> +
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_POL_INV, 0x0a);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_POL_INV, 0x0a);
> +	PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x18);
> +	udelay(2000);

why udelay?

> +
> +	PLL_REG_W(dp_res->phy_base, DP_PHY_CFG, 0x19);
> +
> +	/*
> +	 * Make sure all the register writes are completed before
> +	 * doing any other operation
> +	 */
> +	wmb();
> +
> +	/* poll for PHY ready status */
> +	if (!dp_10nm_phy_rdy_status(dp_res)) {
> +		rc = -EINVAL;
> +		goto lock_err;
> +	}
> +
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_DRV_LVL, 0x38);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_DRV_LVL, 0x38);
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_TX_EMP_POST1_LVL, 0x20);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_TX_EMP_POST1_LVL, 0x20);
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_RES_CODE_LANE_OFFSET_TX, 0x06);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_RES_CODE_LANE_OFFSET_TX, 0x06);
> +	PLL_REG_W(dp_res->ln_tx0_base, TXn_RES_CODE_LANE_OFFSET_RX, 0x07);
> +	PLL_REG_W(dp_res->ln_tx1_base, TXn_RES_CODE_LANE_OFFSET_RX, 0x07);
> +	/* Make sure the PHY register writes are done */
> +	wmb();
> +
> +lock_err:
> +	return rc;

Remove lock_err and return directly above.

> +}
> +
> +static int dp_pll_disable_10nm(struct clk_hw *hw)
> +{
> +	int rc = 0;
> +	struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +	struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +
> +	/* Assert DP PHY power down */
> +	PLL_REG_W(dp_res->phy_base, DP_PHY_PD_CTL, 0x2);
> +	/*
> +	 * Make sure all the register writes to disable PLL are
> +	 * completed before doing any other operation
> +	 */
> +	wmb();
> +
> +	return rc;
> +}
> +
> +
> +int dp_vco_prepare_10nm(struct clk_hw *hw)
> +{
> +	int rc = 0;
> +	struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +	struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +
> +	pr_debug("rate=%ld\n", pll->rate);
> +	if ((dp_res->vco_cached_rate != 0)
> +		&& (dp_res->vco_cached_rate == pll->rate)) {


	if (dp_res->vco_cached_rate && dp_res->vco_cached_rate == pll->rate) {


> +		rc = pll->clk_hw.init->ops->set_rate(hw,
> +			dp_res->vco_cached_rate, dp_res->vco_cached_rate);
> +		if (rc) {
> +			pr_err("index=%d vco_set_rate failed. rc=%d\n",
> +				rc, dp_res->index);
> +			goto error;
> +		}
> +	}
> +
> +	rc = dp_pll_enable_10nm(hw);
> +	if (rc) {
> +		pr_err("ndx=%d failed to enable dp pll\n",
> +					dp_res->index);
> +		goto error;
> +	}
> +
> +	pll->pll_on = true;

Do you need locking around the prepare/unprepare functions?


> +error:
> +	return rc;

Same comment as always

> +}
> +
> +void dp_vco_unprepare_10nm(struct clk_hw *hw)
> +{
> +	struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +	struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +
> +	if (!dp_res) {
> +		pr_err("Invalid input parameter\n");
> +		return;
> +	}
> +
> +	if (!pll->pll_on) {
> +		pr_err("pll resource can't be enabled\n");
> +		return;
> +	}
> +	dp_res->vco_cached_rate = pll->rate;
> +	dp_pll_disable_10nm(hw);
> +
> +	//dp_res->handoff_resources = false;

??

> +	pll->pll_on = false;
> +}
> +
> +int dp_vco_set_rate_10nm(struct clk_hw *hw, unsigned long rate,
> +					unsigned long parent_rate)
> +{
> +	struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +	int rc;
> +
> +	pr_debug("DP lane CLK rate=%ld\n", rate);
> +
> +	rc = dp_config_vco_rate_10nm(pll, rate);
> +	if (rc)
> +		pr_err("%s: Failed to set clk rate\n", __func__);

Should you return early here?

> +
> +	pll->rate = rate;
> +
> +	return 0;
> +}
> +
> +unsigned long dp_vco_recalc_rate_10nm(struct clk_hw *hw,
> +					unsigned long parent_rate)
> +{
> +	struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +	struct dp_pll_10nm *dp_res = to_dp_pll_10nm(pll);
> +	u32 div, hsclk_div, link_clk_div = 0;
> +	u64 vco_rate;
> +
> +	div = PLL_REG_R(dp_res->pll_base, QSERDES_COM_HSCLK_SEL);
> +	div &= 0x0f;
> +
> +	if (div == 12)
> +		hsclk_div = 6; /* Default */
> +	else if (div == 4)
> +		hsclk_div = 4;
> +	else if (div == 0)
> +		hsclk_div = 2;
> +	else if (div == 3)
> +		hsclk_div = 1;
> +	else {
> +		pr_debug("unknown divider. forcing to default\n");
> +		hsclk_div = 5;
> +	}
> +
> +	div = PLL_REG_R(dp_res->phy_base, DP_PHY_AUX_CFG2);
> +	div >>= 2;
> +
> +	if ((div & 0x3) == 0)
> +		link_clk_div = 5;
> +	else if ((div & 0x3) == 1)
> +		link_clk_div = 10;
> +	else if ((div & 0x3) == 2)
> +		link_clk_div = 20;
> +	else
> +		pr_err("%s: unsupported div. Phy_mode: %d\n", __func__, div);
> +
> +	if (link_clk_div == 20) {
> +		vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
> +	} else {
> +		if (hsclk_div == 6)
> +			vco_rate = DP_VCO_HSCLK_RATE_1620MHZDIV1000;
> +		else if (hsclk_div == 4)
> +			vco_rate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
> +		else if (hsclk_div == 2)
> +			vco_rate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
> +		else
> +			vco_rate = DP_VCO_HSCLK_RATE_8100MHZDIV1000;
> +	}
> +
> +	pr_debug("returning vco rate = %lu\n", (unsigned long)vco_rate);
> +
> +	dp_res->vco_cached_rate = pll->rate = vco_rate;
> +	return (unsigned long)vco_rate;

Hopefully this function will become easier to parse with #define'd values

> +}
> +
> +long dp_vco_round_rate_10nm(struct clk_hw *hw, unsigned long rate,
> +			unsigned long *parent_rate)
> +{
> +	unsigned long rrate = rate;
> +	struct msm_dp_pll *pll = hw_clk_to_pll(hw);
> +
> +	if (rate <= pll->min_rate)
> +		rrate = pll->min_rate;
> +	else if (rate <= DP_VCO_HSCLK_RATE_2700MHZDIV1000)
> +		rrate = DP_VCO_HSCLK_RATE_2700MHZDIV1000;
> +	else if (rate <= DP_VCO_HSCLK_RATE_5400MHZDIV1000)
> +		rrate = DP_VCO_HSCLK_RATE_5400MHZDIV1000;
> +	else
> +		rrate = pll->max_rate;

You're assuming min_rate is < 2.7MHz and max_rate > 5.4 MHz. This is true in the
current code, but could change in the future. Fortunatley min/max_rate are only
used here. So delete them from the struct and use the DP_VCO_HSCLK_RATE_* values
directly here.

> +
> +	pr_debug("%s: rrate=%ld\n", __func__, rrate);
> +
> +	*parent_rate = rrate;
> +	return rrate;
> +}
> +
> -- 
> The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
> a Linux Foundation Collaborative Project
> 

-- 
Sean Paul, Software Engineer, Google / Chromium OS


More information about the dri-devel mailing list